@@ -32,18 +32,39 @@ public class ForgedGenerator : IIncrementalGenerator
3232
3333 public void Initialize ( IncrementalGeneratorInitializationContext context )
3434 {
35- var typesToGenerate = context . SyntaxProvider
35+ var typesToFake = context . SyntaxProvider
3636 . ForAttributeWithMetadataName (
3737 "Forged.Core.FakeAttribute" ,
3838 predicate : static ( s , _ ) => s is ClassDeclarationSyntax or RecordDeclarationSyntax or StructDeclarationSyntax ,
39- transform : static ( ctx , _ ) => GetSemanticTargetForGeneration ( ctx . SemanticModel , ctx . TargetNode )
39+ transform : static ( ctx , _ ) => GetFakedTypes ( ctx . SemanticModel , ctx . TargetNode )
4040 )
41- . Where ( static m => m is not null ) ;
41+ . Where ( static m => m is not null )
42+ . Select ( static ( m , _ ) => m ! )
43+ . Collect ( ) ;
4244
43- context . RegisterSourceOutput ( typesToGenerate , static ( spc , source ) => Execute ( spc , source ) ) ;
45+ var fakersToGenerate = context . SyntaxProvider
46+ . ForAttributeWithMetadataName (
47+ "Forged.Core.FakerAttribute`1" ,
48+ predicate : static ( s , _ ) => s is ClassDeclarationSyntax ,
49+ transform : static ( ctx , _ ) => GetFakers ( ctx . SemanticModel , ctx . TargetNode )
50+ )
51+ . Where ( static m => m is not null )
52+ . Select ( static ( m , _ ) => m ! )
53+ . Collect ( ) ;
54+
55+ var combined = typesToFake
56+ . Combine ( fakersToGenerate ) ;
57+
58+ context . RegisterSourceOutput ( combined , static ( spc , source ) => {
59+ var ( types , fakers ) = source ;
60+ foreach ( var ttg in types . Concat ( fakers ) )
61+ {
62+ Execute ( spc , ttg ) ;
63+ }
64+ } ) ;
4465 }
4566
46- private static TypeToGenerate ? GetSemanticTargetForGeneration ( SemanticModel semanticModel , SyntaxNode node )
67+ private static TypeToGenerate ? GetFakedTypes ( SemanticModel semanticModel , SyntaxNode node )
4768 {
4869 if ( semanticModel . GetDeclaredSymbol ( node ) is not INamedTypeSymbol symbol )
4970 {
@@ -66,16 +87,46 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
6687 Properties : properties . ToEquatableReadOnlyList ( )
6788 ) ;
6889 }
69-
70- private static void Execute ( SourceProductionContext context , TypeToGenerate ? typeToGenerate )
90+
91+ private static TypeToGenerate ? GetFakers ( SemanticModel semanticModel , SyntaxNode node )
7192 {
72- if ( typeToGenerate is null ) return ;
93+ if ( semanticModel . GetDeclaredSymbol ( node ) is not INamedTypeSymbol symbol )
94+ {
95+ return null ;
96+ }
97+
98+ var attribute = symbol . GetAttributes ( ) . FirstOrDefault ( a => a . AttributeClass ? . Name == "FakerAttribute" ) ;
99+ if ( attribute is not { AttributeClass : { IsGenericType : true , TypeArguments : [ INamedTypeSymbol targetType ] } } )
100+ {
101+ return null ;
102+ }
103+
104+ var properties = targetType . GetMembers ( )
105+ . OfType < IPropertySymbol > ( )
106+ . Where ( p => p . SetMethod is not null && p . DeclaredAccessibility == Accessibility . Public )
107+ . Select ( p => new PropertyToGenerate (
108+ Name : p . Name ,
109+ Type : p . Type . ToDisplayString ( ) ,
110+ IsRequired : p . IsRequired
111+ ) )
112+ . ToList ( ) ;
113+
114+ return new TypeToGenerate (
115+ Namespace : targetType . ContainingNamespace . IsGlobalNamespace ? string . Empty : targetType . ContainingNamespace . ToDisplayString ( ) ,
116+ Name : targetType . Name ,
117+ Properties : properties . ToEquatableReadOnlyList ( ) ,
118+ FakerName : symbol . Name
119+ ) ;
120+ }
73121
122+ private static void Execute ( SourceProductionContext context , TypeToGenerate typeToGenerate )
123+ {
74124 var w = new IndentedWriter ( ) ;
75125 w . WriteLine ( Header ) ;
76126 w . WriteLine ( "#nullable enable" ) ;
77-
78-
127+
128+ var fakerName = typeToGenerate . FakerName ?? $ "{ typeToGenerate . Name } Faker";
129+
79130 if ( ! string . IsNullOrEmpty ( typeToGenerate . Namespace ) )
80131 {
81132 w . WriteLine ( ) ;
@@ -85,7 +136,7 @@ private static void Execute(SourceProductionContext context, TypeToGenerate? typ
85136 w . WriteLine ( ) ;
86137 w . WriteLine ( Attributes ) ;
87138
88- w . WriteLine ( $ "public partial class { typeToGenerate . Name } Faker : global::Forged.Core.Faker<{ typeToGenerate . Name } >") ;
139+ w . WriteLine ( $ "public partial class { fakerName } : global::Forged.Core.Faker<{ typeToGenerate . Name } >") ;
89140 w . BodyBlock ( ( ) => {
90141
91142 foreach ( var prop in typeToGenerate . Properties )
@@ -105,7 +156,7 @@ private static void Execute(SourceProductionContext context, TypeToGenerate? typ
105156
106157 w . WriteLine ( ) ;
107158 w . WriteLine ( "// Constructor" ) ;
108- w . WriteLine ( $ "public { typeToGenerate . Name } Faker (global::System.Random? random = null, global::System.Globalization.CultureInfo? locale = null) : base(random, locale) {{ }}") ;
159+ w . WriteLine ( $ "public { fakerName } (global::System.Random? random = null, global::System.Globalization.CultureInfo? locale = null) : base(random, locale) {{ }}") ;
109160
110161 w . WriteLine ( ) ;
111162 w . WriteLine ( $ "public override { typeToGenerate . Name } Get()") ;
@@ -132,16 +183,16 @@ private static void Execute(SourceProductionContext context, TypeToGenerate? typ
132183 } ) ;
133184 } ) ;
134185 } ) ;
135-
136- context . AddSource ( $ "{ typeToGenerate . Name } Faker .g.cs", SourceText . From ( w . ToString ( ) , Encoding . UTF8 ) ) ;
186+
187+ context . AddSource ( $ "{ fakerName } .g.cs", SourceText . From ( w . ToString ( ) , Encoding . UTF8 ) ) ;
137188 }
138189
139190 private static string Decapitalize ( string input )
140191 {
141192 var span = input . AsSpan ( ) ;
142193 Span < char > result = stackalloc char [ span . Length ] ;
143194 result [ 0 ] = char . ToLowerInvariant ( span [ 0 ] ) ;
144- span . Slice ( 1 ) . CopyTo ( result . Slice ( 1 ) ) ;
195+ span [ 1 .. ] . CopyTo ( result [ 1 .. ] ) ;
145196 return result . ToString ( ) ;
146197 }
147198}
0 commit comments