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