From 775671e829c2748eedf50cc02da39edee933fa3f Mon Sep 17 00:00:00 2001 From: Funkest Date: Tue, 19 Nov 2024 05:43:31 +0900 Subject: [PATCH 1/2] add UUIDv7 generation option, its test and document --- README.md | 10 ++++-- UnitGenerator.sln | 7 ++++ src/UnitGenerator/SourceGenerator.cs | 12 +++++++ .../UnitGenerator.NET9.Tests.csproj | 25 +++++++++++++ .../UnitOfGuidTests.cs | 35 +++++++++++++++++++ 5 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 tests/UnitGenerator.NET9.Tests/UnitGenerator.NET9.Tests.csproj create mode 100644 tests/UnitGenerator.NET9.Tests/UnitOfGuidTests.cs diff --git a/README.md b/README.md index 2cda480..3af51d3 100644 --- a/README.md +++ b/README.md @@ -243,8 +243,8 @@ namespace UnitGenerator { this.Type = typeof(T); this.Options = options; -   } -    } + } + } #endif } ``` @@ -283,11 +283,15 @@ public static bool operator false(Foo x) => !x.value; public static bool operator !(Foo x) => !x.value; ``` -> When type is Guid or [Ulid](https://github.com/Cysharp/Ulid), also implements `New()` and `New***()` static operator. +> When type is Guid or [Ulid](https://github.com/Cysharp/Ulid), also implements `New()` and `New***()` static operator.
+> For Guid type in .NET 9.0 or later, these methods accept an optional `uuidV7` parameter. When `uuidV7` is set to `true`, the methods use `Guid.CreateVersion7()` internally. ```csharp public static GroupId New(); public static GroupId NewGroupId(); +// .NET 9.0+ +public static GroupId New(bool uuidV7 = false); +public static GroupId NewGroupId(bool uuidV7 = false); ``` Second parameter `UnitGenerateOptions options` can configure which method to implement, default is `None`. diff --git a/UnitGenerator.sln b/UnitGenerator.sln index 24ed89c..84f73fd 100644 --- a/UnitGenerator.sln +++ b/UnitGenerator.sln @@ -19,6 +19,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileGenerate", "sandbox\Fil EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EntityFrameworkApp", "src\EntityFrameworkApp\EntityFrameworkApp.csproj", "{51AE7857-4223-40FE-AEA9-F0E64C5F8238}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitGenerator.NET9.Tests", "tests\UnitGenerator.NET9.Tests\UnitGenerator.NET9.Tests.csproj", "{EAC3A025-E49B-4719-8738-9C3B67484A4A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -45,6 +47,10 @@ Global {51AE7857-4223-40FE-AEA9-F0E64C5F8238}.Debug|Any CPU.Build.0 = Debug|Any CPU {51AE7857-4223-40FE-AEA9-F0E64C5F8238}.Release|Any CPU.ActiveCfg = Release|Any CPU {51AE7857-4223-40FE-AEA9-F0E64C5F8238}.Release|Any CPU.Build.0 = Release|Any CPU + {EAC3A025-E49B-4719-8738-9C3B67484A4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EAC3A025-E49B-4719-8738-9C3B67484A4A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EAC3A025-E49B-4719-8738-9C3B67484A4A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EAC3A025-E49B-4719-8738-9C3B67484A4A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -55,6 +61,7 @@ Global {5DA06D43-A023-4464-B856-8BB42E8E4A05} = {187FBF64-D2AA-444D-AFB1-CE999BC6AD34} {F8353A7A-290E-41D7-A6F8-8D8DBDD44433} = {34EB4113-923D-4855-979C-A0467461B75C} {51AE7857-4223-40FE-AEA9-F0E64C5F8238} = {34EB4113-923D-4855-979C-A0467461B75C} + {EAC3A025-E49B-4719-8738-9C3B67484A4A} = {187FBF64-D2AA-444D-AFB1-CE999BC6AD34} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A64DF779-7829-414F-9E6E-3AF349486508} diff --git a/src/UnitGenerator/SourceGenerator.cs b/src/UnitGenerator/SourceGenerator.cs index 06d47ac..a0281b8 100644 --- a/src/UnitGenerator/SourceGenerator.cs +++ b/src/UnitGenerator/SourceGenerator.cs @@ -554,6 +554,18 @@ public bool TryFormat (Span utf8Destination, out int bytesWritten, ReadOnl return new {{unitTypeName}}(Guid.NewGuid()); } +#if NET9_0_OR_GREATER + public static {{unitTypeName}} New(bool uuidV7 = false) + { + return new {{unitTypeName}}(uuidV7 ? Guid.CreateVersion7() : Guid.NewGuid()); + } + + public static {{unitTypeName}} New{{unitTypeName}}(bool uuidV7 = false) + { + return new {{unitTypeName}}(uuidV7 ? Guid.CreateVersion7() : Guid.NewGuid()); + } +#endif + """); } diff --git a/tests/UnitGenerator.NET9.Tests/UnitGenerator.NET9.Tests.csproj b/tests/UnitGenerator.NET9.Tests/UnitGenerator.NET9.Tests.csproj new file mode 100644 index 0000000..4fcb87c --- /dev/null +++ b/tests/UnitGenerator.NET9.Tests/UnitGenerator.NET9.Tests.csproj @@ -0,0 +1,25 @@ + + + + net9.0 + enable + false + + + + + + + + + + + + + + + + + + + diff --git a/tests/UnitGenerator.NET9.Tests/UnitOfGuidTests.cs b/tests/UnitGenerator.NET9.Tests/UnitOfGuidTests.cs new file mode 100644 index 0000000..d6fc7de --- /dev/null +++ b/tests/UnitGenerator.NET9.Tests/UnitOfGuidTests.cs @@ -0,0 +1,35 @@ +using FluentAssertions; +using System; + +namespace UnitGenerator.NET9.Tests; + +public class UnitOfGuidTests +{ + [Fact] + public void Guidv7_v4_Comparison_AsExpected() + { + // v7 + TryGetUuidV7Timestamp(Guidv7Unit.New(uuidV7: true).AsPrimitive(), out var v).Should().BeTrue(); + // ...approximate check + v?.ToString("yyyyMMdd").Should().Be(DateTime.UtcNow.ToString("yyyyMMdd")); + TryGetUuidV7Timestamp(Guidv7Unit.New().AsPrimitive(), out var _).Should().BeFalse(); + } + + static bool TryGetUuidV7Timestamp(Guid uuid, out DateTimeOffset? timestamp) + { + timestamp = null; + var uuidString = uuid.ToString("N"); + // version number is the 13th character + if (uuidString[12] == '7') + { + var timestampHex = uuidString.Substring(0, 12); + var milliseconds = Convert.ToInt64(timestampHex, 16); + timestamp = DateTimeOffset.FromUnixTimeMilliseconds(milliseconds); + return true; + } + else return false; + } +} + +[UnitOf()] +public readonly partial struct Guidv7Unit { } From 93a1835e108a0013686dd41977578b1ce8e1badd Mon Sep 17 00:00:00 2001 From: Funkest Date: Tue, 19 Nov 2024 14:54:19 +0900 Subject: [PATCH 2/2] remove unnecessary default value --- README.md | 6 +++--- src/UnitGenerator/SourceGenerator.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3af51d3..70e4201 100644 --- a/README.md +++ b/README.md @@ -289,9 +289,9 @@ public static bool operator !(Foo x) => !x.value; ```csharp public static GroupId New(); public static GroupId NewGroupId(); -// .NET 9.0+ -public static GroupId New(bool uuidV7 = false); -public static GroupId NewGroupId(bool uuidV7 = false); +// overload .NET 9.0+ +public static GroupId New(bool uuidV7); +public static GroupId NewGroupId(bool uuidV7); ``` Second parameter `UnitGenerateOptions options` can configure which method to implement, default is `None`. diff --git a/src/UnitGenerator/SourceGenerator.cs b/src/UnitGenerator/SourceGenerator.cs index a0281b8..6ffcc4a 100644 --- a/src/UnitGenerator/SourceGenerator.cs +++ b/src/UnitGenerator/SourceGenerator.cs @@ -555,12 +555,12 @@ public bool TryFormat (Span utf8Destination, out int bytesWritten, ReadOnl } #if NET9_0_OR_GREATER - public static {{unitTypeName}} New(bool uuidV7 = false) + public static {{unitTypeName}} New(bool uuidV7) { return new {{unitTypeName}}(uuidV7 ? Guid.CreateVersion7() : Guid.NewGuid()); } - public static {{unitTypeName}} New{{unitTypeName}}(bool uuidV7 = false) + public static {{unitTypeName}} New{{unitTypeName}}(bool uuidV7) { return new {{unitTypeName}}(uuidV7 ? Guid.CreateVersion7() : Guid.NewGuid()); }