1
+ using System . Collections . Generic ;
2
+ using System . Linq ;
3
+ using System . Text ;
4
+ using Microsoft . CodeAnalysis ;
5
+ using Microsoft . CodeAnalysis . CSharp ;
6
+
7
+ namespace ProtobufSourceGenerator ;
8
+
9
+ public class ProtoClassGenerator
10
+ {
11
+ public IEnumerable < ( string , string ) > CreateClasses ( IEnumerable < PropertyInfo > propertyShadows ) => propertyShadows . GroupBy ( x => x . TypeSymbol . Name ) . Select ( x => ( x . Key , CreateClass ( x ) ) ) ;
12
+
13
+ private string CreateClass ( IEnumerable < PropertyInfo > propertyShadows )
14
+ {
15
+ var classInfo = propertyShadows . First ( ) . TypeSymbol ;
16
+
17
+ StringBuilder sb = new ( ) ;
18
+ sb . AppendLine ( $ "namespace { classInfo . ContainingNamespace } ;") ;
19
+ sb . AppendLine ( ) ;
20
+
21
+ var classSyntax = SyntaxFactory . ClassDeclaration ( classInfo . Name )
22
+ . WithModifiers (
23
+ SyntaxFactory . TokenList (
24
+ new [ ] {
25
+ SyntaxFactory . Token ( SyntaxKind . PublicKeyword ) ,
26
+ SyntaxFactory . Token ( SyntaxKind . PartialKeyword )
27
+ } ) ) ;
28
+
29
+ int counter = 1 ;
30
+ foreach ( var shadow in propertyShadows )
31
+ {
32
+ if ( shadow . Property . AccessorList . Accessors . All ( x => x . Body == null && x . ExpressionBody == null ) )
33
+ {
34
+ string typeName = GetTypeName ( shadow ) ;
35
+ var newProperty = SyntaxFactory . PropertyDeclaration ( SyntaxFactory . ParseTypeName ( typeName ) ,
36
+ SyntaxFactory . Identifier ( $ "Proto{ shadow . Property . Identifier . Text } ") )
37
+ . WithModifiers ( SyntaxFactory . TokenList ( SyntaxFactory . Token ( SyntaxKind . PrivateKeyword ) ) ) ;
38
+
39
+ var getter = SyntaxFactory . AccessorDeclaration ( SyntaxKind . GetAccessorDeclaration )
40
+ . WithExpressionBody ( SyntaxFactory . ArrowExpressionClause ( SyntaxFactory . IdentifierName ( shadow . Property . Identifier . Text ) ) )
41
+ . WithSemicolonToken ( SyntaxFactory . Token ( SyntaxKind . SemicolonToken ) ) ;
42
+
43
+ var setter = SyntaxFactory . AccessorDeclaration ( SyntaxKind . GetAccessorDeclaration )
44
+ . WithExpressionBody ( SyntaxFactory . ArrowExpressionClause ( SyntaxFactory . AssignmentExpression (
45
+ SyntaxKind . SimpleAssignmentExpression ,
46
+ SyntaxFactory . IdentifierName ( shadow . Property . Identifier . Text ) ,
47
+ SyntaxFactory . IdentifierName ( "value" ) ) ) )
48
+ . WithSemicolonToken ( SyntaxFactory . Token ( SyntaxKind . SemicolonToken ) ) ;
49
+
50
+ var protoMemberAttribute = SyntaxFactory . SingletonList (
51
+ SyntaxFactory . AttributeList (
52
+ SyntaxFactory . SingletonSeparatedList (
53
+ SyntaxFactory . Attribute (
54
+ SyntaxFactory . QualifiedName ( SyntaxFactory . IdentifierName ( "Protobuf" ) , SyntaxFactory . IdentifierName ( "ProtoMember" ) ) )
55
+ . WithArgumentList (
56
+ SyntaxFactory . AttributeArgumentList (
57
+ SyntaxFactory . SingletonSeparatedList (
58
+ SyntaxFactory . AttributeArgument (
59
+ SyntaxFactory . LiteralExpression (
60
+ SyntaxKind . NumericLiteralExpression ,
61
+ SyntaxFactory . Literal ( counter ++ ) ) ) ) ) ) ) ) ) ;
62
+
63
+ newProperty = newProperty . WithAccessorList ( SyntaxFactory . AccessorList ( SyntaxFactory . List ( new [ ] { getter , setter } ) ) )
64
+ . NormalizeWhitespace ( ) . WithLeadingTrivia ( SyntaxFactory . TriviaList ( SyntaxFactory . Whitespace ( " " ) ) )
65
+ . WithAttributeLists ( protoMemberAttribute ) ;
66
+
67
+ classSyntax = classSyntax . WithMembers ( classSyntax . Members . Add ( newProperty ) ) ;
68
+ }
69
+ }
70
+ sb . Append ( classSyntax . NormalizeWhitespace ( ) . ToFullString ( ) ) ;
71
+ return sb . ToString ( ) ;
72
+ }
73
+
74
+ private static string GetTypeName ( PropertyInfo shadow )
75
+ {
76
+ if ( shadow . PropertySymbol . Type is INamedTypeSymbol propertyType )
77
+ return GetTypeName ( propertyType ) ;
78
+ return shadow . PropertySymbol . Type . Name ;
79
+ }
80
+
81
+ private static string GetTypeName ( INamedTypeSymbol type )
82
+ {
83
+ if ( ! type . IsGenericType )
84
+ return $ "{ type . ContainingNamespace } .{ type . Name } ";
85
+
86
+ StringBuilder sb = new ( ) ;
87
+ sb . Append ( $ "{ type . ContainingNamespace } { type . Name } <") ;
88
+
89
+ for ( int i = 0 ; i < type . TypeArguments . Length ; i ++ )
90
+ {
91
+ var typeArgument = type . TypeArguments [ i ] ;
92
+ if ( typeArgument is INamedTypeSymbol namedType )
93
+ sb . Append ( GetTypeName ( namedType ) ) ;
94
+ else
95
+ sb . Append ( typeArgument . Name ) ;
96
+
97
+ if ( i < type . TypeArguments . Length - 1 )
98
+ sb . Append ( ", " ) ;
99
+ }
100
+ sb . Append ( ">" ) ;
101
+ return sb . ToString ( ) ;
102
+ }
103
+ }
0 commit comments