Skip to content

Commit 99d0d74

Browse files
committed
Refactor code to improve readability and formatting. Added support for optional enums in generated mapping. Simplified getters and setters.
1 parent dfa6d36 commit 99d0d74

16 files changed

Lines changed: 345 additions & 151 deletions

README.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ A Roslyn-based post-processor for protoc-generated files that adds native Guid,
44

55
## Build State
66

7-
[![Build and Release](https://github.com/Machibuse/Porticle.Grpc.GuidMapper/actions/workflows/release.yaml/badge.svg)](https://github.com/Machibuse/Porticle.CLDR/actions/workflows/release.yaml)
7+
[![Build and Release](https://github.com/Machibuse/Porticle.Grpc.GuidMapper/actions/workflows/release.yaml/badge.svg)](https://github.com/Machibuse/Porticle.CLDR/actions/workflows/release.yaml)
88

9-
## Nuget
9+
## Nuget
1010

1111
[![NuGet Latest Version](https://img.shields.io/nuget/v/Porticle.Grpc.GuidMapper.svg)](https://www.nuget.org/packages/Porticle.Grpc.GuidMapper/) [![NuGet Downloads](https://img.shields.io/nuget/dt/Porticle.Grpc.GuidMapper.svg)](https://www.nuget.org/packages/Porticle.CLDR.Units/)
1212

@@ -19,6 +19,7 @@ code.
1919
## Installation
2020

2121
### Install the package via NuGet:
22+
2223
```powershell
2324
dotnet add package Porticle.Grpc.GuidMapper
2425
```
@@ -37,19 +38,20 @@ After installing the Package, this Post build step ist dynamically added to your
3738
</Project>
3839
```
3940

40-
Dont wonder ist you cant se it in your csproj file. It is dynamically added when your build is processed.
41+
Dont wonder ist you cant se it in your csproj file. It is dynamically added when your build is processed.
4142

4243
## Usage
4344

4445
There are three things you can do in your .proto files:
46+
4547
- Add `// [GrpcGuid]` as comment to a string field - Converts the corresponding c# string property to Guid
4648
- Add `// [GrpcGuid]` as comment to a StringValue field - Converts the corresponding c# string property to Guid?
4749
- Add `// [NullableString]` as comment to a StringValue field - Converts the corresponding c# string property to string?
4850

49-
5051
First an Example of a default .proto file
5152

5253
### Without GuidMapper
54+
5355
```protobuf
5456
syntax = "proto3";
5557
@@ -69,7 +71,9 @@ message User {
6971
repeated string role_ids = 4;
7072
}
7173
```
74+
7275
Will result in generated code like this, everything is a string
76+
7377
```csharp
7478
/// <summary>Guid of the user object</summary>
7579
public string Id {
@@ -96,6 +100,7 @@ public pbc::RepeatedField<string> RoleIds {
96100
```
97101

98102
### With GuidMapper
103+
99104
```protobuf
100105
syntax = "proto3";
101106
@@ -115,7 +120,9 @@ message User {
115120
repeated string role_ids = 4;
116121
}
117122
```
123+
118124
Will result in generated code like this, using string? Guid and Guid?
125+
119126
```csharp
120127
/// <summary>[GrpcGuid] Guid of the user object</summary>
121128
public global::System.Guid Id {

Source/Porticle.Grpc.GuidMapper/ClassVisitor.cs

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,21 @@ public class ClassVisitor : CSharpSyntaxRewriter
99
public override SyntaxNode? VisitClassDeclaration(ClassDeclarationSyntax node)
1010
{
1111
var marker = "[Porticle.Grpc.GuidMapper]";
12-
12+
1313
if (node.GetLeadingTrivia().ToFullString().Contains(marker))
14-
{
1514
// Skip if marker exists - class alreqady patched
1615
return node;
17-
}
18-
16+
1917
// Add marker
2018
var trivia = SyntaxFactory.TriviaList(SyntaxFactory.Comment("/// <remark>" + marker + "</remark>"), SyntaxFactory.LineFeed).AddRange(node.GetLeadingTrivia());
21-
node = node.WithLeadingTrivia(trivia);
22-
19+
node = node.WithLeadingTrivia(trivia);
20+
2321
var propertyVisitor = new PropertyVisitor();
2422
node = (ClassDeclarationSyntax)propertyVisitor.Visit(node);
2523

26-
if (propertyVisitor.NeedGuidConverter)
27-
{
28-
node = node.AddMembers(ClassFromSource(ListWrappers.RepeatedFieldGuidWrapper));
29-
}
24+
if (propertyVisitor.NeedGuidConverter) node = node.AddMembers(ClassFromSource(ListWrappers.RepeatedFieldGuidWrapper));
3025

31-
Console.WriteLine("Visit Methods for "+propertyVisitor.ReplaceProps.Count+" props");
26+
Console.WriteLine("Visit Methods for " + propertyVisitor.ReplaceProps.Count + " props");
3227
var methodVisitor = new MethodVisitor(propertyVisitor.ReplaceProps);
3328
node = (ClassDeclarationSyntax)methodVisitor.Visit(node);
3429

Source/Porticle.Grpc.GuidMapper/Extensions.cs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,11 @@ public static class Extensions
88
{
99
public static T CheckNotNull<T>(this T? value, string valueDescription) where T : class
1010
{
11-
if (value == null)
12-
{
13-
throw new TypeMapperException(valueDescription);
14-
}
11+
if (value == null) throw new TypeMapperException(valueDescription);
1512

1613
return value;
1714
}
18-
15+
1916
public static AccessorDeclarationSyntax GetGetter(this PropertyDeclarationSyntax property)
2017
{
2118
return property.AccessorList.CheckNotNull("Accessors not found").Accessors.FirstOrDefault(a => a.IsKind(SyntaxKind.GetAccessorDeclaration)).CheckNotNull("Getter not found");
@@ -25,8 +22,4 @@ public static AccessorDeclarationSyntax GetSetter(this PropertyDeclarationSyntax
2522
{
2623
return property.AccessorList.CheckNotNull("Accessors not found").Accessors.FirstOrDefault(a => a.IsKind(SyntaxKind.SetAccessorDeclaration)).CheckNotNull("Setter not found");
2724
}
28-
29-
30-
31-
3225
}

Source/Porticle.Grpc.GuidMapper/ListWrappers.cs

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,80 @@
22

33
public static class ListWrappers
44
{
5-
public static string RepeatedFieldGuidWrapper = "class RepeatedFieldGuidWrapper : System.Collections.Generic.IList<System.Guid>\n{\n private readonly Google.Protobuf.Collections.RepeatedField<string> _internList;\n\n public RepeatedFieldGuidWrapper(Google.Protobuf.Collections.RepeatedField<string> internList)\n {\n this._internList = internList;\n }\n\n public System.Collections.Generic.IEnumerator<System.Guid> GetEnumerator()\n {\n using var enumerator = _internList.GetEnumerator();\n while (enumerator.MoveNext())\n {\n yield return System.Guid.Parse(enumerator.Current);\n }\n }\n\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()\n {\n return GetEnumerator();\n }\n\n public void Add(System.Guid item)\n {\n _internList.Add(item.ToString(\"D\"));\n }\n\n public void Clear()\n {\n _internList.Clear();\n }\n\n public bool Contains(System.Guid item)\n {\n return _internList.Contains(item.ToString(\"D\"));\n }\n\n public void CopyTo(System.Guid[] array, int arrayIndex)\n {\n System.Linq.Enumerable.ToArray(System.Linq.Enumerable.Select(_internList, System.Guid.Parse)).CopyTo(array, arrayIndex);\n }\n\n public bool Remove(System.Guid item)\n {\n return _internList.Remove(item.ToString(\"D\"));\n }\n\n public int Count => _internList.Count;\n \n public bool IsReadOnly => _internList.IsReadOnly;\n \n public int IndexOf(System.Guid item)\n {\n return _internList.IndexOf(item.ToString(\"D\"));\n }\n\n public void Insert(int index, System.Guid item)\n {\n _internList.Insert(index, item.ToString(\"D\"));\n }\n\n public void RemoveAt(int index)\n {\n _internList.RemoveAt(index);\n }\n\n public System.Guid this[int index]\n {\n get => System.Guid.Parse(_internList[index]);\n set => _internList[index] = value.ToString(\"D\");\n }\n}";
6-
7-
// not used, because it cant work because of a bug in grpc code generation
8-
//// public static string RepeatedFieldNullableGuidWrapper = "class RepeatedFieldNullableGuidWrapper : System.Collections.Generic.IList<System.Guid?>\n{\n private readonly Google.Protobuf.Collections.RepeatedField<string> _internList;\n\n public RepeatedFieldNullableGuidWrapper(Google.Protobuf.Collections.RepeatedField<string> internList)\n {\n this._internList = internList;\n }\n\n public System.Collections.Generic.IEnumerator<System.Guid?> GetEnumerator()\n {\n using var enumerator = _internList.GetEnumerator();\n while (enumerator.MoveNext())\n {\n yield return string.IsNullOrWhiteSpace(enumerator.Current) ? null : System.Guid.Parse(enumerator.Current);\n }\n }\n\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()\n {\n return GetEnumerator();\n }\n\n public void Add(System.Guid? item)\n {\n _internList.Add(item?.ToString(\"D\")!);\n }\n\n public void Clear()\n {\n _internList.Clear();\n }\n\n public bool Contains(System.Guid? item)\n {\n return _internList.Contains(item?.ToString(\"D\")!);\n }\n\n public void CopyTo(System.Guid?[] array, int arrayIndex)\n {\n System.Linq.Enumerable.ToArray(System.Linq.Enumerable.Select(_internList, s => string.IsNullOrWhiteSpace(s) ? null : (System.Guid?)System.Guid.Parse(s!))).CopyTo(array, arrayIndex);\n }\n\n public bool Remove(System.Guid? item)\n {\n return _internList.Remove(item?.ToString(\"D\")!);\n }\n\n public int Count => _internList.Count;\n \n public bool IsReadOnly => _internList.IsReadOnly;\n \n public int IndexOf(System.Guid? item)\n {\n return _internList.IndexOf(item?.ToString(\"D\")!);\n }\n\n public void Insert(int index, System.Guid? item)\n {\n _internList.Insert(index, item?.ToString(\"D\")!);\n }\n\n public void RemoveAt(int index)\n {\n _internList.RemoveAt(index);\n }\n\n public System.Guid? this[int index]\n {\n get => string.IsNullOrWhiteSpace(_internList[index])?null : System.Guid.Parse(_internList[index]);\n set => _internList[index] = value?.ToString(\"D\")!;\n }\n}";
9-
//// public static string RepeatedFieldNullableStringWrapper = "class RepeatedFieldNullableStringWrapper : System.Collections.Generic.IList<string?>\n{\n private readonly Google.Protobuf.Collections.RepeatedField<string> _internList;\n\n public RepeatedFieldNullableStringWrapper(Google.Protobuf.Collections.RepeatedField<string> internList)\n {\n this._internList = internList;\n }\n\n public System.Collections.Generic.IEnumerator<string?> GetEnumerator()\n {\n using var enumerator = _internList.GetEnumerator();\n while (enumerator.MoveNext())\n {\n yield return enumerator.Current;\n }\n }\n\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()\n {\n return GetEnumerator();\n }\n\n public void Add(string? item)\n {\n _internList.Add(item!);\n }\n\n public void Clear()\n {\n _internList.Clear();\n }\n\n public bool Contains(string? item)\n {\n return _internList.Contains(item!);\n }\n\n public void CopyTo(string?[] array, int arrayIndex)\n {\n System.Linq.Enumerable.ToArray(_internList).CopyTo(array, arrayIndex);\n }\n\n public bool Remove(string? item)\n {\n return _internList.Remove(item!);\n }\n\n public int Count => _internList.Count;\n \n public bool IsReadOnly => _internList.IsReadOnly;\n \n public int IndexOf(string? item)\n {\n return _internList.IndexOf(item!);\n }\n\n public void Insert(int index, string? item)\n {\n _internList.Insert(index, item!);\n }\n\n public void RemoveAt(int index)\n {\n _internList.RemoveAt(index);\n }\n\n public string? this[int index]\n {\n get => _internList[index];\n set => _internList[index] = value!;\n }\n}";
5+
public static string RepeatedFieldGuidWrapper =
6+
"""
7+
class RepeatedFieldGuidWrapper : System.Collections.Generic.IList<System.Guid>
8+
{
9+
private readonly Google.Protobuf.Collections.RepeatedField<string> _internList;
10+
11+
public RepeatedFieldGuidWrapper(Google.Protobuf.Collections.RepeatedField<string> internList)
12+
{
13+
this._internList = internList;
14+
}
15+
16+
public System.Collections.Generic.IEnumerator<System.Guid> GetEnumerator()
17+
{
18+
using var enumerator = _internList.GetEnumerator();
19+
while (enumerator.MoveNext())
20+
{
21+
yield return System.Guid.Parse(enumerator.Current);
22+
}
23+
}
24+
25+
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
26+
{
27+
return GetEnumerator();
28+
}
29+
30+
public void Add(System.Guid item)
31+
{
32+
_internList.Add(item.ToString("D"));
33+
}
34+
35+
public void Clear()
36+
{
37+
_internList.Clear();
38+
}
39+
40+
public bool Contains(System.Guid item)
41+
{
42+
return _internList.Contains(item.ToString("D"));
43+
}
44+
45+
public void CopyTo(System.Guid[] array, int arrayIndex)
46+
{
47+
System.Linq.Enumerable.ToArray(System.Linq.Enumerable.Select(_internList, System.Guid.Parse)).CopyTo(array, arrayIndex);
48+
}
49+
50+
public bool Remove(System.Guid item)
51+
{
52+
return _internList.Remove(item.ToString("D"));
53+
}
54+
55+
public int Count => _internList.Count;
56+
57+
public bool IsReadOnly => _internList.IsReadOnly;
58+
59+
public int IndexOf(System.Guid item)
60+
{
61+
return _internList.IndexOf(item.ToString("D"));
62+
}
63+
64+
public void Insert(int index, System.Guid item)
65+
{
66+
_internList.Insert(index, item.ToString("D"));
67+
}
68+
69+
public void RemoveAt(int index)
70+
{
71+
_internList.RemoveAt(index);
72+
}
73+
74+
public System.Guid this[int index]
75+
{
76+
get => System.Guid.Parse(_internList[index]);
77+
set => _internList[index] = value.ToString("D");
78+
}
79+
}
80+
""";
1081
}

Source/Porticle.Grpc.GuidMapper/MethodVisitor.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,11 @@ public MethodVisitor(HashSet<PropertyToField> replaceProps)
2323
"MergeFrom",
2424
"InternalMergeFrom"
2525
];
26-
26+
2727
public override SyntaxNode? VisitMethodDeclaration(MethodDeclarationSyntax node)
2828
{
29-
if (!ReplaceMethods.Contains(node.Identifier.ValueText.Trim()))
30-
{
31-
return base.VisitMethodDeclaration(node);
32-
}
33-
29+
if (!ReplaceMethods.Contains(node.Identifier.ValueText.Trim())) return base.VisitMethodDeclaration(node);
30+
3431
var propertyToFieldRewriter = new PropertyToFieldRewriter(ReplaceProps);
3532

3633
var newBody = (BlockSyntax?)propertyToFieldRewriter.Visit(node.Body);

Source/Porticle.Grpc.GuidMapper/Porticle.Grpc.GuidMapper.csproj

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@
1515
<RepositoryType>git</RepositoryType>
1616
<PackageReleaseNotes>First test version</PackageReleaseNotes>
1717
<IncludeBuildOutput>false</IncludeBuildOutput>
18-
<DevelopmentDependency>true</DevelopmentDependency>
18+
<DevelopmentDependency>true</DevelopmentDependency>
1919
<Version>1.0.26</Version>
20-
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
20+
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
2121
<TargetFrameworks>net9.0;net8.0</TargetFrameworks>
2222
<PackageReadmeFile>readme.md</PackageReadmeFile>
2323
</PropertyGroup>
2424
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
25-
<WarningsAsErrors>NU1605</WarningsAsErrors>
26-
<NoWarn>1701;1702;NU5119</NoWarn>
25+
<WarningsAsErrors>NU1605</WarningsAsErrors>
26+
<NoWarn>1701;1702;NU5119</NoWarn>
2727
</PropertyGroup>
2828

2929
<ItemGroup>
@@ -32,13 +32,13 @@
3232
</ItemGroup>
3333

3434
<ItemGroup>
35-
<None Include="pinky32.png" Pack="True" PackagePath="/" />
35+
<None Include="pinky32.png" Pack="True" PackagePath="/"/>
3636
</ItemGroup>
3737

38-
38+
3939
<ItemGroup>
40-
<None Include="..\..\readme.md" Pack="true" PackagePath="\"/>
41-
<None Include="Porticle.Grpc.GuidMapper.targets" Pack="true" PackagePath="buildTransitive" />
42-
<None Include="$(OutputPath)**\*" Pack="true" PackagePath="tools\$(TargetFramework)\" />
43-
</ItemGroup>
40+
<None Include="..\..\readme.md" Pack="true" PackagePath="\"/>
41+
<None Include="Porticle.Grpc.GuidMapper.targets" Pack="true" PackagePath="buildTransitive"/>
42+
<None Include="$(OutputPath)**\*" Pack="true" PackagePath="tools\$(TargetFramework)\"/>
43+
</ItemGroup>
4444
</Project>
Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<Project>
2-
<Target Name="RunProtoPostProcessing" AfterTargets="Protobuf_Compile">
3-
<ItemGroup>
4-
<_FilesToPostProcess Include="$(MSBuildProjectDirectory)\%(Protobuf_Compile.OutputDir)\%(Protobuf_Compile.Filename)" />
5-
</ItemGroup>
6-
<Message Text="Proto Postprocessing" Importance="high" />
7-
<Exec Command="dotnet &quot;$(MSBuildThisFileDirectory)..\tools\$(TargetFramework)\Porticle.Grpc.GuidMapper.dll&quot; -- %(_FilesToPostProcess.Identity)" Condition="'@(_FilesToPostProcess)' != ''" />
8-
</Target>
2+
<Target Name="RunProtoPostProcessing" AfterTargets="Protobuf_Compile">
3+
<ItemGroup>
4+
<_FilesToPostProcess Include="$(MSBuildProjectDirectory)\%(Protobuf_Compile.OutputDir)\%(Protobuf_Compile.Filename)"/>
5+
</ItemGroup>
6+
<Message Text="Proto Postprocessing" Importance="high"/>
7+
<Exec Command="dotnet &quot;$(MSBuildThisFileDirectory)..\tools\$(TargetFramework)\Porticle.Grpc.GuidMapper.dll&quot; -- %(_FilesToPostProcess.Identity)" Condition="'@(_FilesToPostProcess)' != ''"/>
8+
</Target>
99
</Project>

Source/Porticle.Grpc.GuidMapper/Program.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@
88
if (args.Length != 1)
99
{
1010
Console.WriteLine("Error: Expected exactly 1 arg but got " + args.Length);
11-
foreach (var arg in args)
12-
{
13-
Console.WriteLine("Error: arg[..] '"+arg+"'");
14-
}
11+
foreach (var arg in args) Console.WriteLine("Error: arg[..] '" + arg + "'");
1512
return;
1613
}
1714

@@ -26,7 +23,7 @@
2623
var directory = Path.GetDirectoryName(args[0])!;
2724
string[] filenames = [StringUtils.LowerUnderscoreToUpperCamelProtocWay(basename) + ".cs", StringUtils.LowerUnderscoreToUpperCamelGrpcWay(basename) + "Grpc.cs"];
2825

29-
Console.WriteLine($"GRPC Post-processing for: {string.Join(", ",filenames)}");
26+
Console.WriteLine($"GRPC Post-processing for: {string.Join(", ", filenames)}");
3027

3128
foreach (var filename in filenames)
3229
{
@@ -42,4 +39,4 @@
4239

4340
File.WriteAllText(filePath, root.ToFullString());
4441
Console.WriteLine("Post-processing complete.");
45-
}
42+
}
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
namespace Porticle.Grpc.GuidMapper;
1+
using Microsoft.CodeAnalysis.CSharp.Syntax;
2+
3+
namespace Porticle.Grpc.GuidMapper;
24

35
/// <summary>
46
/// Represents a mapping between a property name and its corresponding field name.
57
/// </summary>
6-
public record PropertyToField(string PropertyName, string FieldName);
8+
public record PropertyToField(string PropertyName, string FieldName);
9+
10+
public record ClearFunction(string FunctionName, ExpressionStatementSyntax Expression);

Source/Porticle.Grpc.GuidMapper/PropertyToFieldRewriter.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,7 @@ public PropertyToFieldRewriter(HashSet<PropertyToField> replaceNames)
2121
{
2222
var mapping = ReplaceNames.SingleOrDefault(field => field.PropertyName == node.Identifier.Text);
2323

24-
if (mapping != null)
25-
{
26-
return SyntaxFactory.IdentifierName(mapping.FieldName).WithTriviaFrom(node);
27-
}
24+
if (mapping != null) return SyntaxFactory.IdentifierName(mapping.FieldName).WithTriviaFrom(node);
2825

2926
return base.VisitIdentifierName(node);
3027
}

0 commit comments

Comments
 (0)