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/EfGeneratorContext.cs b/src/BlueWest.MapTo/EfGeneratorContext.cs index 2619cfa..1583403 100644 --- a/src/BlueWest.MapTo/EfGeneratorContext.cs +++ b/src/BlueWest.MapTo/EfGeneratorContext.cs @@ -46,11 +46,11 @@ namespace MapTo protected ImmutableArray Usings { get; private set; } - public static EfGeneratorContext Create(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, MemberDeclarationSyntax typeSyntax) + public static EfGeneratorContext Create(Compilation compilation, SourceGenerationOptions sourceGenerationOptions, CandidateMember candidateMember) { - EfGeneratorContext context = typeSyntax switch + EfGeneratorContext context = candidateMember.MemberDeclarationSyntax switch { - PropertyDeclarationSyntax => new EfGeneratorContext(compilation, sourceGenerationOptions, typeSyntax), + PropertyDeclarationSyntax => new EfGeneratorContext(compilation, sourceGenerationOptions, candidateMember.MemberDeclarationSyntax), _ => throw new ArgumentOutOfRangeException() }; diff --git a/src/BlueWest.MapTo/EfMethodsGenerator.cs b/src/BlueWest.MapTo/EfMethodsGenerator.cs index 7798385..604ccef 100644 --- a/src/BlueWest.MapTo/EfMethodsGenerator.cs +++ b/src/BlueWest.MapTo/EfMethodsGenerator.cs @@ -10,15 +10,6 @@ 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 /// @@ -34,12 +25,15 @@ namespace MapTo /// public void Execute(GeneratorExecutionContext context) { + try { var options = SourceGenerationOptions.From(context); var compilation = context.Compilation - .AddSource(ref context, EfAddMethodsAttributeSource.Generate(options)); + .AddSource(ref context, EfAddMethodsAttributeSource.Generate(options)) + .AddSource(ref context, EfUpdateMethodsAttributeSource.Generate(options)); + if (context.SyntaxReceiver is EfMethodsSyntaxReceiver receiver && receiver.CandidateMembers.Any()) { @@ -53,7 +47,7 @@ namespace MapTo } } - private static void AddGeneratedExtensions(GeneratorExecutionContext context, Compilation compilation, IEnumerable candidateTypes, SourceGenerationOptions options) + private static void AddGeneratedExtensions(GeneratorExecutionContext context, Compilation compilation, IEnumerable candidateTypes, SourceGenerationOptions options) { //SpinWait.SpinUntil(() => Debugger.IsAttached); @@ -68,7 +62,7 @@ namespace MapTo } - var (source, hintName) = memberDeclarationSyntax switch + var (source, hintName) = memberDeclarationSyntax.MemberDeclarationSyntax switch { PropertyDeclarationSyntax => EfMethodsSource.Generate(mappingContext.Model), _ => 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