Skip to content

Commit 23d04a8

Browse files
committed
Fix converter generator
1 parent b8c48f8 commit 23d04a8

File tree

3 files changed

+287
-23
lines changed

3 files changed

+287
-23
lines changed

src/StronglyTypedIds/Parser.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ internal static class Parser
1212
{
1313
public const string StronglyTypedIdAttribute = "StronglyTypedIds.StronglyTypedIdAttribute";
1414
public const string StronglyTypedIdDefaultsAttribute = "StronglyTypedIds.StronglyTypedIdDefaultsAttribute";
15-
public const string StronglyTypedIdConvertersAttribute = "StronglyTypedIds.StronglyTypedIdConvertersAttribute";
15+
public const string StronglyTypedIdConvertersAttribute = "StronglyTypedIds.StronglyTypedIdConvertersAttribute`1";
1616
public const string StronglyTypedIdConvertersDefaultsAttribute = "StronglyTypedIds.StronglyTypedIdConvertersDefaultsAttribute";
1717

1818
public static Result<(StructToGenerate info, bool valid)> GetIdSemanticTarget(GeneratorAttributeSyntaxContext ctx, CancellationToken ct)
@@ -149,7 +149,6 @@ internal static class Parser
149149
}
150150

151151
var defaults = new Defaults(template, templateNames, attributeLocation!, hasMultiple);
152-
153152
return new Result<(Defaults, bool)>((defaults, true), errors);
154153
}
155154

@@ -171,9 +170,10 @@ internal static class Parser
171170

172171
foreach (AttributeData attribute in structSymbol.GetAttributes())
173172
{
174-
if (!((attribute.AttributeClass?.Name == "StronglyTypedIdAttribute" ||
175-
attribute.AttributeClass?.Name == "StronglyTypedId") &&
176-
attribute.AttributeClass.ToDisplayString() == StronglyTypedIdAttribute))
173+
if (!((attribute.AttributeClass?.Name == "StronglyTypedIdConvertersAttribute" ||
174+
attribute.AttributeClass?.Name == "StronglyTypedIdConverters") &&
175+
attribute.AttributeClass.IsGenericType &&
176+
attribute.AttributeClass.TypeArguments.Length == 1))
177177
{
178178
// wrong attribute
179179
continue;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
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+
#nullable enable
13+
[global::System.ComponentModel.TypeConverter(typeof(MyIdTypeConverter))]
14+
[global::System.Text.Json.Serialization.JsonConverter(typeof(MyIdSystemTextJsonConverter))]
15+
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("StronglyTypedId", "1.0.0-beta6")]
16+
partial struct MyId :
17+
#if NET6_0_OR_GREATER
18+
global::System.ISpanFormattable,
19+
#endif
20+
#if NET7_0_OR_GREATER
21+
global::System.IParsable<MyId>, global::System.ISpanParsable<MyId>,
22+
#endif
23+
#if NET8_0_OR_GREATER
24+
global::System.IUtf8SpanFormattable,
25+
#endif
26+
global::System.IComparable<MyId>, global::System.IEquatable<MyId>, global::System.IFormattable
27+
{
28+
public global::System.Guid Value { get; }
29+
30+
public MyId(global::System.Guid value)
31+
{
32+
Value = value;
33+
}
34+
35+
public static MyId New() => new MyId(global::System.Guid.NewGuid());
36+
public static readonly MyId Empty = new MyId(global::System.Guid.Empty);
37+
38+
/// <inheritdoc cref="global::System.IEquatable{T}"/>
39+
public bool Equals(MyId other) => this.Value.Equals(other.Value);
40+
public override bool Equals(object? obj)
41+
{
42+
if (ReferenceEquals(null, obj)) return false;
43+
return obj is MyId other && Equals(other);
44+
}
45+
46+
public override int GetHashCode() => Value.GetHashCode();
47+
48+
public override string ToString() => Value.ToString();
49+
50+
public static bool operator ==(MyId a, MyId b) => a.Equals(b);
51+
public static bool operator !=(MyId a, MyId b) => !(a == b);
52+
public static bool operator > (MyId a, MyId b) => a.CompareTo(b) > 0;
53+
public static bool operator < (MyId a, MyId b) => a.CompareTo(b) < 0;
54+
public static bool operator >= (MyId a, MyId b) => a.CompareTo(b) >= 0;
55+
public static bool operator <= (MyId a, MyId b) => a.CompareTo(b) <= 0;
56+
57+
/// <inheritdoc cref="global::System.IComparable{TSelf}"/>
58+
public int CompareTo(MyId other) => Value.CompareTo(other.Value);
59+
60+
public partial class MyIdTypeConverter : global::System.ComponentModel.TypeConverter
61+
{
62+
public override bool CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext? context, global::System.Type sourceType)
63+
{
64+
return sourceType == typeof(global::System.Guid) || sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
65+
}
66+
67+
public override object? ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext? context, global::System.Globalization.CultureInfo? culture, object value)
68+
{
69+
return value switch
70+
{
71+
global::System.Guid guidValue => new MyId(guidValue),
72+
string stringValue when !string.IsNullOrEmpty(stringValue) && global::System.Guid.TryParse(stringValue, out var result) => new MyId(result),
73+
_ => base.ConvertFrom(context, culture, value),
74+
};
75+
}
76+
77+
public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext? context, global::System.Type? sourceType)
78+
{
79+
return sourceType == typeof(global::System.Guid) || sourceType == typeof(string) || base.CanConvertTo(context, sourceType);
80+
}
81+
82+
public override object? ConvertTo(global::System.ComponentModel.ITypeDescriptorContext? context, global::System.Globalization.CultureInfo? culture, object? value, global::System.Type destinationType)
83+
{
84+
if (value is MyId idValue)
85+
{
86+
if (destinationType == typeof(global::System.Guid))
87+
{
88+
return idValue.Value;
89+
}
90+
91+
if (destinationType == typeof(string))
92+
{
93+
return idValue.Value.ToString();
94+
}
95+
}
96+
97+
return base.ConvertTo(context, culture, value, destinationType);
98+
}
99+
}
100+
101+
public partial class MyIdSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter<MyId>
102+
{
103+
public override bool CanConvert(global::System.Type typeToConvert)
104+
=> typeToConvert == typeof(global::System.Guid) || typeToConvert == typeof(string) || base.CanConvert(typeToConvert);
105+
106+
public override MyId Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
107+
=> new (reader.GetGuid());
108+
109+
public override void Write(global::System.Text.Json.Utf8JsonWriter writer, MyId value, global::System.Text.Json.JsonSerializerOptions options)
110+
=> writer.WriteStringValue(value.Value);
111+
112+
#if NET6_0_OR_GREATER
113+
public override MyId ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options)
114+
=> new(global::System.Guid.Parse(reader.GetString() ?? throw new global::System.FormatException("The string for the MyId property was null")));
115+
116+
public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, MyId value, global::System.Text.Json.JsonSerializerOptions options)
117+
=> writer.WritePropertyName(value.Value.ToString());
118+
#endif
119+
}
120+
121+
public static MyId Parse(string input)
122+
=> new(global::System.Guid.Parse(input));
123+
124+
#if NET7_0_OR_GREATER
125+
/// <inheritdoc cref="global::System.IParsable{TSelf}"/>
126+
public static MyId Parse(string input, global::System.IFormatProvider? provider)
127+
=> new(global::System.Guid.Parse(input, provider));
128+
129+
/// <inheritdoc cref="global::System.IParsable{TSelf}"/>
130+
public static bool TryParse(
131+
[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? input,
132+
global::System.IFormatProvider? provider,
133+
out MyId result)
134+
{
135+
if (input is null)
136+
{
137+
result = default;
138+
return false;
139+
}
140+
141+
if (global::System.Guid.TryParse(input, provider, out var guid))
142+
{
143+
result = new(guid);
144+
return true;
145+
}
146+
else
147+
{
148+
result = default;
149+
return false;
150+
}
151+
}
152+
#endif
153+
154+
/// <inheritdoc cref="global::System.IFormattable"/>
155+
public string ToString(
156+
#if NET7_0_OR_GREATER
157+
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.GuidFormat)]
158+
#endif
159+
string? format,
160+
global::System.IFormatProvider? formatProvider)
161+
=> Value.ToString(format, formatProvider);
162+
163+
#if NETCOREAPP2_1_OR_GREATER
164+
public static MyId Parse(global::System.ReadOnlySpan<char> input)
165+
=> new(global::System.Guid.Parse(input));
166+
#endif
167+
168+
#if NET6_0_OR_GREATER
169+
#if NET7_0_OR_GREATER
170+
/// <inheritdoc cref="global::System.ISpanParsable{TSelf}"/>
171+
#endif
172+
public static MyId Parse(global::System.ReadOnlySpan<char> input, global::System.IFormatProvider? provider)
173+
#if NET7_0_OR_GREATER
174+
=> new(global::System.Guid.Parse(input, provider));
175+
#else
176+
=> new(global::System.Guid.Parse(input));
177+
#endif
178+
179+
#if NET7_0_OR_GREATER
180+
/// <inheritdoc cref="global::System.ISpanParsable{TSelf}"/>
181+
#endif
182+
public static bool TryParse(global::System.ReadOnlySpan<char> input, global::System.IFormatProvider? provider, out MyId result)
183+
{
184+
#if NET7_0_OR_GREATER
185+
if (global::System.Guid.TryParse(input, provider, out var guid))
186+
#else
187+
if (global::System.Guid.TryParse(input, out var guid))
188+
#endif
189+
{
190+
result = new(guid);
191+
return true;
192+
}
193+
else
194+
{
195+
result = default;
196+
return false;
197+
}
198+
}
199+
200+
/// <inheritdoc cref="global::System.ISpanFormattable"/>
201+
public bool TryFormat(
202+
global::System.Span<char> destination,
203+
out int charsWritten,
204+
#if NET7_0_OR_GREATER
205+
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.GuidFormat)]
206+
#endif
207+
global::System.ReadOnlySpan<char> format,
208+
global::System.IFormatProvider? provider)
209+
=> Value.TryFormat(destination, out charsWritten, format);
210+
211+
/// <inheritdoc cref="global::System.ISpanFormattable"/>
212+
public bool TryFormat(
213+
global::System.Span<char> destination,
214+
out int charsWritten,
215+
#if NET7_0_OR_GREATER
216+
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.GuidFormat)]
217+
#endif
218+
global::System.ReadOnlySpan<char> format = default)
219+
=> Value.TryFormat(destination, out charsWritten, format);
220+
#endif
221+
#if NET8_0_OR_GREATER
222+
/// <inheritdoc cref="global::System.IUtf8SpanFormattable.TryFormat" />
223+
public bool TryFormat(
224+
global::System.Span<byte> utf8Destination,
225+
out int bytesWritten,
226+
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.GuidFormat)]
227+
global::System.ReadOnlySpan<char> format,
228+
global::System.IFormatProvider? provider)
229+
=> Value.TryFormat(utf8Destination, out bytesWritten, format);
230+
#endif
231+
}
232+
233+
//------------------------------------------------------------------------------
234+
// <auto-generated>
235+
// This code was generated by the StronglyTypedId source generator
236+
//
237+
// Changes to this file may cause incorrect behavior and will be lost if
238+
// the code is regenerated.
239+
// </auto-generated>
240+
//------------------------------------------------------------------------------
241+
242+
#pragma warning disable 1591 // publicly visible type or member must be documented
243+
244+
#nullable enable
245+
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("StronglyTypedId", "1.0.0-beta6")]
246+
partial struct MyId
247+
{
248+
public partial class DapperTypeHandler : global::Dapper.SqlMapper.TypeHandler<MyId>
249+
{
250+
public override void SetValue(global::System.Data.IDbDataParameter parameter, MyId value)
251+
{
252+
parameter.Value = value.Value;
253+
}
254+
255+
public override MyId Parse(object value)
256+
{
257+
return value switch
258+
{
259+
global::System.Guid guidValue => new MyId(guidValue),
260+
string stringValue when !string.IsNullOrEmpty(stringValue) && global::System.Guid.TryParse(stringValue, out var result) => new MyId(result),
261+
_ => throw new global::System.InvalidCastException($"Unable to cast object of type {value.GetType()} to MyId"),
262+
};
263+
}
264+
}
265+
}

test/StronglyTypedIds.Tests/StronglyTypedIdConverterTests.cs

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,30 @@ public StronglyTypedIdConverterTests(ITestOutputHelper output)
1515
_output = output;
1616
}
1717

18-
[Fact]
19-
public Task DefaultConverterIdInGlobalNamespaceWithoutDefaultsDoesntGenerateConverters()
20-
{
21-
const string input =
22-
"""
23-
using StronglyTypedIds;
18+
[Fact]
19+
public Task DefaultConverterIdInGlobalNamespaceWithoutDefaultsDoesntGenerateConverters()
20+
{
21+
const string input =
22+
"""
23+
using StronglyTypedIds;
2424
25-
[StronglyTypedId]
26-
public partial struct MyId {}
27-
28-
[StronglyTypedIdConverters<MyId>]
29-
public partial struct MyIdConverters {}
30-
""";
31-
var (diagnostics, output) = TestHelpers.GetGeneratedOutput<StronglyTypedIdGenerator>(input, includeAttributes: false);
25+
[StronglyTypedId]
26+
public partial struct MyId {}
27+
28+
[StronglyTypedIdConverters<MyId>]
29+
public partial struct MyIdConverters {}
30+
""";
31+
var (diagnostics, output) = TestHelpers.GetGeneratedOutput<StronglyTypedIdGenerator>(input, includeAttributes: false);
3232

33-
Assert.Empty(diagnostics);
33+
Assert.Empty(diagnostics);
3434

35-
return Verifier.Verify(output)
36-
.UseDirectory("Snapshots");
37-
}
35+
return Verifier.Verify(output)
36+
.UseDirectory("Snapshots");
37+
}
3838

3939
[Fact]
4040
public Task CanGenerateDefaultConverterIdInGlobalNamespace()
4141
{
42-
System.Diagnostics.Debugger.Launch();
4342
const string input =
4443
"""
4544
using StronglyTypedIds;

0 commit comments

Comments
 (0)