-
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: Fixed some issues with generator.
- Loading branch information
Showing
19 changed files
with
521 additions
and
53 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netstandard2.0</TargetFramework> | ||
<IsPackable>false</IsPackable> | ||
<GenerateDocumentationFile>false</GenerateDocumentationFile> | ||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> | ||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules> | ||
<NoWarn>$(NoWarn);CA1014;CA1031;CA1308</NoWarn> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="IsExternalInit" Version="1.0.3"> | ||
<PrivateAssets>all</PrivateAssets> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
</PackageReference> | ||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.1" PrivateAssets="all" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
namespace H.Generators; | ||
|
||
public readonly record struct InterfaceData( | ||
string Namespace, | ||
string Name, | ||
IReadOnlyCollection<MethodData> Methods); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
namespace H.Generators; | ||
|
||
public readonly record struct MethodData( | ||
string Name, | ||
string Description, | ||
bool IsAsync, | ||
bool IsVoid, | ||
IReadOnlyCollection<ParameterData> Parameters); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
namespace H.Generators; | ||
|
||
public readonly record struct ParameterData( | ||
string Name, | ||
string Description, | ||
string Type, | ||
string SchemaType, | ||
string? Format, | ||
IReadOnlyCollection<string> EnumValues, | ||
IReadOnlyCollection<ParameterData> Properties, | ||
IReadOnlyCollection<ParameterData> ArrayItem, | ||
bool IsRequired, | ||
bool IsNullable, | ||
string DefaultValue); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,208 @@ | ||
using System.ComponentModel; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
|
||
namespace H.Generators; | ||
|
||
public class OpenAiFunctionsGenerator | ||
{ | ||
#region Methods | ||
|
||
private static string GetDescription(ISymbol symbol) | ||
{ | ||
return symbol.GetAttributes() | ||
.FirstOrDefault(static x => x.AttributeClass?.Name == nameof(DescriptionAttribute))? | ||
.ConstructorArguments.First().Value?.ToString() ?? string.Empty; | ||
} | ||
|
||
private static InterfaceData PrepareData( | ||
(SemanticModel SemanticModel, AttributeData AttributeData, InterfaceDeclarationSyntax InterfaceSyntax, INamedTypeSymbol InterfaceSymbol) tuple) | ||
{ | ||
var (_, _, _, interfaceSymbol) = tuple; | ||
|
||
var methods = interfaceSymbol | ||
.GetMembers() | ||
.OfType<IMethodSymbol>() | ||
.Where(static x => x.MethodKind == MethodKind.Ordinary) | ||
.Select(static x => new MethodData( | ||
Name: x.Name, | ||
Description: GetDescription(x), | ||
IsAsync: x.IsAsync || x.ReturnType.Name == "Task", | ||
IsVoid: x.ReturnsVoid, | ||
Parameters: x.Parameters | ||
.Where(static x => x.Type.MetadataName != "CancellationToken") | ||
.Select(static x => ToParameterData( | ||
typeSymbol: x.Type, | ||
name: x.Name, | ||
description: GetDescription(x), | ||
isRequired: !x.IsOptional)) | ||
.ToArray())) | ||
.ToArray(); | ||
|
||
return new InterfaceData( | ||
Namespace: interfaceSymbol.ContainingNamespace.ToDisplayString(), | ||
Name: interfaceSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), | ||
Methods: methods); | ||
} | ||
|
||
private static ParameterData ToParameterData(ITypeSymbol typeSymbol, string? name = null, string? description = null, bool isRequired = true) | ||
{ | ||
string schemaType; | ||
string? format = null; | ||
var properties = Array.Empty<ParameterData>(); | ||
ParameterData? arrayItem = null; | ||
switch (typeSymbol.TypeKind) | ||
{ | ||
case TypeKind.Enum: | ||
schemaType = "string"; | ||
break; | ||
|
||
case TypeKind.Structure: | ||
switch (typeSymbol.SpecialType) | ||
{ | ||
case SpecialType.System_Int32: | ||
schemaType = "integer"; | ||
format = "int32"; | ||
break; | ||
|
||
case SpecialType.System_Int64: | ||
schemaType = "integer"; | ||
format = "int64"; | ||
break; | ||
|
||
case SpecialType.System_Single: | ||
schemaType = "number"; | ||
format = "double"; | ||
break; | ||
|
||
case SpecialType.System_Double: | ||
schemaType = "number"; | ||
format = "float"; | ||
break; | ||
|
||
case SpecialType.System_DateTime: | ||
schemaType = "string"; | ||
format = "date-time"; | ||
break; | ||
|
||
case SpecialType.System_Boolean: | ||
schemaType = "boolean"; | ||
break; | ||
|
||
case SpecialType.None: | ||
switch (typeSymbol.Name) | ||
{ | ||
case "DateOnly": | ||
schemaType = "string"; | ||
format = "date"; | ||
break; | ||
|
||
default: | ||
throw new NotImplementedException($"{typeSymbol.Name} is not implemented."); | ||
} | ||
break; | ||
|
||
default: | ||
throw new NotImplementedException($"{typeSymbol.SpecialType} is not implemented."); | ||
} | ||
break; | ||
|
||
case TypeKind.Class: | ||
switch (typeSymbol.SpecialType) | ||
{ | ||
case SpecialType.System_String: | ||
schemaType = "string"; | ||
break; | ||
|
||
|
||
case SpecialType.None: | ||
schemaType = "object"; | ||
properties = typeSymbol.GetMembers() | ||
.OfType<IPropertySymbol>() | ||
.Select(static y => ToParameterData( | ||
typeSymbol: y.Type, | ||
name: y.Name, | ||
description: GetDescription(y), | ||
isRequired: true)) | ||
.ToArray(); | ||
break; | ||
|
||
default: | ||
throw new NotImplementedException($"{typeSymbol.SpecialType} is not implemented."); | ||
} | ||
break; | ||
|
||
case TypeKind.Interface when typeSymbol.MetadataName == "IReadOnlyCollection`1": | ||
schemaType = "array"; | ||
arrayItem = (typeSymbol as INamedTypeSymbol)?.TypeArguments | ||
.Select(static y => ToParameterData(y)) | ||
.FirstOrDefault(); | ||
break; | ||
|
||
case TypeKind.Array: | ||
schemaType = "array"; | ||
arrayItem = ToParameterData((typeSymbol as IArrayTypeSymbol)?.ElementType!); | ||
break; | ||
|
||
default: | ||
throw new NotImplementedException($"{typeSymbol.TypeKind} is not implemented."); | ||
} | ||
|
||
return new ParameterData( | ||
Name: !string.IsNullOrWhiteSpace(name) | ||
? name! | ||
: typeSymbol.Name, | ||
Description: !string.IsNullOrWhiteSpace(description) | ||
? description! | ||
: GetDescription(typeSymbol), | ||
Type: typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), | ||
DefaultValue: GetDefaultValue(typeSymbol), | ||
SchemaType: schemaType, | ||
Format: format, | ||
Properties: properties, | ||
ArrayItem: arrayItem != null | ||
? new []{ arrayItem.Value } | ||
: Array.Empty<ParameterData>(), | ||
EnumValues: typeSymbol.TypeKind == TypeKind.Enum | ||
? typeSymbol | ||
.GetMembers() | ||
.OfType<IFieldSymbol>() | ||
.Select(static x => x.Name.ToLowerInvariant()) | ||
.ToArray() | ||
: Array.Empty<string>(), | ||
IsNullable: IsNullable(typeSymbol), | ||
IsRequired: isRequired); | ||
} | ||
|
||
private static bool IsNullable(ITypeSymbol typeSymbol) | ||
{ | ||
if (typeSymbol.TypeKind == TypeKind.Enum) | ||
{ | ||
return false; | ||
} | ||
if (typeSymbol.TypeKind == TypeKind.Structure) | ||
{ | ||
return false; | ||
} | ||
|
||
return typeSymbol.SpecialType switch | ||
{ | ||
SpecialType.System_String => false, | ||
_ => true, | ||
}; | ||
} | ||
|
||
private static string GetDefaultValue(ITypeSymbol typeSymbol) | ||
{ | ||
switch (typeSymbol.SpecialType) | ||
{ | ||
case SpecialType.System_String: | ||
return "string.Empty"; | ||
|
||
default: | ||
return string.Empty; | ||
} | ||
} | ||
|
||
#endregion | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
namespace H.Generators; | ||
|
||
internal static class SourceGenerationHelper | ||
{ | ||
/// <summary> | ||
/// https://swagger.io/docs/specification/data-models/data-types/ | ||
/// </summary> | ||
/// <param name="parameter"></param> | ||
/// <param name="depth"></param> | ||
/// <returns></returns> | ||
public static string GenerateOpenApiSchema(ParameterData parameter, int depth = 0) | ||
{ | ||
var indent = new string(' ', depth * 4); | ||
if (parameter.ArrayItem.Count != 0) | ||
{ | ||
return $@"new | ||
{indent} {{ | ||
{indent} type = ""{parameter.SchemaType}"", | ||
{indent} description = ""{parameter.Description}"", | ||
{indent} items = {GenerateOpenApiSchema(parameter.ArrayItem.First(), depth: depth + 1)}, | ||
{indent} }}"; | ||
} | ||
if (parameter.Properties.Count != 0) | ||
{ | ||
return $@"new | ||
{indent} {{ | ||
{indent} type = ""{parameter.SchemaType}"", | ||
{indent} description = ""{parameter.Description}"", | ||
{indent} properties = new Dictionary<string, object> | ||
{indent} {{ | ||
{indent} {string.Join(",\n " + indent, parameter.Properties.Select(x => $@"[""{x.Name}""] = " + GenerateOpenApiSchema(x, depth: depth + 2)))} | ||
{indent} }}, | ||
{indent} required = new string[] {{ {string.Join(", ", parameter.Properties | ||
.Where(static x => x.IsRequired) | ||
.Select(static x => $"\"{x.Name}\""))} }}, | ||
{indent} }}"; | ||
} | ||
|
||
if (parameter.EnumValues.Count != 0) | ||
{ | ||
return $@"new | ||
{indent} {{ | ||
{indent} type = ""{parameter.SchemaType}"", | ||
{indent} description = ""{parameter.Description}"", | ||
{indent} @enum = new string[] {{ {string.Join(", ", parameter.EnumValues.Select(static x => $"\"{x}\""))} }}, | ||
{indent} }}"; | ||
} | ||
|
||
return $@"new | ||
{indent} {{ | ||
{indent} type = ""{parameter.SchemaType}"",{(parameter.Format != null ? $@" | ||
{indent} format = ""{parameter.Format}""," : "")} | ||
{indent} description = ""{parameter.Description}"", | ||
{indent} }}"; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.