refactor: split into indexers and generators
This commit is contained in:
parent
82c2edce0e
commit
79c9b848c6
9 changed files with 224 additions and 56 deletions
|
@ -1,20 +1,11 @@
|
|||
using Bismarck.CodeGenerator.Extensions;
|
||||
using Bismarck.CodeGenerator.Models;
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.OpenApi.Any;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
using Scriban;
|
||||
|
||||
namespace Bismarck.CodeGenerator.Generators;
|
||||
|
||||
internal record EnumSpec(string Namespace, string TargetName, IEnumerable<string> Values) : SpecBase(Namespace, TargetName)
|
||||
{
|
||||
public IEnumerable<string> Values { get; } = Values;
|
||||
}
|
||||
|
||||
internal class EnumGenerator
|
||||
internal class EnumGenerator : ISchemaGenerator
|
||||
{
|
||||
private readonly Template _enumTemplate;
|
||||
|
||||
|
@ -23,21 +14,10 @@ internal class EnumGenerator
|
|||
_enumTemplate = Template.Parse(typeof(ContractGenerator).LoadRelativeResource("templates", "Enum.tmplcs"));
|
||||
}
|
||||
|
||||
public void Generate(GeneratorContext context, OpenApiSchema schema, string namespaceName, string name)
|
||||
public SchemaKind SupportedSchemaKind => SchemaKind.Enum;
|
||||
|
||||
public void Generate(GeneratorContext context, IGeneratorSpec spec)
|
||||
{
|
||||
context.AddSource(SchemaKind.Enum, name, _enumTemplate.Render(new EnumSpec(namespaceName, name, MapFromAny(schema.Enum))));
|
||||
context.AddSource(SchemaKind.Enum, spec.TargetName, _enumTemplate.Render(spec));
|
||||
}
|
||||
|
||||
private static IEnumerable<string> MapFromAny(IEnumerable<IOpenApiAny> enumValues) => enumValues.SelectMany(v =>
|
||||
{
|
||||
if (v is OpenApiString s)
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
s.Value.SanitizeTypeName()
|
||||
};
|
||||
}
|
||||
|
||||
return Enumerable.Empty<string>();
|
||||
});
|
||||
}
|
|
@ -1,64 +1,59 @@
|
|||
using System.Collections.Immutable;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
using Bismarck.CodeGenerator.Extensions;
|
||||
using Bismarck.CodeGenerator.Indexers;
|
||||
using Bismarck.CodeGenerator.Models;
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
using Scriban;
|
||||
|
||||
namespace Bismarck.CodeGenerator.Generators;
|
||||
|
||||
public record PropertySpec(PropertyType Type, string Name)
|
||||
{
|
||||
public PropertyType Type { get; } = Type;
|
||||
|
||||
public string Name { get; } = Name;
|
||||
}
|
||||
|
||||
internal record ModelSpec(string Namespace, string TargetName) : SpecBase(Namespace, TargetName)
|
||||
{
|
||||
public IList<PropertySpec> Properties { get; set; } = new List<PropertySpec>();
|
||||
}
|
||||
|
||||
internal class ModelGenerator
|
||||
{
|
||||
private readonly Template _modelTemplate;
|
||||
private readonly EnumGenerator _enumGenerator;
|
||||
private readonly IDictionary<SchemaKind, ISchemaIndexer> _schemaIndexers;
|
||||
|
||||
public ModelGenerator()
|
||||
{
|
||||
_modelTemplate = Template.Parse(typeof(ContractGenerator).LoadRelativeResource("templates", "SchemaModel.tmplcs"));
|
||||
_enumGenerator = new EnumGenerator();
|
||||
_modelTemplate =
|
||||
Template.Parse(typeof(ContractGenerator).LoadRelativeResource("templates", "SchemaModel.tmplcs"));
|
||||
_schemaIndexers = new Dictionary<SchemaKind, ISchemaIndexer> { { SchemaKind.Enum, new EnumSchemaIndexer() } };
|
||||
}
|
||||
|
||||
public void GenerateModelSchema(GeneratorContext context, IDictionary<string, OpenApiSchema> schemata, string namespaceName)
|
||||
public void GenerateModelSchema(GeneratorContext context, IDictionary<string, OpenApiSchema> schemata,
|
||||
string namespaceName)
|
||||
{
|
||||
foreach ((string? key, OpenApiSchema? schema) in schemata)
|
||||
{
|
||||
var type = Type(schema, key);
|
||||
var spec = new ModelSpec(namespaceName, key.SanitizeTypeName());
|
||||
GenerateProperties(context, spec, new[]
|
||||
{
|
||||
key
|
||||
}, schema);
|
||||
GenerateProperties(context, spec, new[] { key }, schema);
|
||||
context.AddSource(type.Kind, spec.TargetName, _modelTemplate.Render(spec));
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateProperties(GeneratorContext context, ModelSpec modelSpec, IEnumerable<string> location, OpenApiSchema schema)
|
||||
private void GenerateProperties(GeneratorContext context, ModelSpec modelSpec, IEnumerable<string> location,
|
||||
OpenApiSchema schema)
|
||||
{
|
||||
var parentName = location.Last();
|
||||
foreach ((string? key, OpenApiSchema? value) in schema.Properties)
|
||||
{
|
||||
var propertyType = Type(value, parentName);
|
||||
|
||||
if (_schemaIndexers.TryGetValue(propertyType.Kind, out var indexer))
|
||||
{
|
||||
indexer.Index(value, modelSpec.Namespace, key.SanitizeTypeName());
|
||||
}
|
||||
|
||||
switch (propertyType.Kind)
|
||||
{
|
||||
case SchemaKind.Simple:
|
||||
modelSpec.Properties.Add(new PropertySpec(propertyType, key.SanitizeTypeName()));
|
||||
break;
|
||||
case SchemaKind.Enum:
|
||||
_enumGenerator.Generate(context, value, modelSpec.Namespace, key.SanitizeTypeName());
|
||||
modelSpec.Properties.Add(new PropertySpec(propertyType, key.SanitizeTypeName()));
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
namespace Bismarck.CodeGenerator.Generators;
|
||||
|
||||
public abstract record SpecBase(string Namespace, string TargetName)
|
||||
{
|
||||
public string Namespace { get; } = Namespace;
|
||||
|
||||
public string TargetName { get; } = TargetName;
|
||||
}
|
9
src/Bismarck.CodeGenerator/ISchemaGenerator.cs
Normal file
9
src/Bismarck.CodeGenerator/ISchemaGenerator.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using Bismarck.CodeGenerator.Models;
|
||||
|
||||
namespace Bismarck.CodeGenerator;
|
||||
|
||||
public interface ISchemaGenerator
|
||||
{
|
||||
SchemaKind SupportedSchemaKind { get; }
|
||||
void Generate(GeneratorContext context, IGeneratorSpec spec);
|
||||
}
|
11
src/Bismarck.CodeGenerator/ISchemaIndexer.cs
Normal file
11
src/Bismarck.CodeGenerator/ISchemaIndexer.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using Bismarck.CodeGenerator.Models;
|
||||
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
namespace Bismarck.CodeGenerator;
|
||||
|
||||
public interface ISchemaIndexer
|
||||
{
|
||||
SchemaKind SupportedSchemaKind { get; }
|
||||
IGeneratorSpec Index(OpenApiSchema schema, string namespaceName, string name);
|
||||
}
|
25
src/Bismarck.CodeGenerator/Indexers/EnumSchemaIndexer.cs
Normal file
25
src/Bismarck.CodeGenerator/Indexers/EnumSchemaIndexer.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using Bismarck.CodeGenerator.Extensions;
|
||||
using Bismarck.CodeGenerator.Models;
|
||||
|
||||
using Microsoft.OpenApi.Any;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
namespace Bismarck.CodeGenerator.Indexers;
|
||||
|
||||
public class EnumSchemaIndexer : ISchemaIndexer
|
||||
{
|
||||
public SchemaKind SupportedSchemaKind => SchemaKind.Enum;
|
||||
|
||||
public IGeneratorSpec Index(OpenApiSchema schema, string namespaceName, string name)
|
||||
=> new EnumSpec(namespaceName, name, MapFromAny(schema.Enum));
|
||||
|
||||
private static IEnumerable<string> MapFromAny(IEnumerable<IOpenApiAny> enumValues) => enumValues.SelectMany(v =>
|
||||
{
|
||||
if (v is OpenApiString s)
|
||||
{
|
||||
return new[] { s.Value.SanitizeTypeName() };
|
||||
}
|
||||
|
||||
return Enumerable.Empty<string>();
|
||||
});
|
||||
}
|
75
src/Bismarck.CodeGenerator/Indexers/ObjectSchemaIndexer.cs
Normal file
75
src/Bismarck.CodeGenerator/Indexers/ObjectSchemaIndexer.cs
Normal file
|
@ -0,0 +1,75 @@
|
|||
using System.Collections.Immutable;
|
||||
|
||||
using Bismarck.CodeGenerator.Extensions;
|
||||
using Bismarck.CodeGenerator.Models;
|
||||
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
namespace Bismarck.CodeGenerator.Indexers;
|
||||
|
||||
public class ObjectSchemaIndexer : ISchemaIndexer
|
||||
{
|
||||
private readonly IDictionary<SchemaKind, ISchemaIndexer> _schemaIndexers;
|
||||
|
||||
public ObjectSchemaIndexer(params ISchemaIndexer[] indexers)
|
||||
{
|
||||
_schemaIndexers = indexers
|
||||
.Select(i => (i.SupportedSchemaKind, i))
|
||||
.ToImmutableDictionary(t => t.SupportedSchemaKind, t => t.i);
|
||||
}
|
||||
|
||||
public SchemaKind SupportedSchemaKind => SchemaKind.Object;
|
||||
|
||||
public IGeneratorSpec Index(OpenApiSchema schema, string namespaceName, string name)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private void GenerateProperties(GeneratorContext context, ModelSpec modelSpec, IEnumerable<string> location,
|
||||
OpenApiSchema schema)
|
||||
{
|
||||
var parentName = location.Last();
|
||||
foreach ((string? key, OpenApiSchema? value) in schema.Properties)
|
||||
{
|
||||
var propertyType = Type(value, parentName);
|
||||
|
||||
if (_schemaIndexers.TryGetValue(propertyType.Kind, out var indexer))
|
||||
{
|
||||
indexer.Index(value, modelSpec.Namespace, key.SanitizeTypeName());
|
||||
}
|
||||
|
||||
switch (propertyType.Kind)
|
||||
{
|
||||
case SchemaKind.Simple:
|
||||
modelSpec.Properties.Add(new PropertySpec(propertyType, key.SanitizeTypeName()));
|
||||
break;
|
||||
case SchemaKind.Enum:
|
||||
modelSpec.Properties.Add(new PropertySpec(propertyType, key.SanitizeTypeName()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static PropertyType Type(OpenApiSchema schema, string parentName)
|
||||
{
|
||||
return schema.Type switch
|
||||
{
|
||||
"object" => PropertyType.Object(parentName),
|
||||
"array" => PropertyType.Array(parentName),
|
||||
"string" when schema.Enum.Count == 0 => PropertyType.Simple(nameof(String)),
|
||||
"string" when schema.Enum.Count > 0 => PropertyType.Enum(parentName),
|
||||
"boolean" => PropertyType.Simple(nameof(Boolean)),
|
||||
"integer" => schema.Format switch
|
||||
{
|
||||
"int64" => PropertyType.Simple(nameof(Int64)),
|
||||
_ => PropertyType.Simple(nameof(Int32))
|
||||
},
|
||||
"number" => schema.Format switch
|
||||
{
|
||||
"float" => PropertyType.Simple(nameof(Single)),
|
||||
_ => PropertyType.Simple(nameof(Double))
|
||||
},
|
||||
_ => PropertyType.Object(nameof(Object))
|
||||
};
|
||||
}
|
||||
}
|
38
src/Bismarck.CodeGenerator/Models/GeneratorSpecs.cs
Normal file
38
src/Bismarck.CodeGenerator/Models/GeneratorSpecs.cs
Normal file
|
@ -0,0 +1,38 @@
|
|||
namespace Bismarck.CodeGenerator.Models;
|
||||
|
||||
public interface IGeneratorSpec
|
||||
{
|
||||
string TargetName { get; }
|
||||
string? LookupKey { get; }
|
||||
string Namespace { get; }
|
||||
SchemaKind Kind { get; }
|
||||
}
|
||||
|
||||
public abstract record SpecBase(string Namespace, string TargetName, string? LookupKey) : IGeneratorSpec
|
||||
{
|
||||
public string TargetName { get; } = TargetName;
|
||||
public string? LookupKey { get; } = LookupKey;
|
||||
public string Namespace { get; } = Namespace;
|
||||
public abstract SchemaKind Kind { get; }
|
||||
}
|
||||
|
||||
public record PropertySpec(PropertyType Type, string Name)
|
||||
{
|
||||
public PropertyType Type { get; } = Type;
|
||||
|
||||
public string Name { get; } = Name;
|
||||
}
|
||||
|
||||
internal record ModelSpec(string Namespace, string TargetName, string? LookupKey = null) : SpecBase(Namespace, TargetName,
|
||||
LookupKey)
|
||||
{
|
||||
public IList<PropertySpec> Properties { get; set; } = new List<PropertySpec>();
|
||||
public override SchemaKind Kind => SchemaKind.Object;
|
||||
}
|
||||
|
||||
internal record EnumSpec(string Namespace, string TargetName, IEnumerable<string> Values, string? LookupKey = null)
|
||||
: SpecBase(Namespace, TargetName, LookupKey)
|
||||
{
|
||||
public IEnumerable<string> Values { get; } = Values;
|
||||
public override SchemaKind Kind => SchemaKind.Enum;
|
||||
}
|
43
src/Bismarck.CodeGenerator/SchemaRegistry.cs
Normal file
43
src/Bismarck.CodeGenerator/SchemaRegistry.cs
Normal file
|
@ -0,0 +1,43 @@
|
|||
using System.Collections.Immutable;
|
||||
|
||||
using Bismarck.CodeGenerator.Generators;
|
||||
using Bismarck.CodeGenerator.Models;
|
||||
|
||||
namespace Bismarck.CodeGenerator;
|
||||
|
||||
public class SchemaRegistry
|
||||
{
|
||||
private readonly IDictionary<(SchemaKind, string), IGeneratorSpec> _registeredSpecs;
|
||||
private readonly IDictionary<SchemaKind, ISchemaGenerator> _generators;
|
||||
|
||||
public SchemaRegistry(params ISchemaGenerator[] generators)
|
||||
{
|
||||
_generators = generators
|
||||
.ToImmutableDictionary(g => g.SupportedSchemaKind, g => g);
|
||||
_registeredSpecs = new Dictionary<(SchemaKind, string), IGeneratorSpec>();
|
||||
}
|
||||
|
||||
public bool AddSpec(IGeneratorSpec spec) => _registeredSpecs.TryAdd((spec.Kind, spec.TargetName), spec);
|
||||
|
||||
public void GenerateSources(GeneratorContext context)
|
||||
{
|
||||
var specsByKind = _registeredSpecs.Values
|
||||
.GroupBy(s => s.Kind)
|
||||
.ToImmutableDictionary(grp => grp.Key, grp => grp);
|
||||
|
||||
foreach ((SchemaKind kindToGenerate, IGrouping<SchemaKind, IGeneratorSpec>? specs) in specsByKind)
|
||||
{
|
||||
if (!_generators.ContainsKey(kindToGenerate))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var generator = _generators[kindToGenerate];
|
||||
|
||||
foreach (var generatorSpec in specs)
|
||||
{
|
||||
generator.Generate(context, generatorSpec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue