Skip to content

Commit 8c93a92

Browse files
committed
Add legacy TypeConverter support from System.ComponentModel
This shouldn't be needed in newer ASP.NET Core since the IParsable<T>/IFormattable is the only requirement nowadays. But might be useful in other scenarios.
1 parent 11b53d2 commit 8c93a92

4 files changed

Lines changed: 146 additions & 0 deletions

File tree

src/StructId.FunctionalTests/Functional.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@ public record User(UserId Id, string Name, Wallet Wallet);
2424

2525
public class FunctionalTests(ITestOutputHelper output)
2626
{
27+
[Fact]
28+
public void TypeConverters()
29+
{
30+
var id = ProductId.New();
31+
var converter = TypeDescriptor.GetConverter(id);
32+
33+
Assert.True(converter.CanConvertTo(typeof(string)));
34+
Assert.True(converter.CanConvertFrom(typeof(string)));
35+
36+
var id2 = (ProductId?)converter.ConvertFromString(converter.ConvertToString(id)!);
37+
Assert.Equal(id, id2);
38+
}
39+
2740
[Fact]
2841
public void JsonConversion()
2942
{

src/StructId.FunctionalTests/UlidTests.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections.Concurrent;
2+
using System.ComponentModel;
23
using System.Data;
34
using System.Text.Json;
45
using Dapper;
@@ -69,6 +70,19 @@ public UlidToStringConverter(ConverterMappingHints? mappingHints = null)
6970

7071
public class UlidTests
7172
{
73+
[Fact]
74+
public void TypeConverters()
75+
{
76+
var id = UlidId.New();
77+
var converter = TypeDescriptor.GetConverter(id);
78+
79+
Assert.True(converter.CanConvertTo(typeof(string)));
80+
Assert.True(converter.CanConvertFrom(typeof(string)));
81+
82+
var id2 = (UlidId?)converter.ConvertFromString(converter.ConvertToString(id)!);
83+
Assert.Equal(id, id2);
84+
}
85+
7286
[Fact]
7387
public void JsonConversion()
7488
{
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// <auto-generated/>
2+
#nullable enable
3+
4+
using System;
5+
using System.ComponentModel;
6+
using System.Globalization;
7+
using StructId;
8+
9+
[TStructId]
10+
[TypeConverter(typeof(TSelf.StringConverter))]
11+
file readonly partial record struct TSelf(string Value)
12+
{
13+
partial class StringConverter : TypeConverter
14+
{
15+
/// <inheritdoc />
16+
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
17+
=> sourceType == typeof(string) || sourceType == typeof(TSelf);
18+
19+
/// <inheritdoc />
20+
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
21+
{
22+
if (value == null)
23+
return default(TSelf);
24+
25+
if (value is string typedValue)
26+
return TSelf.New(typedValue);
27+
28+
throw new ArgumentException($"Cannot convert '{value}' to {nameof(TSelf)}", nameof(value));
29+
}
30+
31+
/// <inheritdoc />
32+
public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType)
33+
=> destinationType == typeof(string) || destinationType == typeof(TSelf);
34+
35+
/// <inheritdoc />
36+
public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
37+
{
38+
if (value != null)
39+
{
40+
if (destinationType == typeof(string))
41+
return ((TSelf)value).Value;
42+
43+
if (destinationType == typeof(TSelf))
44+
return value;
45+
}
46+
47+
throw new InvalidOperationException($"Cannot convert '{value}' to '{destinationType}'");
48+
}
49+
}
50+
}
51+
52+
file partial record struct TSelf : INewable<TSelf, string>
53+
{
54+
public static TSelf New(string value) => throw new NotImplementedException();
55+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// <auto-generated/>
2+
#nullable enable
3+
4+
using System;
5+
using System.ComponentModel;
6+
using System.Diagnostics.CodeAnalysis;
7+
using System.Globalization;
8+
using StructId;
9+
10+
[TStructId]
11+
[TypeConverter(typeof(TSelf.StringConverter))]
12+
file readonly partial record struct TSelf(/*!string*/ TValue Value)
13+
{
14+
partial class StringConverter : TypeConverter
15+
{
16+
/// <inheritdoc />
17+
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
18+
=> sourceType == typeof(string) || sourceType == typeof(TSelf);
19+
20+
/// <inheritdoc />
21+
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
22+
{
23+
if (value == null)
24+
return default(TSelf);
25+
26+
if (value is string typedValue)
27+
return TSelf.New(TValue.Parse(typedValue, culture));
28+
29+
throw new ArgumentException($"Cannot convert '{value}' to {nameof(TSelf)}", nameof(value));
30+
}
31+
32+
/// <inheritdoc />
33+
public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType)
34+
=> destinationType == typeof(string) || destinationType == typeof(TSelf);
35+
36+
/// <inheritdoc />
37+
public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
38+
{
39+
if (value != null)
40+
{
41+
if (destinationType == typeof(string))
42+
return ((TSelf)value).Value.ToString(null, culture);
43+
44+
if (destinationType == typeof(TSelf))
45+
return value;
46+
}
47+
48+
throw new InvalidOperationException($"Cannot convert '{value}' to '{destinationType}'");
49+
}
50+
}
51+
}
52+
53+
file partial record struct TSelf : INewable<TSelf, TValue>
54+
{
55+
public static TSelf New(TValue value) => throw new NotImplementedException();
56+
}
57+
58+
// This will be removed when applying the template to each user-defined struct id.
59+
file struct TValue : IParsable<TValue>, IFormattable
60+
{
61+
public static TValue Parse(string s, IFormatProvider? provider) => throw new NotImplementedException();
62+
public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out TValue result) => throw new NotImplementedException();
63+
public string ToString(string? format, IFormatProvider? formatProvider) => throw new NotImplementedException();
64+
}

0 commit comments

Comments
 (0)