-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathEntityGenerator.cs
112 lines (89 loc) · 3.4 KB
/
EntityGenerator.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
using Architect.DomainModeling.Generator.Common;
using Architect.DomainModeling.Generator.Configurators;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Architect.DomainModeling.Generator;
[Generator]
public class EntityGenerator : SourceGenerator
{
public override void Initialize(IncrementalGeneratorInitializationContext context)
{
var provider = context.SyntaxProvider.CreateSyntaxProvider(FilterSyntaxNode, TransformSyntaxNode)
.Where(generatable => generatable is not null)
.DeduplicatePartials();
context.RegisterSourceOutput(provider, GenerateSource!);
var aggregatedProvider = provider
.Collect()
.Combine(EntityFrameworkConfigurationGenerator.CreateMetadataProvider(context));
context.RegisterSourceOutput(aggregatedProvider, DomainModelConfiguratorGenerator.GenerateSourceForEntities!);
}
private static bool FilterSyntaxNode(SyntaxNode node, CancellationToken cancellationToken = default)
{
// Class or record class
if (node is TypeDeclarationSyntax tds && tds is ClassDeclarationSyntax or RecordDeclarationSyntax { ClassOrStructKeyword.ValueText: "class" })
{
// With relevant attribute
if (tds.HasAttributeWithPrefix("Entity"))
return true;
}
return false;
}
private static Generatable? TransformSyntaxNode(GeneratorSyntaxContext context, CancellationToken cancellationToken = default)
{
var model = context.SemanticModel;
var tds = (TypeDeclarationSyntax)context.Node;
var type = model.GetDeclaredSymbol(tds);
if (type is null)
return null;
// Only with the attribute
if (type.GetAttribute("EntityAttribute", Constants.DomainModelingNamespace, arity: 0) is null)
return null;
// Only concrete
if (type.IsAbstract)
return null;
// Only non-generic
if (type.IsGenericType)
return null;
// Only non-nested
if (type.IsNested())
return null;
var result = new Generatable()
{
TypeLocation = type.Locations.FirstOrDefault(),
IsEntity = type.IsOrImplementsInterface(type => type.IsType(Constants.EntityInterfaceName, Constants.DomainModelingNamespace, arity: 0), out _),
TypeName = type.Name, // Non-generic by filter
ContainingNamespace = type.ContainingNamespace.ToString(),
};
var existingComponents = EntityTypeComponents.None;
existingComponents |= EntityTypeComponents.DefaultConstructor.If(type.Constructors.Any(ctor =>
!ctor.IsStatic && ctor.Parameters.Length == 0 /*&& ctor.DeclaringSyntaxReferences.Length > 0*/));
result.ExistingComponents = existingComponents;
return result;
}
private static void GenerateSource(SourceProductionContext context, Generatable generatable)
{
context.CancellationToken.ThrowIfCancellationRequested();
// Require the expected inheritance
if (!generatable.IsEntity)
{
context.ReportDiagnostic("EntityGeneratorUnexpectedInheritance", "Unexpected inheritance",
"Type marked as entity lacks IEntity interface.", DiagnosticSeverity.Warning, generatable.TypeLocation);
return;
}
}
[Flags]
internal enum EntityTypeComponents : ulong
{
None = 0,
DefaultConstructor = 1 << 1,
}
internal sealed record Generatable : IGeneratable
{
public bool IsEntity { get; set; }
public string TypeName { get; set; } = null!;
public string ContainingNamespace { get; set; } = null!;
public EntityTypeComponents ExistingComponents { get; set; }
public SimpleLocation? TypeLocation { get; set; }
}
}