Skip to content

Commit 5016d2c

Browse files
author
machibuse
committed
Add support for nullable enums and refactor PropertyVisitor for improved maintainability and error handling. Extend test coverage with new enum cases.
1 parent 715009a commit 5016d2c

3 files changed

Lines changed: 77 additions & 33 deletions

File tree

Source/Porticle.Grpc.GuidMapper/Extensions.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
namespace Porticle.Grpc.GuidMapper;
1+
using Microsoft.CodeAnalysis;
2+
using Microsoft.CodeAnalysis.CSharp;
3+
using Microsoft.CodeAnalysis.CSharp.Syntax;
4+
5+
namespace Porticle.Grpc.GuidMapper;
26

37
public static class Extensions
48
{
@@ -12,6 +16,16 @@ public static T CheckNotNull<T>(this T? value, string valueDescription) where T
1216
return value;
1317
}
1418

19+
public static AccessorDeclarationSyntax GetGetter(this PropertyDeclarationSyntax property)
20+
{
21+
return property.AccessorList.CheckNotNull("Accessors not found").Accessors.FirstOrDefault(a => a.IsKind(SyntaxKind.GetAccessorDeclaration)).CheckNotNull("Getter not found");
22+
}
23+
24+
public static AccessorDeclarationSyntax GetSetter(this PropertyDeclarationSyntax property)
25+
{
26+
return property.AccessorList.CheckNotNull("Accessors not found").Accessors.FirstOrDefault(a => a.IsKind(SyntaxKind.SetAccessorDeclaration)).CheckNotNull("Setter not found");
27+
}
28+
1529

1630

1731

Source/Porticle.Grpc.GuidMapper/PropertyVisitor.cs

Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -102,23 +102,39 @@ public class PropertyVisitor : CSharpSyntaxRewriter
102102

103103
return null;
104104
}
105-
106-
if (property.Type.ToString() == "string")
105+
106+
if (property.GetLeadingTrivia().ToFullString().Contains("[NullableEnum]"))
107107
{
108-
if (property.GetLeadingTrivia().ToFullString().Contains("[GrpcGuid]"))
109-
{
110-
return ConvertToGuidProperty(property);
111-
}
108+
return ConvertOptionalToNullableEnum(property);
109+
}
110+
111+
112+
113+
// dont change anything
114+
return null;
115+
}
112116

113-
if (property.GetLeadingTrivia().ToFullString().Contains("[NullableString]"))
114-
{
115-
return ConvertToNullableStringProperty(property);
116-
}
117+
private PropertyDeclarationSyntax? ConvertOptionalToNullableEnum(PropertyDeclarationSyntax property)
118+
{
119+
var setter = property.GetSetter();
120+
121+
var getter = property.GetGetter();
122+
123+
AssignmentExpressionSyntax[] assignment = setter.Body!.Statements
124+
.OfType<ExpressionStatementSyntax>()
125+
.Select(s => (s.Expression as AssignmentExpressionSyntax)!)
126+
.ToArray();
117127

118-
return null;
128+
if (assignment.Length != 2)
129+
{
130+
throw new TypeMapperException("Exactly 2 Assignment expressions expected in optional setter");
119131
}
132+
133+
var hasBitsAssignment = assignment[0];
134+
var setValueAssignment = assignment[0];
120135

121-
// dont change anything
136+
137+
122138
return null;
123139
}
124140

@@ -154,9 +170,9 @@ public class PropertyVisitor : CSharpSyntaxRewriter
154170

155171
private PropertyDeclarationSyntax? ConvertToGuidProperty(PropertyDeclarationSyntax property)
156172
{
157-
var setter = property.AccessorList?.Accessors.FirstOrDefault(a => a.IsKind(SyntaxKind.SetAccessorDeclaration));
173+
var setter = property.GetSetter();
158174

159-
if (setter?.Body == null)
175+
if (setter.Body == null)
160176
{
161177
Console.WriteLine($"[Error] No setter found in property {property.Identifier}");
162178
return null;
@@ -165,25 +181,15 @@ public class PropertyVisitor : CSharpSyntaxRewriter
165181
var isNullable = !setter.Body.ToFullString().Contains("ProtoPreconditions.CheckNotNull");
166182

167183
// Manipulate setter
168-
var assignment = setter.Body.Statements
169-
.OfType<ExpressionStatementSyntax>()
170-
.Select(s => s.Expression as AssignmentExpressionSyntax)
171-
.FirstOrDefault();
172-
173-
if (assignment == null)
174-
{
175-
Console.WriteLine($"[Error] Setter has no valid assignment expression in property {property.Identifier}");
176-
return null;
177-
}
184+
var assignment = GetAssignmentExpression(setter);
178185

179186
var originalRightHandSide = assignment.Right;
180187

181188
if (!isNullable)
182189
{
183190
if (originalRightHandSide is not InvocationExpressionSyntax invocationExpr || !invocationExpr.Expression.ToString().EndsWith("CheckNotNull"))
184191
{
185-
Console.WriteLine($"[Error] Can't find CheckNotNull call in setter od property {property.Identifier}");
186-
return null;
192+
throw new TypeMapperException($"[Error] Can't find CheckNotNull call in setter od property {property.Identifier}");
187193
}
188194

189195
originalRightHandSide = invocationExpr.ArgumentList.Arguments.First().Expression;
@@ -195,16 +201,15 @@ public class PropertyVisitor : CSharpSyntaxRewriter
195201
var parenthesizedoriginalRightHandSide = SyntaxFactory.ParenthesizedExpression(originalRightHandSide);
196202

197203
ExpressionSyntax newRightHandSide = isNullable
198-
? SyntaxFactory.ConditionalAccessExpression(parenthesizedoriginalRightHandSide,
199-
SyntaxFactory.InvocationExpression(SyntaxFactory.MemberBindingExpression(toStringMethodName), toStringArgumentList))
200-
: SyntaxFactory.InvocationExpression(
201-
SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, parenthesizedoriginalRightHandSide, toStringMethodName), toStringArgumentList);
204+
? SyntaxFactory.ConditionalAccessExpression(parenthesizedoriginalRightHandSide, SyntaxFactory.InvocationExpression(SyntaxFactory.MemberBindingExpression(toStringMethodName), toStringArgumentList))
205+
: SyntaxFactory.InvocationExpression(SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, parenthesizedoriginalRightHandSide, toStringMethodName), toStringArgumentList);
202206
var newAssignment = assignment.WithRight(newRightHandSide);
203207
var newSetter = setter.ReplaceNode(assignment, newAssignment);
208+
204209
property = property.ReplaceNode(setter, newSetter);
205210

206211
// Manipulate getter
207-
var getter = property.AccessorList?.Accessors.FirstOrDefault(a => a.IsKind(SyntaxKind.GetAccessorDeclaration));
212+
var getter = property.GetGetter();
208213

209214
if (getter?.Body == null)
210215
{
@@ -264,4 +269,19 @@ public class PropertyVisitor : CSharpSyntaxRewriter
264269

265270
return property;
266271
}
272+
273+
private static AssignmentExpressionSyntax GetAssignmentExpression(AccessorDeclarationSyntax setter)
274+
{
275+
var assignment = setter.Body.Statements
276+
.OfType<ExpressionStatementSyntax>()
277+
.Select(s => s.Expression as AssignmentExpressionSyntax)
278+
.FirstOrDefault();
279+
280+
if (assignment == null)
281+
{
282+
throw new TypeMapperException($"Setter has no valid assignment expression");
283+
}
284+
285+
return assignment;
286+
}
267287
}

Source/Porticle.Grpc.UnitTests/Test.proto

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,15 @@ message TestMessageMapped {
2525
// [GrpcGuid]
2626
repeated string list_of_guid = 5;
2727

28-
28+
TestEnum enum_required = 6;
29+
30+
// [NullableEnum]
31+
optional TestEnum enum_optional = 7;
2932
}
33+
34+
35+
enum TestEnum
36+
{
37+
FOO = 0;
38+
BAR = 1;
39+
}

0 commit comments

Comments
 (0)