Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for IUtf8SpanFormattable and IUtf8SpanParsable #119

Merged
merged 1 commit into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/StronglyTypedIds.Templates/guid-full.typedid
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#endif
#if NET7_0_OR_GREATER
global::System.IParsable<PLACEHOLDERID>, global::System.ISpanParsable<PLACEHOLDERID>,
#endif
#if NET8_0_OR_GREATER
global::System.IUtf8SpanFormattable,
#endif
global::System.IComparable<PLACEHOLDERID>, global::System.IEquatable<PLACEHOLDERID>, global::System.IFormattable
{
Expand Down Expand Up @@ -203,6 +206,16 @@
global::System.ReadOnlySpan<char> format = default)
=> Value.TryFormat(destination, out charsWritten, format);
#endif
#if NET8_0_OR_GREATER
/// <inheritdoc cref="global::System.IUtf8SpanFormattable.TryFormat" />
public bool TryFormat(
global::System.Span<byte> utf8Destination,
out int bytesWritten,
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.GuidFormat)]
global::System.ReadOnlySpan<char> format,
global::System.IFormatProvider? provider)
=> Value.TryFormat(utf8Destination, out bytesWritten, format);
#endif

public partial class DapperTypeHandler : global::Dapper.SqlMapper.TypeHandler<PLACEHOLDERID>
{
Expand Down
36 changes: 33 additions & 3 deletions src/StronglyTypedIds.Templates/int-full.typedid
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#endif
#if NET7_0_OR_GREATER
global::System.IParsable<PLACEHOLDERID>, global::System.ISpanParsable<PLACEHOLDERID>,
#endif
#if NET8_0_OR_GREATER
global::System.IUtf8SpanParsable<PLACEHOLDERID>, global::System.IUtf8SpanFormattable,
#endif
global::System.IComparable<PLACEHOLDERID>, global::System.IEquatable<PLACEHOLDERID>, global::System.IFormattable
{
Expand Down Expand Up @@ -136,7 +139,7 @@
/// <inheritdoc cref="global::System.IFormattable"/>
public string ToString(
#if NET7_0_OR_GREATER
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.GuidFormat)]
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.NumericFormat)]
#endif
string? format,
global::System.IFormatProvider? formatProvider)
Expand Down Expand Up @@ -182,7 +185,7 @@
global::System.Span<char> destination,
out int charsWritten,
#if NET7_0_OR_GREATER
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.GuidFormat)]
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.NumericFormat)]
#endif
global::System.ReadOnlySpan<char> format,
global::System.IFormatProvider? provider)
Expand All @@ -193,11 +196,38 @@
global::System.Span<char> destination,
out int charsWritten,
#if NET7_0_OR_GREATER
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.GuidFormat)]
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.NumericFormat)]
#endif
global::System.ReadOnlySpan<char> format = default)
=> Value.TryFormat(destination, out charsWritten, format);
#endif
#if NET8_0_OR_GREATER
/// <inheritdoc cref="global::System.IUtf8SpanFormattable.TryFormat" />
public bool TryFormat(
global::System.Span<byte> utf8Destination,
out int bytesWritten,
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.NumericFormat)]
global::System.ReadOnlySpan<char> format = default,
global::System.IFormatProvider? provider = null)
=> Value.TryFormat(utf8Destination, out bytesWritten, format, provider);

/// <inheritdoc cref="global::System.IUtf8SpanParsable{TSelf}.Parse(global::System.ReadOnlySpan{byte}, global::System.IFormatProvider?)" />
public static PLACEHOLDERID Parse(global::System.ReadOnlySpan<byte> utf8Text, global::System.IFormatProvider? provider)
=> new(int.Parse(utf8Text, provider));

/// <inheritdoc cref="global::System.IUtf8SpanParsable{TSelf}.TryParse(global::System.ReadOnlySpan{byte}, global::System.IFormatProvider?, out TSelf)" />
public static bool TryParse(global::System.ReadOnlySpan<byte> utf8Text, global::System.IFormatProvider? provider, out PLACEHOLDERID result)
{
if (int.TryParse(utf8Text, provider, out var intResult))
{
result = new PLACEHOLDERID(intResult);
return true;
}

result = default;
return false;
}
#endif

public partial class DapperTypeHandler : global::Dapper.SqlMapper.TypeHandler<PLACEHOLDERID>
{
Expand Down
37 changes: 33 additions & 4 deletions src/StronglyTypedIds.Templates/long-full.typedid
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#endif
#if NET7_0_OR_GREATER
global::System.IParsable<PLACEHOLDERID>, global::System.ISpanParsable<PLACEHOLDERID>,
#endif
#if NET8_0_OR_GREATER
global::System.IUtf8SpanParsable<PLACEHOLDERID>, global::System.IUtf8SpanFormattable,
#endif
global::System.IComparable<PLACEHOLDERID>, global::System.IEquatable<PLACEHOLDERID>, global::System.IFormattable
{
Expand Down Expand Up @@ -136,7 +139,7 @@
/// <inheritdoc cref="global::System.IFormattable"/>
public string ToString(
#if NET7_0_OR_GREATER
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.GuidFormat)]
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.NumericFormat)]
#endif
string? format,
global::System.IFormatProvider? formatProvider)
Expand Down Expand Up @@ -182,7 +185,7 @@
global::System.Span<char> destination,
out int charsWritten,
#if NET7_0_OR_GREATER
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.GuidFormat)]
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.NumericFormat)]
#endif
global::System.ReadOnlySpan<char> format,
global::System.IFormatProvider? provider)
Expand All @@ -193,12 +196,38 @@
global::System.Span<char> destination,
out int charsWritten,
#if NET7_0_OR_GREATER
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.GuidFormat)]
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.NumericFormat)]
#endif
global::System.ReadOnlySpan<char> format = default)
=> Value.TryFormat(destination, out charsWritten, format);
#endif

#if NET8_0_OR_GREATER
/// <inheritdoc cref="global::System.IUtf8SpanFormattable.TryFormat" />
public bool TryFormat(
global::System.Span<byte> utf8Destination,
out int bytesWritten,
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.NumericFormat)]
global::System.ReadOnlySpan<char> format = default,
global::System.IFormatProvider? provider = null)
=> Value.TryFormat(utf8Destination, out bytesWritten, format, provider);

/// <inheritdoc cref="global::System.IUtf8SpanParsable{TSelf}.Parse(global::System.ReadOnlySpan{byte}, global::System.IFormatProvider?)" />
public static PLACEHOLDERID Parse(global::System.ReadOnlySpan<byte> utf8Text, global::System.IFormatProvider? provider)
=> new(long.Parse(utf8Text, provider));

/// <inheritdoc cref="global::System.IUtf8SpanParsable{TSelf}.TryParse(global::System.ReadOnlySpan{byte}, global::System.IFormatProvider?, out TSelf)" />
public static bool TryParse(global::System.ReadOnlySpan<byte> utf8Text, global::System.IFormatProvider? provider, out PLACEHOLDERID result)
{
if (long.TryParse(utf8Text, provider, out var intResult))
{
result = new PLACEHOLDERID(intResult);
return true;
}

result = default;
return false;
}
#endif
public partial class DapperTypeHandler : global::Dapper.SqlMapper.TypeHandler<PLACEHOLDERID>
{
public override void SetValue(global::System.Data.IDbDataParameter parameter, PLACEHOLDERID value)
Expand Down
13 changes: 13 additions & 0 deletions src/StronglyTypedIds/EmbeddedSources.Guid.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ partial struct PLACEHOLDERID :
#endif
#if NET7_0_OR_GREATER
global::System.IParsable<PLACEHOLDERID>, global::System.ISpanParsable<PLACEHOLDERID>,
#endif
#if NET8_0_OR_GREATER
global::System.IUtf8SpanFormattable,
#endif
global::System.IComparable<PLACEHOLDERID>, global::System.IEquatable<PLACEHOLDERID>, global::System.IFormattable
{
Expand Down Expand Up @@ -204,6 +207,16 @@ public bool TryFormat(
#endif
global::System.ReadOnlySpan<char> format = default)
=> Value.TryFormat(destination, out charsWritten, format);
#endif
#if NET8_0_OR_GREATER
/// <inheritdoc cref="global::System.IUtf8SpanFormattable.TryFormat" />
public bool TryFormat(
global::System.Span<byte> utf8Destination,
out int bytesWritten,
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.GuidFormat)]
global::System.ReadOnlySpan<char> format,
global::System.IFormatProvider? provider)
=> Value.TryFormat(utf8Destination, out bytesWritten, format);
#endif
}
""";
Expand Down
34 changes: 32 additions & 2 deletions src/StronglyTypedIds/EmbeddedSources.Int.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ partial struct PLACEHOLDERID :
#endif
#if NET7_0_OR_GREATER
global::System.IParsable<PLACEHOLDERID>, global::System.ISpanParsable<PLACEHOLDERID>,
#endif
#if NET8_0_OR_GREATER
global::System.IUtf8SpanParsable<PLACEHOLDERID>, global::System.IUtf8SpanFormattable,
#endif
global::System.IComparable<PLACEHOLDERID>, global::System.IEquatable<PLACEHOLDERID>, global::System.IFormattable
{
Expand Down Expand Up @@ -184,7 +187,7 @@ public bool TryFormat(
global::System.Span<char> destination,
out int charsWritten,
#if NET7_0_OR_GREATER
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.GuidFormat)]
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.NumericFormat)]
#endif
global::System.ReadOnlySpan<char> format,
global::System.IFormatProvider? provider)
Expand All @@ -195,10 +198,37 @@ public bool TryFormat(
global::System.Span<char> destination,
out int charsWritten,
#if NET7_0_OR_GREATER
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.GuidFormat)]
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.NumericFormat)]
#endif
global::System.ReadOnlySpan<char> format = default)
=> Value.TryFormat(destination, out charsWritten, format);
#endif
#if NET8_0_OR_GREATER
/// <inheritdoc cref="global::System.IUtf8SpanFormattable.TryFormat" />
public bool TryFormat(
global::System.Span<byte> utf8Destination,
out int bytesWritten,
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.NumericFormat)]
global::System.ReadOnlySpan<char> format = default,
global::System.IFormatProvider? provider = null)
=> Value.TryFormat(utf8Destination, out bytesWritten, format, provider);

/// <inheritdoc cref="global::System.IUtf8SpanParsable{TSelf}.Parse(global::System.ReadOnlySpan{byte}, global::System.IFormatProvider?)" />
public static PLACEHOLDERID Parse(global::System.ReadOnlySpan<byte> utf8Text, global::System.IFormatProvider? provider)
=> new(int.Parse(utf8Text, provider));

/// <inheritdoc cref="global::System.IUtf8SpanParsable{TSelf}.TryParse(global::System.ReadOnlySpan{byte}, global::System.IFormatProvider?, out TSelf)" />
public static bool TryParse(global::System.ReadOnlySpan<byte> utf8Text, global::System.IFormatProvider? provider, out PLACEHOLDERID result)
{
if (int.TryParse(utf8Text, provider, out var intResult))
{
result = new PLACEHOLDERID(intResult);
return true;
}

result = default;
return false;
}
#endif
}
""";
Expand Down
30 changes: 30 additions & 0 deletions src/StronglyTypedIds/EmbeddedSources.Long.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ partial struct PLACEHOLDERID :
#endif
#if NET7_0_OR_GREATER
global::System.IParsable<PLACEHOLDERID>, global::System.ISpanParsable<PLACEHOLDERID>,
#endif
#if NET8_0_OR_GREATER
global::System.IUtf8SpanParsable<PLACEHOLDERID>, global::System.IUtf8SpanFormattable,
#endif
global::System.IComparable<PLACEHOLDERID>, global::System.IEquatable<PLACEHOLDERID>, global::System.IFormattable
{
Expand Down Expand Up @@ -199,6 +202,33 @@ public bool TryFormat(
#endif
global::System.ReadOnlySpan<char> format = default)
=> Value.TryFormat(destination, out charsWritten, format);
#endif
#if NET8_0_OR_GREATER
/// <inheritdoc cref="global::System.IUtf8SpanFormattable.TryFormat" />
public bool TryFormat(
global::System.Span<byte> utf8Destination,
out int bytesWritten,
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.NumericFormat)]
global::System.ReadOnlySpan<char> format = default,
global::System.IFormatProvider? provider = null)
=> Value.TryFormat(utf8Destination, out bytesWritten, format, provider);

/// <inheritdoc cref="global::System.IUtf8SpanParsable{TSelf}.Parse(ReadOnlySpan{byte}, IFormatProvider?)" />
public static PLACEHOLDERID Parse(global::System.ReadOnlySpan<byte> utf8Text, global::System.IFormatProvider? provider)
=> new(long.Parse(utf8Text, provider));

/// <inheritdoc cref="global::System.IUtf8SpanParsable{TSelf}.TryParse(ReadOnlySpan{byte}, IFormatProvider?, out TSelf)" />
public static bool TryParse(global::System.ReadOnlySpan<byte> utf8Text, global::System.IFormatProvider? provider, out PLACEHOLDERID result)
{
if (long.TryParse(utf8Text, provider, out var intResult))
{
result = new PLACEHOLDERID(intResult);
return true;
}

result = default;
return false;
}
#endif
}
""";
Expand Down
17 changes: 17 additions & 0 deletions test/StronglyTypedIds.IntegrationTests/GuidIdTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,23 @@ public void CantCreateEmptyGeneratedId1()
Assert.NotEqual((object)bar, (object)foo);
}

#if NET8_0_OR_GREATER
[Fact]
public void CanFormatAsUtf8()
{
var expected = "17b4c96b-023a-4050-8ab5-4511c0e7fd09"u8;
var id = GuidId1.Parse("17B4C96B-023A-4050-8AB5-4511C0E7FD09");

var format = "D"; // in expected format
var actual = new byte[expected.Length].AsSpan();
var success = id.TryFormat(actual, out var charsWritten, format, provider: null);

Assert.True(success);
Assert.Equal(expected.Length, charsWritten);
Assert.True(actual.SequenceEqual(expected));
}
#endif

[Fact]
public void CanSerializeToGuid_WithTypeConverter()
{
Expand Down
16 changes: 16 additions & 0 deletions test/StronglyTypedIds.IntegrationTests/IntIdTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,22 @@ public void DifferentTypesAreUnequal()
Assert.NotEqual((object) bar, (object) foo);
}

#if NET8_0_OR_GREATER
[Fact]
public void CanRoundTripUtf8()
{
var id = new IntId(123);

var actual = new byte[16].AsSpan();
Assert.True(id.TryFormat(actual, out var charsWritten));

var success = IntId.TryParse(actual.Slice(0, charsWritten), provider: null, out var result);

Assert.True(success);
Assert.Equal(id, result);
}
#endif

[Fact]
public void CanSerializeToNullableInt_WithNewtonsoftJsonProvider()
{
Expand Down
16 changes: 16 additions & 0 deletions test/StronglyTypedIds.IntegrationTests/LongIdTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,22 @@ public void DifferentTypesAreUnequal()
Assert.NotEqual((object)bar, (object)foo);
}

#if NET8_0_OR_GREATER
[Fact]
public void CanRoundTripUtf8()
{
var id = new LongId(123L);

var actual = new byte[16].AsSpan();
Assert.True(id.TryFormat(actual, out var charsWritten));

var success = LongId.TryParse(actual.Slice(0, charsWritten), provider: null, out var result);

Assert.True(success);
Assert.Equal(id, result);
}
#endif

[Fact]
public void CanSerializeToNullableInt_WithNewtonsoftJsonProvider()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
#endif
#if NET7_0_OR_GREATER
global::System.IParsable<MyId>, global::System.ISpanParsable<MyId>,
#endif
#if NET8_0_OR_GREATER
global::System.IUtf8SpanFormattable,
#endif
global::System.IComparable<MyId>, global::System.IEquatable<MyId>, global::System.IFormattable
{
Expand Down Expand Up @@ -213,5 +216,15 @@
#endif
global::System.ReadOnlySpan<char> format = default)
=> Value.TryFormat(destination, out charsWritten, format);
#endif
#if NET8_0_OR_GREATER
/// <inheritdoc cref="global::System.IUtf8SpanFormattable.TryFormat" />
public bool TryFormat(
global::System.Span<byte> utf8Destination,
out int bytesWritten,
[global::System.Diagnostics.CodeAnalysis.StringSyntax(global::System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.GuidFormat)]
global::System.ReadOnlySpan<char> format,
global::System.IFormatProvider? provider)
=> Value.TryFormat(utf8Destination, out bytesWritten, format);
#endif
}
Loading
Loading