Skip to content

Commit 3c867bd

Browse files
gunpal5Gunpal Jain
andauthored
fix: Json Schema Generation (#22)
* fix: Json Schema Generation, used System.Text.Json JsonSchema generation. * fix: warning suppressions --------- Co-authored-by: Gunpal Jain <[email protected]>
1 parent 0a26ed0 commit 3c867bd

File tree

59 files changed

+801
-3492
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+801
-3492
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace CSharpToJsonSchema.Generators.Conversion;
2+
3+
public static class StringHelpers
4+
{
5+
public static string ToCamelCase(this string str)
6+
{
7+
if (!string.IsNullOrEmpty(str) && str.Length > 1)
8+
{
9+
return char.ToLowerInvariant(str[0]) + str.Substring(1);
10+
}
11+
12+
return str.ToLowerInvariant();
13+
}
14+
}

src/libs/CSharpToJsonSchema.Generators/Conversion/ToModels.cs

Lines changed: 133 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.ComponentModel;
2+
using CSharpToJsonSchema.Generators.JsonGen.Helpers;
23
using CSharpToJsonSchema.Generators.Models;
34
using Microsoft.CodeAnalysis;
45

@@ -24,31 +25,18 @@ public static InterfaceData PrepareData(
2425
var parameters = x.Parameters
2526
.Where(static x => x.Type.MetadataName != "CancellationToken")
2627
.ToArray();
27-
28+
2829
return new MethodData(
2930
Name: x.Name,
3031
Description: GetDescription(x),
3132
IsAsync: x.IsAsync || x.ReturnType.Name == "Task",
3233
IsVoid: x.ReturnsVoid || x.ReturnType.MetadataName == "Task",
3334
IsStrict: isStrict,
34-
Parameters: new OpenApiSchema(
35-
Name: x.Name,
36-
Description: GetDescription(x),
37-
Type: "object",
38-
SchemaType: "object",
39-
Properties: parameters
40-
.Select(y => ToParameterData(
41-
typeSymbol: y.Type,
42-
name: y.Name,
43-
description: GetDescription(y),
44-
isRequired: isStrict || !y.IsOptional))
45-
.ToArray(),
46-
EnumValues: Array.Empty<string>(),
47-
IsNullable: false,
48-
IsRequired: true,
49-
Format: null,
50-
ArrayItem: Array.Empty<OpenApiSchema>(),
51-
DefaultValue: string.Empty));
35+
Parameters: parameters.Select(static y => y).ToArray(),
36+
Descriptions: parameters.Select(static l => GetParameterDescriptions(l)).SelectMany(s => s)
37+
.ToDictionary(s => s.Key, s => s.Value),
38+
ReturnType: x.ReturnType
39+
);
5240
})
5341
.ToArray();
5442

@@ -57,8 +45,106 @@ public static InterfaceData PrepareData(
5745
Name: interfaceSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat),
5846
Methods: methods);
5947
}
60-
61-
private static OpenApiSchema ToParameterData(ITypeSymbol typeSymbol, string? name = null, string? description = null, bool isRequired = true)
48+
49+
public static InterfaceData PrepareMethodData(
50+
this IMethodSymbol interfaceSymbol,
51+
AttributeData attributeData)
52+
{
53+
interfaceSymbol = interfaceSymbol ?? throw new ArgumentNullException(nameof(interfaceSymbol));
54+
attributeData = attributeData ?? throw new ArgumentNullException(nameof(attributeData));
55+
56+
var isStrict = attributeData.NamedArguments.FirstOrDefault(x => x.Key == "Strict").Value.Value is bool strict &&
57+
strict;
58+
var x = interfaceSymbol;
59+
var parameters = x.Parameters
60+
//.Where(static x => x.Type.MetadataName != "CancellationToken")
61+
.ToArray();
62+
63+
var methodData = new MethodData(
64+
Name: x.Name,
65+
Description: GetDescription(x),
66+
IsAsync: x.IsAsync || x.ReturnType.Name == "Task",
67+
IsVoid: x.ReturnsVoid || x.ReturnType.MetadataName == "Task",
68+
IsStrict: isStrict,
69+
Parameters: parameters.Select(static y => y).ToArray(),
70+
Descriptions: parameters.Select(static l => GetParameterDescriptions(l)).SelectMany(s => s)
71+
.ToDictionary(s => s.Key, s => s.Value),
72+
ReturnType:x.ReturnType
73+
);
74+
75+
76+
return new InterfaceData(
77+
Namespace: interfaceSymbol.ContainingNamespace.ToDisplayString(),
78+
Name: "I"+interfaceSymbol.Name,
79+
Methods: [methodData]);
80+
}
81+
82+
// private static Dictionary<string, bool> GetIsRequired(IParameterSymbol[] parameters, Dictionary<string, bool>? dics = null)
83+
// {
84+
// dics ??= new Dictionary<string, bool>();
85+
//
86+
// foreach (var parameter in parameters)
87+
// {
88+
// if (dics.TryAdd(parameter.Name, IsRequired(parameter)))
89+
// {
90+
// if (parameter is IParameterSymbol namedTypeSymbol)
91+
// {
92+
// GetIsRequired(namedTypeSymbol.Type.GetMembers().OfType<IPropertySymbol>().ToArray(),dics)
93+
// }
94+
// }
95+
// }
96+
//
97+
// return dics;
98+
// }
99+
//
100+
// private static bool IsRequired(ISymbol parameter)
101+
// {
102+
// return false;
103+
// //parameter.GetAttributes().OfType<global::System.ComponentModel.D.RequiredAttribute>()
104+
// }
105+
106+
private static List<KeyValuePair<string, string>> GetParameterDescriptions(IParameterSymbol parameters,
107+
Dictionary<string, string>? dics = null)
108+
{
109+
dics ??= new Dictionary<string, string>();
110+
111+
112+
if (dics.TryAdd(parameters.Name.ToCamelCase(), GetDescription(parameters)))
113+
{
114+
if (parameters is IParameterSymbol namedTypeSymbol)
115+
{
116+
GetParameterDescriptions(namedTypeSymbol.Type.GetMembers().OfType<IPropertySymbol>().ToArray(), dics);
117+
}
118+
}
119+
120+
return dics.Select(x => new KeyValuePair<string, string>(x.Key, x.Value)).ToList();
121+
}
122+
123+
private static Dictionary<string, string> GetParameterDescriptions(IPropertySymbol[] parameters,
124+
Dictionary<string, string>? dics = null)
125+
{
126+
dics ??= new Dictionary<string, string>();
127+
128+
foreach (var parameter in parameters)
129+
{
130+
var description = GetDescription(parameter);
131+
if (string.IsNullOrWhiteSpace(description)) continue;
132+
133+
if (dics.TryAdd(parameter.Name, description))
134+
{
135+
if (parameter is IPropertySymbol namedTypeSymbol)
136+
{
137+
GetParameterDescriptions(namedTypeSymbol.Type.GetMembers().OfType<IPropertySymbol>().ToArray(),
138+
dics);
139+
}
140+
}
141+
}
142+
143+
return dics;
144+
}
145+
146+
private static OpenApiSchema ToParameterData(ITypeSymbol typeSymbol, string? name = null,
147+
string? description = null, bool isRequired = true)
62148
{
63149
string schemaType;
64150
string? format = null;
@@ -69,65 +155,67 @@ private static OpenApiSchema ToParameterData(ITypeSymbol typeSymbol, string? nam
69155
case TypeKind.Enum:
70156
schemaType = "string";
71157
break;
72-
158+
73159
case TypeKind.Structure:
74160
switch (typeSymbol.SpecialType)
75161
{
76162
case SpecialType.System_Int32:
77163
schemaType = "integer";
78164
format = "int32";
79165
break;
80-
166+
81167
case SpecialType.System_Int64:
82168
schemaType = "integer";
83169
format = "int64";
84170
break;
85-
171+
86172
case SpecialType.System_Single:
87173
schemaType = "number";
88174
format = "double";
89175
break;
90-
176+
91177
case SpecialType.System_Double:
92178
schemaType = "number";
93179
format = "float";
94180
break;
95-
181+
96182
case SpecialType.System_DateTime:
97183
schemaType = "string";
98184
format = "date-time";
99185
break;
100-
186+
101187
case SpecialType.System_Boolean:
102188
schemaType = "boolean";
103189
break;
104-
190+
105191
case SpecialType.None:
106192
switch (typeSymbol.Name)
107193
{
108194
case "DateOnly":
109195
schemaType = "string";
110196
format = "date";
111197
break;
112-
198+
113199
default:
114200
throw new NotImplementedException($"{typeSymbol.Name} is not implemented.");
115201
}
202+
116203
break;
117-
204+
118205
default:
119206
throw new NotImplementedException($"{typeSymbol.SpecialType} is not implemented.");
120207
}
208+
121209
break;
122-
210+
123211
case TypeKind.Class:
124212
switch (typeSymbol.SpecialType)
125213
{
126214
case SpecialType.System_String:
127215
schemaType = "string";
128216
break;
129-
130-
217+
218+
131219
case SpecialType.None:
132220
schemaType = "object";
133221
properties = typeSymbol.GetMembers()
@@ -139,28 +227,29 @@ private static OpenApiSchema ToParameterData(ITypeSymbol typeSymbol, string? nam
139227
isRequired: true))
140228
.ToArray();
141229
break;
142-
230+
143231
default:
144232
throw new NotImplementedException($"{typeSymbol.SpecialType} is not implemented.");
145233
}
234+
146235
break;
147-
236+
148237
case TypeKind.Interface when typeSymbol.MetadataName == "IReadOnlyCollection`1":
149238
schemaType = "array";
150239
arrayItem = (typeSymbol as INamedTypeSymbol)?.TypeArguments
151240
.Select(static y => ToParameterData(y))
152241
.FirstOrDefault();
153242
break;
154-
243+
155244
case TypeKind.Array:
156245
schemaType = "array";
157246
arrayItem = ToParameterData((typeSymbol as IArrayTypeSymbol)?.ElementType!);
158247
break;
159-
248+
160249
default:
161250
throw new NotImplementedException($"{typeSymbol.TypeKind} is not implemented.");
162251
}
163-
252+
164253
return new OpenApiSchema(
165254
Name: !string.IsNullOrWhiteSpace(name)
166255
? name!
@@ -184,13 +273,14 @@ private static OpenApiSchema ToParameterData(ITypeSymbol typeSymbol, string? nam
184273
IsNullable: IsNullable(typeSymbol),
185274
IsRequired: isRequired);
186275
}
187-
276+
188277
private static bool IsNullable(ITypeSymbol typeSymbol)
189278
{
190279
if (typeSymbol.TypeKind == TypeKind.Enum)
191280
{
192281
return false;
193282
}
283+
194284
if (typeSymbol.TypeKind == TypeKind.Structure)
195285
{
196286
return false;
@@ -202,20 +292,20 @@ private static bool IsNullable(ITypeSymbol typeSymbol)
202292
_ => true,
203293
};
204294
}
205-
206-
private static string GetDefaultValue(ITypeSymbol typeSymbol)
295+
296+
public static string GetDefaultValue(this ITypeSymbol typeSymbol)
207297
{
208298
switch (typeSymbol.SpecialType)
209299
{
210300
case SpecialType.System_String:
211301
return "string.Empty";
212-
302+
213303
default:
214304
return string.Empty;
215305
}
216306
}
217307

218-
private static string GetDescription(ISymbol symbol)
308+
public static string GetDescription(ISymbol symbol)
219309
{
220310
return symbol.GetAttributes()
221311
.FirstOrDefault(static x => x.AttributeClass?.Name == nameof(DescriptionAttribute))?

src/libs/CSharpToJsonSchema.Generators/JsonSchemaGenerator.cs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,35 @@ public class JsonSchemaGenerator : IIncrementalGenerator
2222
#region Methods
2323

2424
public void Initialize(IncrementalGeneratorInitializationContext context)
25+
{
26+
//Process Interfaces
27+
ProcessInterfaces(context);
28+
29+
//Process Methods
30+
// ProcessMethods(context);
31+
}
32+
33+
private void ProcessMethods(IncrementalGeneratorInitializationContext context)
34+
{
35+
var attributes =
36+
context.SyntaxProvider
37+
.ForAttributeWithMetadataNameMethodSyntax("CSharpToJsonSchema.FunctionToolAttribute")
38+
.SelectManyAllAttributesOfCurrentMethodSyntax()
39+
.SelectAndReportExceptions(PrepareMethodData, context, Id);
40+
41+
attributes
42+
.SelectAndReportExceptions(AsFunctionTools, context, Id)
43+
.AddSource(context);
44+
45+
// attributes
46+
// .SelectAndReportExceptions(AsCalls, context, Id)
47+
// .AddSource(context);
48+
49+
// var generator = new JsonSourceGenerator();
50+
// generator.Initialize2(context);
51+
}
52+
53+
private void ProcessInterfaces(IncrementalGeneratorInitializationContext context)
2554
{
2655
var attributes =
2756
context.SyntaxProvider
@@ -41,21 +70,35 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
4170
generator.Initialize2(context);
4271
}
4372

44-
73+
4574
private static InterfaceData PrepareData(
4675
(SemanticModel SemanticModel, AttributeData AttributeData, InterfaceDeclarationSyntax InterfaceSyntax, INamedTypeSymbol InterfaceSymbol) tuple)
4776
{
4877
var (_, attributeData, _, interfaceSymbol) = tuple;
4978

5079
return interfaceSymbol.PrepareData(attributeData);
5180
}
81+
82+
private static InterfaceData PrepareMethodData(
83+
(SemanticModel SemanticModel, AttributeData AttributeData, MethodDeclarationSyntax InterfaceSyntax, IMethodSymbol InterfaceSymbol) tuple)
84+
{
85+
var (_, attributeData, _, interfaceSymbol) = tuple;
86+
87+
return interfaceSymbol.PrepareMethodData(attributeData);
88+
}
5289

5390
private static FileWithName AsTools(InterfaceData @interface)
5491
{
5592
return new FileWithName(
5693
Name: $"{@interface.Name}.Tools.generated.cs",
5794
Text: Sources.GenerateClientImplementation(@interface));
5895
}
96+
private static FileWithName AsFunctionTools(InterfaceData @interface)
97+
{
98+
return new FileWithName(
99+
Name: $"{@interface.Name}.FunctionTools.generated.cs",
100+
Text: Sources.GenerateFunctionToolClientImplementation(@interface));
101+
}
59102

60103
private static FileWithName AsCalls(InterfaceData @interface)
61104
{

0 commit comments

Comments
 (0)