Add template working

This commit is contained in:
Wvader 2022-08-27 04:19:43 +01:00
parent 340a89bbd2
commit e232fa6e76
5 changed files with 112 additions and 57 deletions

View File

@ -12,16 +12,21 @@ using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace MapTo
{
internal class EfGeneratorContext
internal class EfAddGeneratorContext
{
private readonly List<SymbolDisplayPart> _ignoredNamespaces;
public EfGeneratorContext(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, MemberDeclarationSyntax memberSyntax)
protected EfAddGeneratorContext(
Compilation compilation,
SourceGenerationOptions sourceGenerationOptions,
MemberDeclarationSyntax memberSyntax,
string addSourceTemplate)
{
Compilation = compilation;
_ignoredNamespaces = new();
Diagnostics = ImmutableArray<Diagnostic>.Empty;
Usings = ImmutableArray.Create("System", Constants.RootNamespace);
SourceTemplate = addSourceTemplate;
SourceGenerationOptions = sourceGenerationOptions;
MemberSyntax = memberSyntax;
@ -30,6 +35,8 @@ namespace MapTo
AddUsingIfRequired(sourceGenerationOptions.SupportNullableStaticAnalysis, "System.Diagnostics.CodeAnalysis");
}
public string SourceTemplate { get; }
public ImmutableArray<Diagnostic> Diagnostics { get; private set; }
public EfMethodsModel? Model { get; private set; }
@ -38,19 +45,21 @@ namespace MapTo
protected INamedTypeSymbol MappingContextTypeSymbol { get; }
protected INamedTypeSymbol EfAddMethodsAttributeTypeSymbol { get; }
protected SourceGenerationOptions SourceGenerationOptions { get; }
protected MemberDeclarationSyntax MemberSyntax { get; }
protected ImmutableArray<string> Usings { get; private set; }
public static EfGeneratorContext Create(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, MemberDeclarationSyntax typeSyntax)
public static EfAddGeneratorContext Create(
Compilation compilation,
SourceGenerationOptions sourceGenerationOptions,
MemberDeclarationSyntax typeSyntax,
string addSourceTemplate)
{
EfGeneratorContext context = typeSyntax switch
EfAddGeneratorContext context = typeSyntax switch
{
PropertyDeclarationSyntax => new EfGeneratorContext(compilation, sourceGenerationOptions, typeSyntax),
PropertyDeclarationSyntax => new EfAddGeneratorContext(compilation, sourceGenerationOptions, typeSyntax, addSourceTemplate),
_ => throw new ArgumentOutOfRangeException()
};
@ -178,9 +187,9 @@ namespace MapTo
// context name
var dbContextName = classDeclarationSyntax.Identifier.ValueText;
var namespaceDeclaration = classDeclarationSyntax.Parent as NamespaceDeclarationSyntax;
var namespaceFullName = namespaceDeclaration.Name.ToString();
var contextNamespace = namespaceDeclaration.Name.ToString();
var contextTypeFullName = $"{namespaceFullName}.{dbContextName}";
var contextTypeFullName = $"{contextNamespace}.{dbContextName}";
var propertyName = entityTypeName;
@ -208,7 +217,7 @@ namespace MapTo
return new EfMethodsModel(
SourceGenerationOptions,
namespaceFullName,
contextNamespace,
propertyName,
dbContextName,
contextTypeFullName,

View File

@ -10,20 +10,11 @@ using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace MapTo
{
/*/// <summary>
/// Base source code generator typed class.
/// </summary>
public class SourceCodeGenerator : ISourceGenerator
{
public virtual void Initialize(GeneratorInitializationContext context) {}
public virtual void Execute(GeneratorExecutionContext context) {}
}*/
/// <summary>
/// Ef methods source generator
/// </summary>
[Generator]
public class EfMethodsGenerator: ISourceGenerator
public class EfAddMethodsGenerator: ISourceGenerator
{
/// <inheritdoc />
public void Initialize(GeneratorInitializationContext context)
@ -59,18 +50,28 @@ namespace MapTo
foreach (var memberDeclarationSyntax in candidateTypes)
{
var mappingContext = EfGeneratorContext.Create(compilation, options, memberDeclarationSyntax);
mappingContext.Diagnostics.ForEach(context.ReportDiagnostic);
string addSourceTemplate = string.Empty;
if (context.AdditionalFiles.Length > 0)
{
addSourceTemplate = context.AdditionalFiles
.FirstOrDefault()?
.GetText()?
.ToString() ?? string.Empty;
}
var mappingContext = EfAddGeneratorContext.Create(compilation, options, memberDeclarationSyntax, addSourceTemplate);
mappingContext.Diagnostics.ForEach(context.ReportDiagnostic);
if (mappingContext.Model is null)
{
continue;
}
var (source, hintName) = memberDeclarationSyntax switch
{
PropertyDeclarationSyntax => EfMethodsSource.Generate(mappingContext.Model),
PropertyDeclarationSyntax => EfMethodsSource.Generate(mappingContext.Model, addSourceTemplate),
_ => throw new ArgumentOutOfRangeException()
};

View File

@ -74,7 +74,7 @@ namespace MapTo
string Namespace,
string PropertyName,
string ContextTypeName,
string ContestFullType,
string ContextFullType,
string EntityTypeFullName,
string EntityTypeIdentifierName,

View File

@ -1,4 +1,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using MapTo.Extensions;
using static MapTo.Sources.Constants;
@ -6,42 +9,84 @@ namespace MapTo.Sources
{
internal static class EfMethodsSource
{
internal static SourceCode Generate(EfMethodsModel model)
static IEnumerable<string> GetFilesFromDir(string dir) =>
Directory.EnumerateFiles(dir);
internal static SourceCode Generate(EfMethodsModel model, string addSourceTemplate)
{
const bool writeDebugInfo = false;
using var builder = new SourceBuilder();
var entityTypeName = model.EntityTypeIdentifierName;
var entityTypeFullName = model.EntityTypeFullName;
var readTypeName = model.ReadTypeIdentifierName;
var readTypeFullName = model.ReadTypeFullName;
var createTypeName = model.CreateTypeIdentifierName;
var createTypeFullName = model.CreateTypeFullName;
var contextFullName = model.ContextFullType;
var propertyName = model.PropertyName;
var newEntityVarName = $"new{model.EntityTypeIdentifierName}";
var toCreateVarName = $"{model.EntityTypeIdentifierName.ToCamelCase()}ToCreate";
List<string> constructorHeaders = new List<string>();
if (!addSourceTemplate.IsEmpty())
{
builder
.WriteLine(GeneratedFilesHeader)
.WriteUsings(model.Usings);
var templateToSourceBuilder = new StringBuilder(addSourceTemplate);
templateToSourceBuilder
.Replace("{Namespace} ", model.Namespace)
.Replace("{entityTypeName}", entityTypeName)
.Replace("{readTypeFullName}", readTypeFullName)
.Replace("{createTypeFullName}", createTypeFullName)
.Replace("{contextFullName}", contextFullName)
.Replace("{toCreateVarName}", toCreateVarName)
.Replace("{newEntityVarName}", newEntityVarName)
.Replace("{entityTypeFullName}", entityTypeFullName)
.Replace("{propertyName}", propertyName);
using var builder = new SourceBuilder()
builder
.Write(templateToSourceBuilder.ToString());
}
if (addSourceTemplate.IsEmpty())
{
builder
.WriteLine(GeneratedFilesHeader)
.WriteUsings(model.Usings)
.WriteLine("using Microsoft.EntityFrameworkCore;")
.WriteLine()
// Namespace declaration
.WriteLine($"namespace {model.Namespace}")
.WriteOpeningBracket()
// Class declaration
.WriteLine($"public static partial class {model.EntityTypeIdentifierName}Extensions")
.WriteOpeningBracket()
.WriteLine()
.WriteLine($"public static (bool, {model.ReadTypeFullName}) Add{model.EntityTypeIdentifierName}(")
.WriteLine($"this {model.ContestFullType} dbContext,")
.WriteLine($"{model.CreateTypeFullName} {model.EntityTypeIdentifierName.ToLower()}toCreate)")
.WriteOpeningBracket()
.WriteLine($"var new{model.EntityTypeIdentifierName} = new {model.EntityTypeFullName}({model.EntityTypeIdentifierName.ToLower()}toCreate);")
.WriteLine($"dbContext.{model.PropertyName}.Add(new{model.EntityTypeIdentifierName});")
.WriteLine($"var success = dbContext.SaveChanges() >= 0;")
.WriteLine($"return (success, new {model.ReadTypeFullName}(new{model.EntityTypeIdentifierName}));")
.WriteClosingBracket();
builder
.WriteLine()
// End class declaration
.WriteClosingBracket()
.WriteLine()
// End namespace declaration
.WriteClosingBracket();
.WriteUsings(model.Usings)
.WriteLine("using Microsoft.EntityFrameworkCore;")
.WriteLine()
// Namespace declaration
.WriteLine($"namespace {model.Namespace}")
.WriteOpeningBracket()
// Class declaration
.WriteLine($"public static partial class {model.EntityTypeIdentifierName}Extensions")
.WriteOpeningBracket()
.WriteLine()
.WriteLine($"public static (bool, {model.ReadTypeFullName}) Add{entityTypeName}(")
.WriteLine($"this {contextFullName} dbContext,")
.WriteLine($"{createTypeFullName} {toCreateVarName})")
.WriteOpeningBracket()
.WriteLine($"var {newEntityVarName} = new {model.EntityTypeFullName}({toCreateVarName});")
.WriteLine($"dbContext.{propertyName}.Add({newEntityVarName});")
.WriteLine($"var success = dbContext.SaveChanges() >= 0;")
.WriteLine($"return (success, new {readTypeFullName}({newEntityVarName}));")
.WriteClosingBracket();
builder
.WriteLine()
// End class declaration
.WriteClosingBracket()
.WriteLine()
// End namespace declaration
.WriteClosingBracket();
return new(builder.ToString(), $"{model.Namespace}.{model.EntityTypeIdentifierName}Extensions.g.cs");
}
var generatedCode = builder.ToString();
var hintName = $"{model.EntityTypeIdentifierName}Extensions.g.cs";
return new(generatedCode, hintName);
}
}
}

View File

@ -50,7 +50,7 @@ namespace MapTo.Tests.Infrastructure
}
var driver = CSharpGeneratorDriver.Create(
new List<ISourceGenerator>() { new MapToGenerator(), new EfMethodsGenerator() },
new List<ISourceGenerator>() { new MapToGenerator(), new EfAddMethodsGenerator() },
optionsProvider: new TestAnalyzerConfigOptionsProvider(analyzerConfigOptions),
parseOptions: new CSharpParseOptions(languageVersion)
);