1+ using System ;
2+ using System . Collections . Concurrent ;
3+ using System . Collections . Generic ;
4+ using System . Collections . Immutable ;
5+ using System . Linq ;
6+ using System . Reflection ;
7+ using System . Text ;
8+ using System . Text . RegularExpressions ;
9+ using Antelcat . AutoGen . ComponentModel . Diagnostic ;
10+ using Antelcat . AutoGen . SourceGenerators . Extensions ;
11+ using Antelcat . AutoGen . SourceGenerators . Generators . Base ;
12+ using Microsoft . CodeAnalysis ;
13+ using Microsoft . CodeAnalysis . CSharp . Syntax ;
14+
15+ namespace Antelcat . AutoGen . SourceGenerators . Generators . Diagnostic ;
16+
17+ [ Generator ( LanguageNames . CSharp ) ]
18+ public class MetadataGenerator : AttributeDetectBaseGenerator < AutoMetadataFrom >
19+ {
20+ private static IEnumerable < string > GetPlaceholders ( string template )
21+ {
22+ foreach ( var match in Regex . Matches ( template , "{[\\ w+((\\ .)?)]+}" ) )
23+ {
24+ var str = match . ToString ( ) ;
25+ yield return str . Substring ( 1 , str . Length - 2 ) ;
26+ }
27+ }
28+
29+ private static StringBuilder Resolve ( MemberInfo info , StringBuilder stringBuilder )
30+ {
31+ foreach ( var placeholder in GetPlaceholders ( stringBuilder . ToString ( ) ) )
32+ {
33+ object value = info ;
34+ foreach ( var part in placeholder . Split ( '.' ) )
35+ {
36+ if ( value is not MemberInfo memberInfo ) continue ;
37+ var val = GetValue ( memberInfo , part ) ;
38+ if ( val is null ) return stringBuilder ;
39+ value = val ;
40+ }
41+
42+ stringBuilder = stringBuilder . Replace ( '{' + placeholder + '}' ,
43+ value is Feast . CodeAnalysis . CompileTime . Type compile
44+ ? compile . Symbol . GetFullyQualifiedName ( )
45+ : value . ToString ( ) ) ;
46+ }
47+
48+ return stringBuilder ;
49+ }
50+
51+ private static object ? GetValue ( MemberInfo info , string propertyName )
52+ {
53+ var type = info . GetType ( ) ;
54+ return PropsMap . GetOrAdd ( info . GetType ( ) ,
55+ _ =>
56+ {
57+ ConcurrentDictionary < string , PropertyInfo ? > ret = [ ] ;
58+ var prop = type . GetProperty ( propertyName ) ;
59+ if ( prop is null ) return ret ;
60+ ret . TryAdd ( propertyName , prop ) ;
61+ return ret ;
62+ } ) . GetOrAdd ( propertyName , _ => type . GetProperty ( propertyName ) ) ?
63+ . GetValue ( info ) ;
64+ }
65+
66+ private static readonly ConcurrentDictionary < Type , ConcurrentDictionary < string , PropertyInfo ? > > PropsMap = [ ] ;
67+
68+ protected override bool FilterSyntax ( SyntaxNode node ) => true ;
69+
70+ protected override void Initialize ( SourceProductionContext context ,
71+ Compilation compilation ,
72+ ImmutableArray < GeneratorAttributeSyntaxContext > syntaxArray )
73+ {
74+ foreach ( var groupedSyntaxContext in syntaxArray . GroupBy ( x => ( x . TargetSymbol as INamedTypeSymbol ) ! ,
75+ SymbolEqualityComparer . Default ) )
76+ {
77+ var @class = ( groupedSyntaxContext . Key as INamedTypeSymbol ) ! ;
78+ foreach ( var syntaxContext in groupedSyntaxContext )
79+ {
80+ foreach ( var ( metadata , index ) in syntaxContext . Attributes . GetAttributes < AutoMetadataFrom > ( )
81+ . Select ( ( x , i ) => ( x , i ) ) )
82+ {
83+ var partial = @class . PartialTypeDeclaration ( ) ;
84+ List < string > members = [ ] ;
85+ if ( metadata . Leading != null ) members . Add ( metadata . Leading ) ;
86+ var target = metadata . ForType ;
87+ var fileName =
88+ $ "{ @class . ToType ( ) . QualifiedFullFileName ( ) } _From_{ target . QualifiedFullFileName ( ) } _{ index } .cs";
89+
90+ const BindingFlags flags = BindingFlags . NonPublic |
91+ BindingFlags . Public |
92+ BindingFlags . Instance |
93+ BindingFlags . Static ;
94+ Map ( MemberTypes . Field , ( ) => target . GetFields ( flags ) . Where ( x => ! x . IsSpecialName ) ) ;
95+ Map ( MemberTypes . Property , ( ) => target . GetProperties ( flags ) ) ;
96+ Map ( MemberTypes . Constructor , ( ) => target . GetConstructors ( flags ) ) ;
97+ Map ( MemberTypes . NestedType , ( ) => target . GetNestedTypes ( flags ) ) ;
98+ Map ( MemberTypes . Event , ( ) => target . GetEvents ( flags ) ) ;
99+ Map ( MemberTypes . Method , ( ) => target . GetMethods ( flags ) . Where ( x => ! x . IsSpecialName ) ) ;
100+
101+ if ( metadata . Final != null ) members . Add ( metadata . Final ) ;
102+ var member = ParseMemberDeclaration ( string . Join ( "" , members ) ) ;
103+ if ( member != null ) partial = partial . AddMembers ( member ) ;
104+
105+ var file = CompilationUnit ( )
106+ . AddPartialType ( @class , x => partial )
107+ . NormalizeWhitespace ( ) ;
108+ context . AddSource ( fileName , file . GetText ( Encoding . UTF8 ) ) ;
109+ continue ;
110+
111+ void Map ( MemberTypes memberTypes , Func < IEnumerable < MemberInfo > > memberGetter )
112+ {
113+ if ( ! metadata . MemberTypes . HasFlag ( memberTypes ) ) return ;
114+ members . AddRange ( memberGetter ( )
115+ . Select ( field => Resolve ( field , new StringBuilder ( metadata . Template ) )
116+ . ToString ( ) )
117+ ) ;
118+ }
119+ }
120+ }
121+ }
122+ }
123+ }
0 commit comments