Skip to content

Commit cb9880b

Browse files
author
machibuse
committed
Rename and restructure solution to introduce TypeMapper and UnitTests
1 parent e4e171e commit cb9880b

34 files changed

Lines changed: 1192 additions & 271 deletions

.github/workflows/release.yaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,11 @@ jobs:
3030
- name: Build project
3131
run: dotnet build Source/Porticle.Grpc.sln --configuration Release --no-restore
3232

33+
- name: Run tests
34+
run: dotnet test Source/Porticle.Grpc.UnitTests/Porticle.Grpc.UnitTests.csproj --configuration Release --no-build --verbosity normal
35+
3336
- name: Pack NuGet package
34-
run: dotnet pack Source/Porticle.Grpc.GuidMapper/Porticle.Grpc.GuidMapper.csproj --configuration Release --no-build --output ./nuget --property:PackageVersion=${{ env.VERSION }}
37+
run: dotnet pack Source/Porticle.Grpc.TypeMapper/Porticle.Grpc.TypeMapper.csproj --configuration Release --no-build --output ./nuget --property:PackageVersion=${{ env.VERSION }}
3538

3639
- name: Publish NuGet package
3740
run: dotnet nuget push ./nuget/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
@@ -41,7 +44,7 @@ jobs:
4144
with:
4245
tag_name: v${{ env.VERSION }}
4346
name: Release v${{ env.VERSION }}
44-
body: "Nuget Release für Porticle.Grpc.GuidMapper v${{ env.VERSION }}."
47+
body: "Nuget Release für Porticle.Grpc.TypeMapper v${{ env.VERSION }}."
4548
files: ./nuget/*.nupkg
4649
draft: false
4750
prerelease: ${{ contains(env.VERSION, '-beta') || contains(env.VERSION, '-alpha') || contains(env.VERSION, '-rc') }}

README.md

Lines changed: 247 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,248 @@
1-
# A Roslyn PostProcessor for protoc generated files to add Guid support
1+
# Porticle.Grpc.TypeMapper
22

3-
This library allows you to map string and StringValue to Guid and Guid?.
3+
A Roslyn-based post-processor for protoc-generated files that adds automatic mappings for `Guid`, `Guid?`, `string?` or nullable enums. By simply adding this package and adding
4+
comments to
5+
your `.proto` file.
6+
7+
## Build State
8+
9+
[![Build and Release](https://github.com/Machibuse/Porticle.Grpc.TypeMapper/actions/workflows/release.yaml/badge.svg)](https://github.com/Machibuse/Porticle.CLDR/actions/workflows/release.yaml)
10+
11+
## Nuget
12+
13+
[![NuGet Latest Version](https://img.shields.io/nuget/v/Porticle.Grpc.TypeMapper.svg)](https://www.nuget.org/packages/Porticle.Grpc.TypeMapper/) [![NuGet Downloads](https://img.shields.io/nuget/dt/Porticle.Grpc.TypeMapper.svg)](https://www.nuget.org/packages/Porticle.CLDR.Units/)
14+
15+
## Overview
16+
17+
This library adds automatic conversion for:
18+
19+
- Protobuf string to C# Guid
20+
- Protobuf google.Protobuf.StringValue to C# Guid?
21+
- Protobuf google.Protobuf.StringValue to C# string?
22+
- Protobuf optional enum to C# nullable enum
23+
24+
This Library adds a Roslyn Postprocessing zu the c# files generated by the protoc compiler.
25+
Enabling seamless integration of Guid, Guid?, string? And nullable Enums in your gRPC services without manual conversion.
26+
Code.
27+
28+
## Installation
29+
30+
### Install the package via NuGet:
31+
32+
```powershell
33+
dotnet add package Porticle.Grpc.TypeMapper
34+
```
35+
36+
After installing the Package, this Post build step ist dynamically added to your build.
37+
38+
```msbuild
39+
40+
<Project>
41+
<Target Name="RunProtoPostProcessing" AfterTargets="Protobuf_Compile">
42+
<ItemGroup>
43+
<_FilesToPostProcess Include="$(MSBuildProjectDirectory)\%(Protobuf_Compile.OutputDir)\%(Protobuf_Compile.Filename)"/>
44+
</ItemGroup>
45+
<Message Text="Proto Postprocessing" Importance="high"/>
46+
<Exec Command="dotnet &quot;$(MSBuildThisFileDirectory)..\tools\$(TargetFramework)\Porticle.Grpc.TypeMapper.dll&quot; -- %(_FilesToPostProcess.Identity)" Condition="'@(_FilesToPostProcess)' != ''"/>
47+
</Target>
48+
</Project>
49+
```
50+
51+
Don't wonder ist you cant se it in your csproj file. It is dynamically added when your build is processed.
52+
53+
## Usage
54+
55+
There are three things you can do in your .proto files:
56+
57+
- Add `// [GrpcGuid]` as comment to a string field - Converts the corresponding c# string property to Guid
58+
- Add `// [GrpcGuid]` as comment to a StringValue field - Converts the corresponding c# string property to Guid?
59+
- Add `// [NullableString]` as comment to a StringValue field - Converts the corresponding c# string property to string?
60+
- Add `// [NullableEnum]` as comment to a optional enum field - Converts the corresponding optional proto enum to a C# nullable Enum
61+
62+
First an Example of a default .proto file
63+
64+
### Without TypeMapper
65+
66+
```protobuf
67+
syntax = "proto3";
68+
69+
import "google/protobuf/wrappers.proto";
70+
71+
enum TestEnum {
72+
FOO = 0;
73+
BAR = 1;
74+
}
75+
76+
message User {
77+
// Guid of the user object
78+
string id = 1;
79+
80+
// Optional parent UserId
81+
google.protobuf.StringValue optional_parent_user_id = 2;
82+
83+
// Optional description
84+
google.protobuf.StringValue description = 3;
85+
86+
// List of roles
87+
repeated string role_ids = 4;
88+
89+
// Simple Enum
90+
optional TestEnum foo_bar = 5;
91+
}
92+
```
93+
94+
Will result in protoc generated code like this, everything is a string or a simple enum
95+
96+
```csharp
97+
/// <summary>Guid of the user object</summary>
98+
public string Id {
99+
get { return id_; }
100+
set { id_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); }
101+
}
102+
103+
/// <summary>Optional Guid of the parent UserId</summary>
104+
public string OptionalParentUserId {
105+
get { return optionalParentUserId_; }
106+
set { optionalParentUserId_ = value; }
107+
}
108+
109+
/// <summary>Optional description string</summary>
110+
public string Description {
111+
get { return description_; }
112+
set { description_ = value; }
113+
}
114+
115+
/// <summary>List of roles</summary>
116+
public pbc::RepeatedField<string> RoleIds {
117+
get { return roleIds_; }
118+
}
119+
120+
public global::Porticle.Grpc.UnitTests.TestEnum FooBar {
121+
get {
122+
if ((_hasBits0 & 1) != 0)
123+
return fooBar_;
124+
else
125+
return FooBarDefaultValue;
126+
}
127+
set {
128+
_hasBits0 |= 1;
129+
fooBar_ = value;
130+
}
131+
}
132+
```
133+
134+
### Now a sample with TypeMapper enabled
135+
136+
```protobuf
137+
syntax = "proto3";
138+
139+
import "google/protobuf/wrappers.proto";
140+
141+
enum TestEnum {
142+
FOO = 0;
143+
BAR = 1;
144+
}
145+
146+
message User {
147+
// [GrpcGuid] Guid of the user object
148+
string id = 1;
149+
150+
// [GrpcGuid] Optional Guid of the parent UserId
151+
google.protobuf.StringValue optional_parent_user_id = 2;
152+
153+
// [NullableString] Optional description string
154+
google.protobuf.StringValue description = 3;
155+
156+
// [GrpcGuid] List of roles
157+
repeated string role_ids = 4;
158+
159+
// [NullableEnum] Simple Enum
160+
optional TestEnum foo_bar = 5;
161+
}
162+
```
163+
164+
Will result in generated code like this, using string? Guid and Guid? and a nullable Enum
165+
166+
```csharp
167+
/// <summary>[GrpcGuid] Guid of the user object</summary>
168+
public global::System.Guid Id {
169+
get
170+
{
171+
// Parse the internal string as Guid and return it
172+
return global::System.Guid.Parse(id_);
173+
}
174+
set
175+
{
176+
// Set the internal string fron the given guid
177+
id_ = (value).ToString("D");
178+
}
179+
}
180+
181+
/// <summary>[GrpcGuid] Optional Guid of the parent UserId</summary>
182+
public global::System.Guid? OptionalParentUserId {
183+
get {
184+
// return null wehn corresponding string is null
185+
if(optionalParentUserId_==null) return default;
186+
// return a Guid instead of the string
187+
return global::System.Guid.Parse(optionalParentUserId_);
188+
}
189+
set
190+
{
191+
// sets the internal string from the given guid
192+
optionalParentUserId_ = (value)?.ToString("D");
193+
}
194+
}
195+
196+
197+
// enable nullable for this property and return string? instead of string
198+
#nullable enable
199+
/// <summary>[NullableString] Optional description string</summary>
200+
public string? Description {
201+
get { return description_; }
202+
set { description_ = value; }
203+
}
204+
#nullable disable
205+
206+
/// <summary>[GrpcGuid] List of roles</summary>
207+
public IList<Guid> RoleIds {
208+
get
209+
{
210+
// returns a wrapper that converts a list of strings to a list of guids
211+
return new RepeatedFieldGuidWrapper(roleIds_);
212+
}
213+
}
214+
215+
public global::Porticle.Grpc.UnitTests.TestEnum? FooBar {
216+
get {
217+
if ((_hasBits0 & 1) != 0)
218+
{
219+
return fooBar_;
220+
}
221+
else
222+
{
223+
// return null instead of default value when Has-Flag is false
224+
return null;
225+
}
226+
}
227+
set {
228+
if(value==null) {
229+
// Set hasflag to false when null is assigend
230+
_hasBits0 &=~1;
231+
fooBar_ = FooBarDefaultValue;
232+
}
233+
else
234+
{
235+
_hasBits0 |= 1;
236+
fooBar_ = value.Value;
237+
}
238+
}
239+
}
240+
```
241+
242+
## What is currently not Possible?
243+
244+
- Mapping `repeated google.protobuf.StringValue` to `List<Guid?>` or `List<string?>` because grpc internally uses `RepeatedField<string>` instead of `RepeatedField<StringValue>`.
245+
This may be a bug in protoc compiler, because it is also not possible to add `null` to `repeated google.protobuf.StringValue` because there is a not null check in the Add
246+
function in `RepeatedField<T>`
247+
248+
- This Tool actually don't works when protoc / Grpc.Tools is compiled with GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE

Source/.idea/.idea.Porticle.Grpc/.idea/indexLayout.xml

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Source/.idea/.idea.Porticle.Grpc/.idea/vcs.xml

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Source/ConsoleApp1/.idea/.idea.ConsoleApp1/.idea/.gitignore

Lines changed: 0 additions & 13 deletions
This file was deleted.

Source/ConsoleApp1/.idea/.idea.ConsoleApp1/.idea/AndroidProjectSystem.xml

Lines changed: 0 additions & 6 deletions
This file was deleted.

Source/ConsoleApp1/.idea/.idea.ConsoleApp1/.idea/encodings.xml

Lines changed: 0 additions & 4 deletions
This file was deleted.

Source/ConsoleApp1/.idea/.idea.ConsoleApp1/.idea/indexLayout.xml

Lines changed: 0 additions & 8 deletions
This file was deleted.

Source/ConsoleApp1/.idea/.idea.ConsoleApp1/.idea/vcs.xml

Lines changed: 0 additions & 6 deletions
This file was deleted.

Source/ConsoleApp1/ConsoleApp1.sln

Lines changed: 0 additions & 16 deletions
This file was deleted.

0 commit comments

Comments
 (0)