Skip to content

Commit 705c187

Browse files
author
machibuse
committed
Add support for [Decimal] repeated field transformation in PropertyVisitor, update ListWrappers with RepeatedFieldDecimalWrapper, extend proto definitions, and add unit tests for decimal collections
1 parent 65a982f commit 705c187

6 files changed

Lines changed: 143 additions & 2 deletions

File tree

.claude/settings.local.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@
88
"Bash(dir:*)",
99
"Bash(findstr:*)",
1010
"WebFetch(domain:github.com)",
11-
"WebFetch(domain:reese.codes)"
11+
"WebFetch(domain:reese.codes)",
12+
"Bash(dotnet msbuild:*)",
13+
"Bash(cmd.exe /c \"taskkill /PID 9504 /F\")",
14+
"Bash(cmd.exe:*)",
15+
"Bash(powershell -Command:*)",
16+
"Bash(dotnet build-server:*)"
1217
]
1318
}
1419
}

Source/Porticle.Grpc.TypeMapper/ClassVisitor.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ public class ClassVisitor(TaskLoggingHelper log, bool wrapAllNonNullableStrings,
2929
node = node.AddMembers(InterfaceFromSource(ListWrappers.IListWithRangeAdd));
3030
}
3131

32+
if (propertyVisitor.NeedDecimalConverter)
33+
{
34+
node = node.AddMembers(ClassFromSource(ListWrappers.RepeatedFieldDecimalWrapper));
35+
if (!propertyVisitor.NeedGuidConverter)
36+
{
37+
node = node.AddMembers(InterfaceFromSource(ListWrappers.IListWithRangeAdd));
38+
}
39+
}
40+
3241
var methodVisitor = new MethodVisitor(propertyVisitor.ReplaceProps);
3342
node = (ClassDeclarationSyntax)methodVisitor.Visit(node);
3443

Source/Porticle.Grpc.TypeMapper/ListWrappers.cs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,85 @@ public System.Guid this[int index]
8989
}
9090
}
9191
""";
92+
93+
public static string RepeatedFieldDecimalWrapper = """
94+
class RepeatedFieldDecimalWrapper : IListWithRangeAdd<decimal>
95+
{
96+
private readonly Google.Protobuf.Collections.RepeatedField<string> _internList;
97+
98+
public RepeatedFieldDecimalWrapper(Google.Protobuf.Collections.RepeatedField<string> internList)
99+
{
100+
this._internList = internList;
101+
}
102+
103+
public System.Collections.Generic.IEnumerator<decimal> GetEnumerator()
104+
{
105+
using var enumerator = _internList.GetEnumerator();
106+
while (enumerator.MoveNext())
107+
{
108+
yield return decimal.Parse(enumerator.Current, System.Globalization.CultureInfo.InvariantCulture);
109+
}
110+
}
111+
112+
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
113+
{
114+
return GetEnumerator();
115+
}
116+
117+
public void Add(decimal item)
118+
{
119+
_internList.Add(item.ToString(System.Globalization.CultureInfo.InvariantCulture));
120+
}
121+
122+
public void Add(IEnumerable<decimal> items)
123+
{
124+
_internList.AddRange(items.Select(i => i.ToString(System.Globalization.CultureInfo.InvariantCulture)));
125+
}
126+
127+
public void Clear()
128+
{
129+
_internList.Clear();
130+
}
131+
132+
public bool Contains(decimal item)
133+
{
134+
return _internList.Contains(item.ToString(System.Globalization.CultureInfo.InvariantCulture));
135+
}
136+
137+
public void CopyTo(decimal[] array, int arrayIndex)
138+
{
139+
System.Linq.Enumerable.ToArray(System.Linq.Enumerable.Select(_internList, s => decimal.Parse(s, System.Globalization.CultureInfo.InvariantCulture))).CopyTo(array, arrayIndex);
140+
}
141+
142+
public bool Remove(decimal item)
143+
{
144+
return _internList.Remove(item.ToString(System.Globalization.CultureInfo.InvariantCulture));
145+
}
146+
147+
public int Count => _internList.Count;
148+
149+
public bool IsReadOnly => _internList.IsReadOnly;
150+
151+
public int IndexOf(decimal item)
152+
{
153+
return _internList.IndexOf(item.ToString(System.Globalization.CultureInfo.InvariantCulture));
154+
}
155+
156+
public void Insert(int index, decimal item)
157+
{
158+
_internList.Insert(index, item.ToString(System.Globalization.CultureInfo.InvariantCulture));
159+
}
160+
161+
public void RemoveAt(int index)
162+
{
163+
_internList.RemoveAt(index);
164+
}
165+
166+
public decimal this[int index]
167+
{
168+
get => decimal.Parse(_internList[index], System.Globalization.CultureInfo.InvariantCulture);
169+
set => _internList[index] = value.ToString(System.Globalization.CultureInfo.InvariantCulture);
170+
}
171+
}
172+
""";
92173
}

Source/Porticle.Grpc.TypeMapper/PropertyVisitor.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ public class PropertyVisitor(TaskLoggingHelper log, bool wrapAllNonNullableStrin
1111

1212
public bool NeedGuidConverter { get; set; }
1313

14+
public bool NeedDecimalConverter { get; set; }
15+
1416

1517
public override SyntaxNode? VisitPropertyDeclaration(PropertyDeclarationSyntax node)
1618
{
@@ -85,7 +87,14 @@ public class PropertyVisitor(TaskLoggingHelper log, bool wrapAllNonNullableStrin
8587

8688
if (property.GetLeadingTrivia().ToFullString().Contains("[Decimal]"))
8789
{
88-
log.LogError("Decimal is not supported for repeated fields");
90+
NeedDecimalConverter = true;
91+
var newReturnExpression = SyntaxFactory.InvocationExpression(SyntaxFactory.ParseExpression("new RepeatedFieldDecimalWrapper"),
92+
SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(originalReturnExpression))));
93+
var newReturnStatement = returnStatement.WithExpression(newReturnExpression).WithTrailingTrivia(SyntaxFactory.Space);
94+
var newGetterBody = SyntaxFactory.Block(newReturnStatement);
95+
var newGetter = getter.WithBody(newGetterBody.WithTrailingTrivia(SyntaxFactory.CarriageReturnLineFeed));
96+
property = property.ReplaceNode(getter, newGetter);
97+
return property.WithType(SyntaxFactory.ParseTypeName("IListWithRangeAdd<decimal>").WithTrailingTrivia(SyntaxFactory.ElasticSpace));
8998
}
9099

91100
if (property.GetLeadingTrivia().ToFullString().Contains("[NullableString]"))

Source/Porticle.Grpc.UnitTests/Test.proto

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ message TestMessage {
1212
repeated string list_of_guid = 5;
1313
string single_decimal = 8;
1414
google.protobuf.StringValue single_nullable_decimal = 9;
15+
repeated string list_of_decimal = 10;
1516
}
1617

1718
message TestMessageMapped {
@@ -37,6 +38,9 @@ message TestMessageMapped {
3738

3839
// [Decimal]
3940
google.protobuf.StringValue single_nullable_decimal = 9;
41+
42+
// [Decimal]
43+
repeated string list_of_decimal = 10;
4044
}
4145

4246
//////////////////////////////////////////////

Source/Porticle.Grpc.UnitTests/Tests.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,39 @@ public void TestDecimalMappedToUnmapped()
7272
Assert.AreEqual(Decimal2.ToString(CultureInfo.InvariantCulture), deserializedMessage.SingleNullableDecimal);
7373
}
7474

75+
[TestMethod]
76+
public void TestRepeatedDecimal()
77+
{
78+
decimal[] decimals = [1.5m, -99.99m, 0m];
79+
80+
var message = new TestMessageMapped { SingleGuid = Guid4, SingleDecimal = Decimal1, ListOfDecimal = { decimals } };
81+
82+
var byteArray = message.ToByteArray();
83+
84+
var deserializedMessage = TestMessageMapped.Parser.ParseFrom(byteArray);
85+
86+
Assert.AreEqual(3, deserializedMessage.ListOfDecimal.Count);
87+
Assert.AreEqual(1.5m, deserializedMessage.ListOfDecimal[0]);
88+
Assert.AreEqual(-99.99m, deserializedMessage.ListOfDecimal[1]);
89+
Assert.AreEqual(0m, deserializedMessage.ListOfDecimal[2]);
90+
Assert.IsTrue(message.ListOfDecimal.SequenceEqual(deserializedMessage.ListOfDecimal));
91+
}
92+
93+
[TestMethod]
94+
public void TestRepeatedDecimalUnmappedToMapped()
95+
{
96+
var message = new TestMessage { SingleGuid = Guid4.ToString(), SingleDecimal = Decimal1.ToString(CultureInfo.InvariantCulture), ListOfDecimal = { "1.5", "-99.99", "0" } };
97+
98+
var byteArray = message.ToByteArray();
99+
100+
var deserializedMessage = TestMessageMapped.Parser.ParseFrom(byteArray);
101+
102+
Assert.AreEqual(3, deserializedMessage.ListOfDecimal.Count);
103+
Assert.AreEqual(1.5m, deserializedMessage.ListOfDecimal[0]);
104+
Assert.AreEqual(-99.99m, deserializedMessage.ListOfDecimal[1]);
105+
Assert.AreEqual(0m, deserializedMessage.ListOfDecimal[2]);
106+
}
107+
75108
[TestMethod]
76109
public void TestWithNull()
77110
{

0 commit comments

Comments
 (0)