Skip to content

Commit 6d36325

Browse files
authored
Fixes #74 (#77)
* Fixes #74 * Fixes whitespace * Adds regression test * TestHelper can handle multiple IDs * Removes unused namespace
1 parent 0e42031 commit 6d36325

File tree

5 files changed

+261
-10
lines changed

5 files changed

+261
-10
lines changed

src/StronglyTypedIds/SourceGenerationHelper.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,5 +196,21 @@ private static void ReplaceInterfaces(StringBuilder sb, bool useIEquatable, bool
196196
sb.Replace(": INTERFACES", string.Empty);
197197
}
198198
}
199+
200+
internal static string CreateSourceName(string nameSpace, ParentClass? parent, string name)
201+
{
202+
var sb = new StringBuilder(nameSpace).Append('.');
203+
while (parent != null)
204+
{
205+
var s = parent.Name
206+
.Replace(" ", "")
207+
.Replace(",", "")
208+
.Replace("<", "__")
209+
.Replace(">", "");
210+
sb.Append(s).Append('.');
211+
parent = parent.Child;
212+
}
213+
return sb.Append(name).Append(".g.cs").ToString();
214+
}
199215
}
200-
}
216+
}

src/StronglyTypedIds/StronglyTypedIdGenerator.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,11 @@ static void Execute(
7777
values.BackingType,
7878
values.Implementations,
7979
sb);
80-
context.AddSource(idToGenerate.Name + ".g.cs", SourceText.From(result, Encoding.UTF8));
80+
var fileName = SourceGenerationHelper.CreateSourceName(
81+
idToGenerate.NameSpace,
82+
idToGenerate.Parent,
83+
idToGenerate.Name);
84+
context.AddSource(fileName, SourceText.From(result, Encoding.UTF8));
8185
}
8286
}
8387
}
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by the StronglyTypedId source generator
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
#pragma warning disable 1591 // publicly visible type or member must be documented
11+
12+
namespace MyContracts.V1
13+
{
14+
[Newtonsoft.Json.JsonConverter(typeof(MyIdNewtonsoftJsonConverter))]
15+
[System.ComponentModel.TypeConverter(typeof(MyIdTypeConverter))]
16+
readonly partial struct MyId : System.IComparable<MyId>, System.IEquatable<MyId>
17+
{
18+
public System.Guid Value { get; }
19+
20+
public MyId(System.Guid value)
21+
{
22+
Value = value;
23+
}
24+
25+
public static MyId New() => new MyId(System.Guid.NewGuid());
26+
public static readonly MyId Empty = new MyId(System.Guid.Empty);
27+
28+
public bool Equals(MyId other) => this.Value.Equals(other.Value);
29+
public override bool Equals(object obj)
30+
{
31+
if (ReferenceEquals(null, obj)) return false;
32+
return obj is MyId other && Equals(other);
33+
}
34+
35+
public override int GetHashCode() => Value.GetHashCode();
36+
37+
public override string ToString() => Value.ToString();
38+
public static bool operator ==(MyId a, MyId b) => a.Equals(b);
39+
public static bool operator !=(MyId a, MyId b) => !(a == b);
40+
public int CompareTo(MyId other) => Value.CompareTo(other.Value);
41+
42+
class MyIdTypeConverter : System.ComponentModel.TypeConverter
43+
{
44+
public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
45+
{
46+
return sourceType == typeof(System.Guid) || sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
47+
}
48+
49+
public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
50+
{
51+
return value switch
52+
{
53+
System.Guid guidValue => new MyId(guidValue),
54+
string stringValue when !string.IsNullOrEmpty(stringValue) && System.Guid.TryParse(stringValue, out var result) => new MyId(result),
55+
_ => base.ConvertFrom(context, culture, value),
56+
};
57+
}
58+
59+
public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
60+
{
61+
return sourceType == typeof(System.Guid) || sourceType == typeof(string) || base.CanConvertTo(context, sourceType);
62+
}
63+
64+
public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType)
65+
{
66+
if (value is MyId idValue)
67+
{
68+
if (destinationType == typeof(System.Guid))
69+
{
70+
return idValue.Value;
71+
}
72+
73+
if (destinationType == typeof(string))
74+
{
75+
return idValue.Value.ToString();
76+
}
77+
}
78+
79+
return base.ConvertTo(context, culture, value, destinationType);
80+
}
81+
}
82+
83+
class MyIdNewtonsoftJsonConverter : Newtonsoft.Json.JsonConverter
84+
{
85+
public override bool CanConvert(System.Type objectType)
86+
{
87+
return objectType == typeof(MyId);
88+
}
89+
90+
public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
91+
{
92+
var id = (MyId)value;
93+
serializer.Serialize(writer, id.Value);
94+
}
95+
96+
public override object ReadJson(Newtonsoft.Json.JsonReader reader, System.Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
97+
{
98+
var guid = serializer.Deserialize<System.Guid?>(reader);
99+
return guid.HasValue ? new MyId(guid.Value) : null;
100+
}
101+
}
102+
}
103+
}
104+
105+
//------------------------------------------------------------------------------
106+
// <auto-generated>
107+
// This code was generated by the StronglyTypedId source generator
108+
//
109+
// Changes to this file may cause incorrect behavior and will be lost if
110+
// the code is regenerated.
111+
// </auto-generated>
112+
//------------------------------------------------------------------------------
113+
114+
#pragma warning disable 1591 // publicly visible type or member must be documented
115+
116+
namespace MyContracts.V2
117+
{
118+
[Newtonsoft.Json.JsonConverter(typeof(MyIdNewtonsoftJsonConverter))]
119+
[System.ComponentModel.TypeConverter(typeof(MyIdTypeConverter))]
120+
readonly partial struct MyId : System.IComparable<MyId>, System.IEquatable<MyId>
121+
{
122+
public System.Guid Value { get; }
123+
124+
public MyId(System.Guid value)
125+
{
126+
Value = value;
127+
}
128+
129+
public static MyId New() => new MyId(System.Guid.NewGuid());
130+
public static readonly MyId Empty = new MyId(System.Guid.Empty);
131+
132+
public bool Equals(MyId other) => this.Value.Equals(other.Value);
133+
public override bool Equals(object obj)
134+
{
135+
if (ReferenceEquals(null, obj)) return false;
136+
return obj is MyId other && Equals(other);
137+
}
138+
139+
public override int GetHashCode() => Value.GetHashCode();
140+
141+
public override string ToString() => Value.ToString();
142+
public static bool operator ==(MyId a, MyId b) => a.Equals(b);
143+
public static bool operator !=(MyId a, MyId b) => !(a == b);
144+
public int CompareTo(MyId other) => Value.CompareTo(other.Value);
145+
146+
class MyIdTypeConverter : System.ComponentModel.TypeConverter
147+
{
148+
public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
149+
{
150+
return sourceType == typeof(System.Guid) || sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
151+
}
152+
153+
public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
154+
{
155+
return value switch
156+
{
157+
System.Guid guidValue => new MyId(guidValue),
158+
string stringValue when !string.IsNullOrEmpty(stringValue) && System.Guid.TryParse(stringValue, out var result) => new MyId(result),
159+
_ => base.ConvertFrom(context, culture, value),
160+
};
161+
}
162+
163+
public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
164+
{
165+
return sourceType == typeof(System.Guid) || sourceType == typeof(string) || base.CanConvertTo(context, sourceType);
166+
}
167+
168+
public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType)
169+
{
170+
if (value is MyId idValue)
171+
{
172+
if (destinationType == typeof(System.Guid))
173+
{
174+
return idValue.Value;
175+
}
176+
177+
if (destinationType == typeof(string))
178+
{
179+
return idValue.Value.ToString();
180+
}
181+
}
182+
183+
return base.ConvertTo(context, culture, value, destinationType);
184+
}
185+
}
186+
187+
class MyIdNewtonsoftJsonConverter : Newtonsoft.Json.JsonConverter
188+
{
189+
public override bool CanConvert(System.Type objectType)
190+
{
191+
return objectType == typeof(MyId);
192+
}
193+
194+
public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
195+
{
196+
var id = (MyId)value;
197+
serializer.Serialize(writer, id.Value);
198+
}
199+
200+
public override object ReadJson(Newtonsoft.Json.JsonReader reader, System.Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
201+
{
202+
var guid = serializer.Deserialize<System.Guid?>(reader);
203+
return guid.HasValue ? new MyId(guid.Value) : null;
204+
}
205+
}
206+
}
207+
}

test/StronglyTypedIds.Tests/StronglyTypedIdGeneratorTests.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,33 @@ public partial struct MyId {}
182182
.UseDirectory("Snapshots");
183183
}
184184

185+
[Fact]
186+
public Task CanGenerateMultipleIdsWithSameName()
187+
{
188+
// https://github.com/andrewlock/StronglyTypedId/issues/74
189+
const string input = @"using StronglyTypedIds;
190+
191+
namespace MyContracts.V1
192+
{
193+
[StronglyTypedId]
194+
public partial struct MyId {}
195+
}
196+
197+
namespace MyContracts.V2
198+
{
199+
[StronglyTypedId]
200+
public partial struct MyId {}
201+
}";
202+
203+
// This only includes the last ID but that's good enough for this
204+
var (diagnostics, output) = TestHelpers.GetGeneratedOutput<StronglyTypedIdGenerator>(input);
205+
206+
Assert.Empty(diagnostics);
207+
208+
return Verifier.Verify(output)
209+
.UseDirectory("Snapshots");
210+
}
211+
185212
public static TheoryData<StronglyTypedIdBackingType, StronglyTypedIdConverter?> GetData => new()
186213
{
187214
{StronglyTypedIdBackingType.Guid, null},

test/StronglyTypedIds.Tests/TestHelpers.cs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,12 @@ public static (ImmutableArray<Diagnostic> Diagnostics, string Output) GetGenerat
2424
references,
2525
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
2626

27-
var originalTreeCount = compilation.SyntaxTrees.Length;
28-
var generator = new T();
27+
CSharpGeneratorDriver
28+
.Create(new T())
29+
.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out var diagnostics);
30+
var output = string.Join("\n", outputCompilation.SyntaxTrees.Skip(6).Select(t => t.ToString()));
2931

30-
var driver = CSharpGeneratorDriver.Create(generator);
31-
driver.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out var diagnostics);
32-
33-
var trees = outputCompilation.SyntaxTrees.ToList();
34-
35-
return (diagnostics, trees.Count != originalTreeCount ? trees[^1].ToString() : string.Empty);
32+
return (diagnostics, output);
3633
}
3734

3835
internal static string LoadEmbeddedResource(string resourceName)

0 commit comments

Comments
 (0)