diff --git a/src/BlueWest.MapTo/EfGeneratorContext.cs b/src/BlueWest.MapTo/EfAddGeneratorContext.cs similarity index 90% rename from src/BlueWest.MapTo/EfGeneratorContext.cs rename to src/BlueWest.MapTo/EfAddGeneratorContext.cs index 2619cfa..195099d 100644 --- a/src/BlueWest.MapTo/EfGeneratorContext.cs +++ b/src/BlueWest.MapTo/EfAddGeneratorContext.cs @@ -12,16 +12,21 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; namespace MapTo { - internal class EfGeneratorContext + internal class EfAddGeneratorContext { private readonly List _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.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 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 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, diff --git a/src/BlueWest.MapTo/EfMethodsGenerator.cs b/src/BlueWest.MapTo/EfAddMethodsGenerator.cs similarity index 77% rename from src/BlueWest.MapTo/EfMethodsGenerator.cs rename to src/BlueWest.MapTo/EfAddMethodsGenerator.cs index 7798385..ac63178 100644 --- a/src/BlueWest.MapTo/EfMethodsGenerator.cs +++ b/src/BlueWest.MapTo/EfAddMethodsGenerator.cs @@ -10,20 +10,11 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; namespace MapTo { - /*/// - /// Base source code generator typed class. - /// - public class SourceCodeGenerator : ISourceGenerator - { - public virtual void Initialize(GeneratorInitializationContext context) {} - public virtual void Execute(GeneratorExecutionContext context) {} - }*/ - /// /// Ef methods source generator /// [Generator] - public class EfMethodsGenerator: ISourceGenerator + public class EfAddMethodsGenerator: ISourceGenerator { /// 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() }; diff --git a/src/BlueWest.MapTo/Models.cs b/src/BlueWest.MapTo/Models.cs index de88e28..8e0d1ea 100644 --- a/src/BlueWest.MapTo/Models.cs +++ b/src/BlueWest.MapTo/Models.cs @@ -74,7 +74,7 @@ namespace MapTo string Namespace, string PropertyName, string ContextTypeName, - string ContestFullType, + string ContextFullType, string EntityTypeFullName, string EntityTypeIdentifierName, diff --git a/src/BlueWest.MapTo/Sources/EfMethodsSource.cs b/src/BlueWest.MapTo/Sources/EfMethodsSource.cs index 2692f07..11a25d4 100644 --- a/src/BlueWest.MapTo/Sources/EfMethodsSource.cs +++ b/src/BlueWest.MapTo/Sources/EfMethodsSource.cs @@ -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 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 constructorHeaders = new List(); + 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); } } } \ No newline at end of file diff --git a/test/BlueWest.MapTo.Tests/Infrastructure/CSharpGenerator.cs b/test/BlueWest.MapTo.Tests/Infrastructure/CSharpGenerator.cs index b43e274..af9eca0 100644 --- a/test/BlueWest.MapTo.Tests/Infrastructure/CSharpGenerator.cs +++ b/test/BlueWest.MapTo.Tests/Infrastructure/CSharpGenerator.cs @@ -50,7 +50,7 @@ namespace MapTo.Tests.Infrastructure } var driver = CSharpGeneratorDriver.Create( - new List() { new MapToGenerator(), new EfMethodsGenerator() }, + new List() { new MapToGenerator(), new EfAddMethodsGenerator() }, optionsProvider: new TestAnalyzerConfigOptionsProvider(analyzerConfigOptions), parseOptions: new CSharpParseOptions(languageVersion) );