diff --git a/src/BlueWest.MapTo/BlueWest.MapTo.csproj b/src/BlueWest.MapTo/BlueWest.MapTo.csproj index 01debfe..04ec218 100644 --- a/src/BlueWest.MapTo/BlueWest.MapTo.csproj +++ b/src/BlueWest.MapTo/BlueWest.MapTo.csproj @@ -45,6 +45,7 @@ + diff --git a/src/BlueWest.MapTo/EfAddMethodsGenerator.cs b/src/BlueWest.MapTo/EfAddMethodsGenerator.cs index ac63178..f73093a 100644 --- a/src/BlueWest.MapTo/EfAddMethodsGenerator.cs +++ b/src/BlueWest.MapTo/EfAddMethodsGenerator.cs @@ -10,11 +10,20 @@ 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 EfAddMethodsGenerator: ISourceGenerator + public class EfMethodsGenerator: ISourceGenerator { /// public void Initialize(GeneratorInitializationContext context) @@ -44,11 +53,11 @@ namespace MapTo } } - private static void AddGeneratedExtensions(GeneratorExecutionContext context, Compilation compilation, IEnumerable candidateTypes, SourceGenerationOptions options) + private static void AddGeneratedExtensions(GeneratorExecutionContext context, Compilation compilation, IEnumerable candidateMembers, SourceGenerationOptions options) { //SpinWait.SpinUntil(() => Debugger.IsAttached); - foreach (var memberDeclarationSyntax in candidateTypes) + foreach (var candidateMember in candidateMembers) { string addSourceTemplate = string.Empty; @@ -60,7 +69,7 @@ namespace MapTo .ToString() ?? string.Empty; } - var mappingContext = EfAddGeneratorContext.Create(compilation, options, memberDeclarationSyntax, addSourceTemplate); + var mappingContext = EfAddGeneratorContext.Create(compilation, options, candidateMember.MemberDeclarationSyntax, addSourceTemplate); mappingContext.Diagnostics.ForEach(context.ReportDiagnostic); @@ -69,7 +78,7 @@ namespace MapTo continue; } - var (source, hintName) = memberDeclarationSyntax switch + var (source, hintName) = candidateMember.MemberDeclarationSyntax switch { PropertyDeclarationSyntax => EfMethodsSource.Generate(mappingContext.Model, addSourceTemplate), _ => throw new ArgumentOutOfRangeException() diff --git a/src/BlueWest.MapTo/EfMethodsSyntaxReceiver.cs b/src/BlueWest.MapTo/EfMethodsSyntaxReceiver.cs index df28571..30b048e 100644 --- a/src/BlueWest.MapTo/EfMethodsSyntaxReceiver.cs +++ b/src/BlueWest.MapTo/EfMethodsSyntaxReceiver.cs @@ -6,9 +6,22 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; namespace MapTo { + internal enum EfMethodsAttributeType + { + Add, + Update, + Invalid + } + + internal record CandidateMember( + MemberDeclarationSyntax MemberDeclarationSyntax, + EfMethodsAttributeType AttributeType + ); + + internal class EfMethodsSyntaxReceiver : ISyntaxReceiver { - public List CandidateMembers { get; } = new(); + public List CandidateMembers { get; } = new(); public void OnVisitSyntaxNode(SyntaxNode syntaxNode) { @@ -17,22 +30,44 @@ namespace MapTo return; } + var syntaxAttributeState = EfMethodsAttributeType.Invalid; + var attributeSyntax = attributes .SelectMany(a => a.Attributes) - .FirstOrDefault(a => a.Name is - IdentifierNameSyntax { Identifier: { ValueText: EfAddMethodsAttributeSource.AttributeName } } // For: [EfAddMethods] - or - QualifiedNameSyntax // For: [MapTo.EfAddMethods] - { - Left: IdentifierNameSyntax { Identifier: { ValueText: Constants.RootNamespace } }, - Right: IdentifierNameSyntax { Identifier: { ValueText: EfAddMethodsAttributeSource.AttributeName } } - } - ); + .FirstOrDefault(a => + { + syntaxAttributeState = IsValidAttributeType(a); + return syntaxAttributeState != EfMethodsAttributeType.Invalid; + }); + if (attributeSyntax is not null) { - CandidateMembers.Add(typeDeclarationSyntax); + CandidateMembers.Add(new CandidateMember(typeDeclarationSyntax, syntaxAttributeState)); } } + + private static EfMethodsAttributeType IsValidAttributeType(AttributeSyntax a) + { + return a.Name switch + { + IdentifierNameSyntax { Identifier: { ValueText: EfAddMethodsAttributeSource.AttributeName } } // For: [EfAddMethods] + or QualifiedNameSyntax // For: [MapTo.EfAddMethods] + { + Left: IdentifierNameSyntax { Identifier: { ValueText: Constants.RootNamespace } }, + Right: IdentifierNameSyntax { Identifier: { ValueText: EfAddMethodsAttributeSource.AttributeName } } + } => EfMethodsAttributeType.Add, + + IdentifierNameSyntax { Identifier: { ValueText: EfUpdateMethodsAttributeSource.AttributeName } } // For: [EfAddMethods] + or QualifiedNameSyntax // For: [MapTo.EfUpdateMethods] + { + Left: IdentifierNameSyntax { Identifier: { ValueText: Constants.RootNamespace } }, + Right: IdentifierNameSyntax { Identifier: { ValueText: EfUpdateMethodsAttributeSource.AttributeName } } + } => EfMethodsAttributeType.Update, + + _ => EfMethodsAttributeType.Invalid + }; + } + } } \ No newline at end of file diff --git a/src/BlueWest.MapTo/Sources/EfUpdateMethodsAttributeSource.cs b/src/BlueWest.MapTo/Sources/EfUpdateMethodsAttributeSource.cs new file mode 100644 index 0000000..36457ff --- /dev/null +++ b/src/BlueWest.MapTo/Sources/EfUpdateMethodsAttributeSource.cs @@ -0,0 +1,46 @@ +using static MapTo.Sources.Constants; + +namespace MapTo.Sources +{ + public class EfUpdateMethodsAttributeSource + { + internal const string AttributeName = "EfUpdateMethods"; + internal const string AttributeClassName = AttributeName + "Attribute"; + internal const string FullyQualifiedName = RootNamespace + "." + AttributeClassName; + + internal static SourceCode Generate(SourceGenerationOptions options) + { + using var builder = new SourceBuilder() + .WriteLine(GeneratedFilesHeader) + .WriteLine("using System;") + .WriteLine() + .WriteLine($"namespace {RootNamespace}") + .WriteOpeningBracket(); + + if (options.GenerateXmlDocument) + { + builder + .WriteLine("/// ") + .WriteLine("/// Generate Add methods for interacting with the entity") + .WriteLine("/// "); + } + + builder + .WriteLine("[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]") + .WriteLine($"public sealed class {AttributeName}Attribute : Attribute") + .WriteOpeningBracket(); + + builder + .WriteLine($"public {AttributeName}Attribute(Type updateType = null, Type returnType = null, string keyPropertyMemberName, Type keyPropertyMemberType)") + .WriteOpeningBracket() + .WriteClosingBracket() + .WriteLine(); + + builder + .WriteClosingBracket() // class + .WriteClosingBracket(); // namespace + + return new(builder.ToString(), $"{AttributeName}Attribute.g.cs"); + } + } +} \ No newline at end of file