diff --git a/src/TransparentValueObjects.Augments/IHasRandomValueGenerator.cs b/src/TransparentValueObjects.Augments/IHasRandomValueGenerator.cs new file mode 100644 index 0000000..7bc3b95 --- /dev/null +++ b/src/TransparentValueObjects.Augments/IHasRandomValueGenerator.cs @@ -0,0 +1,27 @@ +using System; + +namespace TransparentValueObjects.Augments; + +/// +/// Augment to enable support for random value generation via . +/// +/// +/// +/// +public interface IHasRandomValueGenerator + where TValueObject : IValueObject + where TValue : notnull + where TRandom : Random +{ + /// + /// Gets the random source. can be used for better performance. + /// + /// + public static abstract TRandom GetRandom(); + + /// + /// Gets the random object. + /// + /// + public static abstract TValueObject NewRandomValue(); +} diff --git a/src/TransparentValueObjects.Augments/IHasUnmanagedRandomValueGenerator.cs b/src/TransparentValueObjects.Augments/IHasUnmanagedRandomValueGenerator.cs new file mode 100644 index 0000000..4ecbd00 --- /dev/null +++ b/src/TransparentValueObjects.Augments/IHasUnmanagedRandomValueGenerator.cs @@ -0,0 +1,18 @@ +using System; + +namespace TransparentValueObjects.Augments; + +/// +/// Augment to extend support for random value generation via . +/// Provides a high performance implementation for structs. +/// +/// +/// +/// +public interface IHasUnmanagedRandomValueGenerator : IHasRandomValueGenerator + where TValueObject : IValueObject + where TValue : unmanaged + where TRandom : Random +{ + +} diff --git a/src/TransparentValueObjects.Sample/Generated/TransparentValueObjects/TransparentValueObjects.ValueObjectIncrementalSourceGenerator/SampleValueObjectGuid.g.cs b/src/TransparentValueObjects.Sample/Generated/TransparentValueObjects/TransparentValueObjects.ValueObjectIncrementalSourceGenerator/SampleValueObjectGuid.g.cs index d182e3c..2ae2a9b 100644 --- a/src/TransparentValueObjects.Sample/Generated/TransparentValueObjects/TransparentValueObjects.ValueObjectIncrementalSourceGenerator/SampleValueObjectGuid.g.cs +++ b/src/TransparentValueObjects.Sample/Generated/TransparentValueObjects/TransparentValueObjects.ValueObjectIncrementalSourceGenerator/SampleValueObjectGuid.g.cs @@ -32,7 +32,7 @@ private SampleValueObjectGuid(global::System.Guid value) public override string ToString() => Value.ToString(); public bool Equals(SampleValueObjectGuid other) => Equals(other.Value); - public bool Equals(global::System.Guid other) => Value.Equals(other); + public bool Equals(global::System.Guid other) => InnerValueDefaultEqualityComparer.Equals(Value, other); public bool Equals(SampleValueObjectGuid other, global::System.Collections.Generic.IEqualityComparer comparer) => comparer.Equals(Value, other.Value); public override bool Equals(object? obj) { diff --git a/src/TransparentValueObjects.Sample/Generated/TransparentValueObjects/TransparentValueObjects.ValueObjectIncrementalSourceGenerator/SampleValueObjectInt.g.cs b/src/TransparentValueObjects.Sample/Generated/TransparentValueObjects/TransparentValueObjects.ValueObjectIncrementalSourceGenerator/SampleValueObjectInt.g.cs index e925d56..495419d 100644 --- a/src/TransparentValueObjects.Sample/Generated/TransparentValueObjects/TransparentValueObjects.ValueObjectIncrementalSourceGenerator/SampleValueObjectInt.g.cs +++ b/src/TransparentValueObjects.Sample/Generated/TransparentValueObjects/TransparentValueObjects.ValueObjectIncrementalSourceGenerator/SampleValueObjectInt.g.cs @@ -32,7 +32,7 @@ private SampleValueObjectInt(global::System.Int32 value) public override string ToString() => Value.ToString(); public bool Equals(SampleValueObjectInt other) => Equals(other.Value); - public bool Equals(global::System.Int32 other) => Value.Equals(other); + public bool Equals(global::System.Int32 other) => InnerValueDefaultEqualityComparer.Equals(Value, other); public bool Equals(SampleValueObjectInt other, global::System.Collections.Generic.IEqualityComparer comparer) => comparer.Equals(Value, other.Value); public override bool Equals(object? obj) { @@ -92,6 +92,17 @@ public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter } + public static global::System.Random GetRandom() => new global::System.Random(); + public static SampleValueObjectInt NewRandomValue() + { + var random = GetRandom(); + var size = global::System.Runtime.CompilerServices.Unsafe.SizeOf(); + global::System.Span bytes = stackalloc byte[size]; + random.NextBytes(bytes); + var id = global::System.Runtime.InteropServices.MemoryMarshal.Cast(bytes)[0]; + return SampleValueObjectInt.From(id); + } + public global::System.Int32 CompareTo(SampleValueObjectInt other) => Value.CompareTo(other); public static bool operator <(SampleValueObjectInt left, SampleValueObjectInt right) => left.Value.CompareTo(right.Value) < 0; public static bool operator >(SampleValueObjectInt left, SampleValueObjectInt right) => left.Value.CompareTo(right.Value) > 0; diff --git a/src/TransparentValueObjects.Sample/Generated/TransparentValueObjects/TransparentValueObjects.ValueObjectIncrementalSourceGenerator/SampleValueObjectString.g.cs b/src/TransparentValueObjects.Sample/Generated/TransparentValueObjects/TransparentValueObjects.ValueObjectIncrementalSourceGenerator/SampleValueObjectString.g.cs index 14ea0cb..ee235c6 100644 --- a/src/TransparentValueObjects.Sample/Generated/TransparentValueObjects/TransparentValueObjects.ValueObjectIncrementalSourceGenerator/SampleValueObjectString.g.cs +++ b/src/TransparentValueObjects.Sample/Generated/TransparentValueObjects/TransparentValueObjects.ValueObjectIncrementalSourceGenerator/SampleValueObjectString.g.cs @@ -92,6 +92,7 @@ public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter } + public static global::System.Random GetRandom() => new global::System.Random(); public global::System.Int32 CompareTo(SampleValueObjectString other) => Value.CompareTo(other); public static bool operator <(SampleValueObjectString left, SampleValueObjectString right) => left.Value.CompareTo(right.Value) < 0; public static bool operator >(SampleValueObjectString left, SampleValueObjectString right) => left.Value.CompareTo(right.Value) > 0; diff --git a/src/TransparentValueObjects.Sample/SampleValueObjectGuid.cs b/src/TransparentValueObjects.Sample/SampleValueObjectGuid.cs index d409c97..6cdfe0b 100644 --- a/src/TransparentValueObjects.Sample/SampleValueObjectGuid.cs +++ b/src/TransparentValueObjects.Sample/SampleValueObjectGuid.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using TransparentValueObjects.Augments; using TransparentValueObjects.Generated; @@ -7,7 +8,12 @@ namespace TransparentValueObjects.Sample; [ValueObject] public readonly partial struct SampleValueObjectGuid : IHasDefaultValue, - IHasSystemTextJsonConverter + IHasDefaultEqualityComparer, + IHasSystemTextJsonConverter, + IHasRandomValueGenerator { public static SampleValueObjectGuid DefaultValue => From(Guid.Empty); + public static IEqualityComparer InnerValueDefaultEqualityComparer => EqualityComparer.Default; + public static Random GetRandom() => Random.Shared; + public static SampleValueObjectGuid NewRandomValue() => From(Guid.NewGuid()); } diff --git a/src/TransparentValueObjects.Sample/SampleValueObjectInt.cs b/src/TransparentValueObjects.Sample/SampleValueObjectInt.cs index b9d2d8a..43f1115 100644 --- a/src/TransparentValueObjects.Sample/SampleValueObjectInt.cs +++ b/src/TransparentValueObjects.Sample/SampleValueObjectInt.cs @@ -1,4 +1,6 @@ -using TransparentValueObjects.Augments; +using System; +using System.Collections.Generic; +using TransparentValueObjects.Augments; using TransparentValueObjects.Generated; namespace TransparentValueObjects.Sample; @@ -6,7 +8,10 @@ namespace TransparentValueObjects.Sample; [ValueObject] public readonly partial struct SampleValueObjectInt : IHasDefaultValue, - IHasSystemTextJsonConverter + IHasDefaultEqualityComparer, + IHasSystemTextJsonConverter, + IHasUnmanagedRandomValueGenerator { public static SampleValueObjectInt DefaultValue => From(0); + public static IEqualityComparer InnerValueDefaultEqualityComparer => EqualityComparer.Default; } diff --git a/src/TransparentValueObjects.Sample/SampleValueObjectString.cs b/src/TransparentValueObjects.Sample/SampleValueObjectString.cs index 9320902..cd3562b 100644 --- a/src/TransparentValueObjects.Sample/SampleValueObjectString.cs +++ b/src/TransparentValueObjects.Sample/SampleValueObjectString.cs @@ -9,8 +9,18 @@ namespace TransparentValueObjects.Sample; public readonly partial struct SampleValueObjectString : IHasDefaultValue, IHasDefaultEqualityComparer, - IHasSystemTextJsonConverter + IHasSystemTextJsonConverter, + IHasRandomValueGenerator { public static SampleValueObjectString DefaultValue => From("Hello World!"); public static IEqualityComparer InnerValueDefaultEqualityComparer => StringComparer.OrdinalIgnoreCase; + public static SampleValueObjectString NewRandomValue() + { + const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + return From(string.Create(10, GetRandom(), static (span, random) => + { + for (var i = 0; i < span.Length; i++) + span[i] = chars[random.Next(0, chars.Length)]; + })); + } } diff --git a/src/TransparentValueObjects/ValueObjectIncrementalSourceGenerator.cs b/src/TransparentValueObjects/ValueObjectIncrementalSourceGenerator.cs index 35550f7..22e4aee 100644 --- a/src/TransparentValueObjects/ValueObjectIncrementalSourceGenerator.cs +++ b/src/TransparentValueObjects/ValueObjectIncrementalSourceGenerator.cs @@ -20,6 +20,8 @@ public class ValueObjectIncrementalSourceGenerator : IIncrementalGenerator private const string HasDefaultValueInterfaceName = "IHasDefaultValue"; private const string HasDefaultEqualityComparerInterfaceName = "IHasDefaultEqualityComparer"; private const string HasSystemTextJsonConverterInterfaceName = "IHasSystemTextJsonConverter"; + private const string HasRandomValueGeneratorInterfaceName = "IHasRandomValueGenerator"; + private const string HasUnmanagedRandomValueGeneratorInterfaceName = "IHasUnmanagedRandomValueGenerator"; private const string AttributeSourceCode = $$""" @@ -173,6 +175,24 @@ private static void Generate(SourceProductionContext context, Compilation compil if (hasSystemTextJsonConverter && !hasSystemTextJsonConverterOverride) AddSystemTextJsonClasses(cw, valueObjectTypeName, innerValueTypeName, hasDefaultValue); + // The NewRandomValue + if (GetAugment(valueObjectInterfaces, HasRandomValueGeneratorInterfaceName) is { TypeArguments.Length: 3 } randomAugmentTypeSymbol) + { + var randomType = randomAugmentTypeSymbol.TypeArguments[2]; + var randomTypeName = $"global::{randomType.ContainingNamespace.ToDisplayString()}.{randomType.Name}"; + var hasGetRandomOverride = valueObjectNamedTypeSymbol.GetMembers("GetRandom") + .Any(x => x is IMethodSymbol { ReturnType: var ret, Parameters.Length: 0 } && SymbolEqualityComparer.Default.Equals(ret, randomType)); + AddRandomValueMethod(cw, randomTypeName, hasGetRandomOverride); + } + if (GetAugment(valueObjectInterfaces, HasUnmanagedRandomValueGeneratorInterfaceName) is { TypeArguments.Length: 3 } uRandomAugmentTypeSymbol) + { + var randomType = uRandomAugmentTypeSymbol.TypeArguments[2]; + var randomTypeName = $"global::{randomType.ContainingNamespace.ToDisplayString()}.{randomType.Name}"; + var hasGetRandomOverride = valueObjectNamedTypeSymbol.GetMembers("GetRandom") + .Any(x => x is IMethodSymbol { ReturnType: var ret, Parameters.Length: 0 } && SymbolEqualityComparer.Default.Equals(ret, randomType)); + AddUnmanagedRandomValueMethod(cw, valueObjectTypeName, innerValueTypeName, randomTypeName, hasGetRandomOverride); + } + if (comparableInterfaceTypeSymbol is not null) { ForwardInterface(cw, valueObjectTypeName, comparableInterfaceTypeSymbol); @@ -200,6 +220,11 @@ private static void Generate(SourceProductionContext context, Compilation compil ); } + private static INamedTypeSymbol? GetAugment(ImmutableArray existingInterfaces, string augmentName) + { + return existingInterfaces.FirstOrDefault(x => x.Name == augmentName && x.ContainingNamespace.ToDisplayString() == AugmentedNamespace); + } + private static bool HasAugment(ImmutableArray existingInterfaces, string augmentName) { return existingInterfaces.Any(x => @@ -442,6 +467,41 @@ public static void AddSystemTextJsonClasses(CodeWriter cw, string valueObjectTyp } } + public static void AddRandomValueMethod( + CodeWriter cw, + string randomTypeName, + bool hasGetRandomOverride) + { + if (!hasGetRandomOverride) + { + cw.AppendLine($"public static {randomTypeName} GetRandom() => new {randomTypeName}();"); + } + } + + public static void AddUnmanagedRandomValueMethod( + CodeWriter cw, + string valueObjectTypeName, + string innerValueTypeName, + string randomTypeName, + bool hasGetRandomOverride) + { + if (!hasGetRandomOverride) + { + cw.AppendLine($"public static {randomTypeName} GetRandom() => new {randomTypeName}();"); + } + + cw.AppendLine($"public static {valueObjectTypeName} NewRandomValue()"); + using (cw.AddBlock()) + { + cw.AppendLine("var random = GetRandom();"); + cw.AppendLine($"var size = global::System.Runtime.CompilerServices.Unsafe.SizeOf<{innerValueTypeName}>();"); + cw.AppendLine("global::System.Span bytes = stackalloc byte[size];"); + cw.AppendLine("random.NextBytes(bytes);"); + cw.AppendLine($"var id = global::System.Runtime.InteropServices.MemoryMarshal.Cast(bytes)[0];"); + cw.AppendLine($"return {valueObjectTypeName}.From(id);"); + } + } + private readonly struct Target : IEquatable { public readonly StructDeclarationSyntax Syntax; diff --git a/tests/TransparentValueObjects.Tests/ValueObjectIncrementalSourceGeneratorTests/Augments/HasRandomValueGenerator.cs b/tests/TransparentValueObjects.Tests/ValueObjectIncrementalSourceGeneratorTests/Augments/HasRandomValueGenerator.cs new file mode 100644 index 0000000..558a9b7 --- /dev/null +++ b/tests/TransparentValueObjects.Tests/ValueObjectIncrementalSourceGeneratorTests/Augments/HasRandomValueGenerator.cs @@ -0,0 +1,104 @@ +using Xunit; + +namespace TransparentValueObjects.Tests.ValueObjectIncrementalSourceGeneratorTests.Augments; + +public class HasRandomValueGenerator +{ + private const string Input = +""" +using TransparentValueObjects.Generated; +using TransparentValueObjects.Augments; + +namespace TestNamespace; + +[ValueObject] +public readonly partial struct StringValueObject : IHasRandomValueGenerator +{ + public static StringValueObject DefaultValue => From("Hello World!"); +} +"""; + + private const string Output = +""" +// +#nullable enable +namespace TestNamespace; + +[global::System.Diagnostics.DebuggerDisplay("{Value}")] +[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage(Justification = "Auto-generated.")] +readonly partial struct StringValueObject : + global::TransparentValueObjects.Augments.IValueObject, + global::System.IEquatable, + global::System.IEquatable, + global::System.IComparable +{ + public readonly global::System.String Value; + + public static global::System.Type InnerValueType => typeof(global::System.String); + + [global::System.Obsolete($"Use StringValueObject.{nameof(From)} instead.", error: true)] + public StringValueObject() + { + throw new global::System.InvalidOperationException($"Use StringValueObject.{nameof(From)} instead."); + } + + private StringValueObject(global::System.String value) + { + Value = value; + } + + public static StringValueObject From(global::System.String value) => new(value); + + public override int GetHashCode() => Value.GetHashCode(); + + public override string ToString() => Value.ToString(); + + public bool Equals(StringValueObject other) => Equals(other.Value); + public bool Equals(global::System.String? other) => Value.Equals(other); + public bool Equals(StringValueObject other, global::System.Collections.Generic.IEqualityComparer comparer) => comparer.Equals(Value, other.Value); + public override bool Equals(object? obj) + { + if (obj is null) return false; + if (obj is StringValueObject value) return Equals(value); + if (obj is global::System.String innerValue) return Equals(innerValue); + return false; + } + + public static bool operator ==(StringValueObject left, StringValueObject right) => left.Equals(right); + public static bool operator !=(StringValueObject left, StringValueObject right) => !left.Equals(right); + + public static bool operator ==(StringValueObject left, global::System.String right) => left.Equals(right); + public static bool operator !=(StringValueObject left, global::System.String right) => !left.Equals(right); + + public static bool operator ==(global::System.String left, StringValueObject right) => right.Equals(left); + public static bool operator !=(global::System.String left, StringValueObject right) => !right.Equals(left); + + public static explicit operator StringValueObject(global::System.String value) => From(value); + public static explicit operator global::System.String(StringValueObject value) => value.Value; + + public static global::.Random GetRandom() => new global::.Random(); + public global::System.Int32 CompareTo(StringValueObject other) => Value.CompareTo(other); + public static bool operator <(StringValueObject left, StringValueObject right) => left.Value.CompareTo(right.Value) < 0; + public static bool operator >(StringValueObject left, StringValueObject right) => left.Value.CompareTo(right.Value) > 0; + public static bool operator <=(StringValueObject left, StringValueObject right) => left.Value.CompareTo(right.Value) <= 0; + public static bool operator >=(StringValueObject left, StringValueObject right) => left.Value.CompareTo(right.Value) >= 0; + + public static bool operator <(global::System.String left, StringValueObject right) => left.CompareTo(right.Value) < 0; + public static bool operator >(global::System.String left, StringValueObject right) => left.CompareTo(right.Value) > 0; + public static bool operator <=(global::System.String left, StringValueObject right) => left.CompareTo(right.Value) <= 0; + public static bool operator >=(global::System.String left, StringValueObject right) => left.CompareTo(right.Value) >= 0; + + public static bool operator <(StringValueObject left, global::System.String right) => left.Value.CompareTo(right) < 0; + public static bool operator >(StringValueObject left, global::System.String right) => left.Value.CompareTo(right) > 0; + public static bool operator <=(StringValueObject left, global::System.String right) => left.Value.CompareTo(right) <= 0; + public static bool operator >=(StringValueObject left, global::System.String right) => left.Value.CompareTo(right) >= 0; + +} +"""; + + [Fact] + public void TestAugment() + { + TestHelpers.TestGenerator(Input, "StringValueObject.g.cs", Output); + } +} diff --git a/tests/TransparentValueObjects.Tests/ValueObjectIncrementalSourceGeneratorTests/Augments/HasRandomValueGenerator_WithRandom.cs b/tests/TransparentValueObjects.Tests/ValueObjectIncrementalSourceGeneratorTests/Augments/HasRandomValueGenerator_WithRandom.cs new file mode 100644 index 0000000..e8f784f --- /dev/null +++ b/tests/TransparentValueObjects.Tests/ValueObjectIncrementalSourceGeneratorTests/Augments/HasRandomValueGenerator_WithRandom.cs @@ -0,0 +1,105 @@ +using Xunit; + +namespace TransparentValueObjects.Tests.ValueObjectIncrementalSourceGeneratorTests.Augments; + +public class HasRandomValueGenerator_WithRandom +{ + private const string Input = +""" +using TransparentValueObjects.Generated; +using TransparentValueObjects.Augments; + +namespace TestNamespace; + +[ValueObject] +public readonly partial struct StringValueObject : IHasRandomValueGenerator +{ + public static StringValueObject DefaultValue => From("Hello World!"); + + public static Random GetRandom() => new(); +} +"""; + + private const string Output = +""" +// +#nullable enable +namespace TestNamespace; + +[global::System.Diagnostics.DebuggerDisplay("{Value}")] +[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage(Justification = "Auto-generated.")] +readonly partial struct StringValueObject : + global::TransparentValueObjects.Augments.IValueObject, + global::System.IEquatable, + global::System.IEquatable, + global::System.IComparable +{ + public readonly global::System.String Value; + + public static global::System.Type InnerValueType => typeof(global::System.String); + + [global::System.Obsolete($"Use StringValueObject.{nameof(From)} instead.", error: true)] + public StringValueObject() + { + throw new global::System.InvalidOperationException($"Use StringValueObject.{nameof(From)} instead."); + } + + private StringValueObject(global::System.String value) + { + Value = value; + } + + public static StringValueObject From(global::System.String value) => new(value); + + public override int GetHashCode() => Value.GetHashCode(); + + public override string ToString() => Value.ToString(); + + public bool Equals(StringValueObject other) => Equals(other.Value); + public bool Equals(global::System.String? other) => Value.Equals(other); + public bool Equals(StringValueObject other, global::System.Collections.Generic.IEqualityComparer comparer) => comparer.Equals(Value, other.Value); + public override bool Equals(object? obj) + { + if (obj is null) return false; + if (obj is StringValueObject value) return Equals(value); + if (obj is global::System.String innerValue) return Equals(innerValue); + return false; + } + + public static bool operator ==(StringValueObject left, StringValueObject right) => left.Equals(right); + public static bool operator !=(StringValueObject left, StringValueObject right) => !left.Equals(right); + + public static bool operator ==(StringValueObject left, global::System.String right) => left.Equals(right); + public static bool operator !=(StringValueObject left, global::System.String right) => !left.Equals(right); + + public static bool operator ==(global::System.String left, StringValueObject right) => right.Equals(left); + public static bool operator !=(global::System.String left, StringValueObject right) => !right.Equals(left); + + public static explicit operator StringValueObject(global::System.String value) => From(value); + public static explicit operator global::System.String(StringValueObject value) => value.Value; + + public global::System.Int32 CompareTo(StringValueObject other) => Value.CompareTo(other); + public static bool operator <(StringValueObject left, StringValueObject right) => left.Value.CompareTo(right.Value) < 0; + public static bool operator >(StringValueObject left, StringValueObject right) => left.Value.CompareTo(right.Value) > 0; + public static bool operator <=(StringValueObject left, StringValueObject right) => left.Value.CompareTo(right.Value) <= 0; + public static bool operator >=(StringValueObject left, StringValueObject right) => left.Value.CompareTo(right.Value) >= 0; + + public static bool operator <(global::System.String left, StringValueObject right) => left.CompareTo(right.Value) < 0; + public static bool operator >(global::System.String left, StringValueObject right) => left.CompareTo(right.Value) > 0; + public static bool operator <=(global::System.String left, StringValueObject right) => left.CompareTo(right.Value) <= 0; + public static bool operator >=(global::System.String left, StringValueObject right) => left.CompareTo(right.Value) >= 0; + + public static bool operator <(StringValueObject left, global::System.String right) => left.Value.CompareTo(right) < 0; + public static bool operator >(StringValueObject left, global::System.String right) => left.Value.CompareTo(right) > 0; + public static bool operator <=(StringValueObject left, global::System.String right) => left.Value.CompareTo(right) <= 0; + public static bool operator >=(StringValueObject left, global::System.String right) => left.Value.CompareTo(right) >= 0; + +} +"""; + + [Fact] + public void TestAugment() + { + TestHelpers.TestGenerator(Input, "StringValueObject.g.cs", Output); + } +} diff --git a/tests/TransparentValueObjects.Tests/ValueObjectIncrementalSourceGeneratorTests/Augments/HasRandomValueGenerator_WithUnmanaged.cs b/tests/TransparentValueObjects.Tests/ValueObjectIncrementalSourceGeneratorTests/Augments/HasRandomValueGenerator_WithUnmanaged.cs new file mode 100644 index 0000000..402a263 --- /dev/null +++ b/tests/TransparentValueObjects.Tests/ValueObjectIncrementalSourceGeneratorTests/Augments/HasRandomValueGenerator_WithUnmanaged.cs @@ -0,0 +1,111 @@ +using Xunit; + +namespace TransparentValueObjects.Tests.ValueObjectIncrementalSourceGeneratorTests.Augments; + +public class HasRandomValueGenerator_WithUnmanaged +{ + private const string Input = +""" +using TransparentValueObjects.Generated; +using TransparentValueObjects.Augments; + +namespace TestNamespace; + +[ValueObject] +public readonly partial struct Int32ValueObject : IHasUnmanagedRandomValueGenerator { } +"""; + + private const string Output = +""" +// +#nullable enable +namespace TestNamespace; + +[global::System.Diagnostics.DebuggerDisplay("{Value}")] +[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage(Justification = "Auto-generated.")] +readonly partial struct Int32ValueObject : + global::TransparentValueObjects.Augments.IValueObject, + global::System.IEquatable, + global::System.IEquatable, + global::System.IComparable +{ + public readonly global::System.String Value; + + public static global::System.Type InnerValueType => typeof(global::System.String); + + [global::System.Obsolete($"Use Int32ValueObject.{nameof(From)} instead.", error: true)] + public Int32ValueObject() + { + throw new global::System.InvalidOperationException($"Use Int32ValueObject.{nameof(From)} instead."); + } + + private Int32ValueObject(global::System.String value) + { + Value = value; + } + + public static Int32ValueObject From(global::System.String value) => new(value); + + public override int GetHashCode() => Value.GetHashCode(); + + public override string ToString() => Value.ToString(); + + public bool Equals(Int32ValueObject other) => Equals(other.Value); + public bool Equals(global::System.String? other) => Value.Equals(other); + public bool Equals(Int32ValueObject other, global::System.Collections.Generic.IEqualityComparer comparer) => comparer.Equals(Value, other.Value); + public override bool Equals(object? obj) + { + if (obj is null) return false; + if (obj is Int32ValueObject value) return Equals(value); + if (obj is global::System.String innerValue) return Equals(innerValue); + return false; + } + + public static bool operator ==(Int32ValueObject left, Int32ValueObject right) => left.Equals(right); + public static bool operator !=(Int32ValueObject left, Int32ValueObject right) => !left.Equals(right); + + public static bool operator ==(Int32ValueObject left, global::System.String right) => left.Equals(right); + public static bool operator !=(Int32ValueObject left, global::System.String right) => !left.Equals(right); + + public static bool operator ==(global::System.String left, Int32ValueObject right) => right.Equals(left); + public static bool operator !=(global::System.String left, Int32ValueObject right) => !right.Equals(left); + + public static explicit operator Int32ValueObject(global::System.String value) => From(value); + public static explicit operator global::System.String(Int32ValueObject value) => value.Value; + + public static global::.Random GetRandom() => new global::.Random(); + public static Int32ValueObject NewRandomValue() + { + var random = GetRandom(); + var size = global::System.Runtime.CompilerServices.Unsafe.SizeOf(); + global::System.Span bytes = stackalloc byte[size]; + random.NextBytes(bytes); + var id = global::System.Runtime.InteropServices.MemoryMarshal.Cast(bytes)[0]; + return Int32ValueObject.From(id); + } + + public global::System.Int32 CompareTo(Int32ValueObject other) => Value.CompareTo(other); + public static bool operator <(Int32ValueObject left, Int32ValueObject right) => left.Value.CompareTo(right.Value) < 0; + public static bool operator >(Int32ValueObject left, Int32ValueObject right) => left.Value.CompareTo(right.Value) > 0; + public static bool operator <=(Int32ValueObject left, Int32ValueObject right) => left.Value.CompareTo(right.Value) <= 0; + public static bool operator >=(Int32ValueObject left, Int32ValueObject right) => left.Value.CompareTo(right.Value) >= 0; + + public static bool operator <(global::System.String left, Int32ValueObject right) => left.CompareTo(right.Value) < 0; + public static bool operator >(global::System.String left, Int32ValueObject right) => left.CompareTo(right.Value) > 0; + public static bool operator <=(global::System.String left, Int32ValueObject right) => left.CompareTo(right.Value) <= 0; + public static bool operator >=(global::System.String left, Int32ValueObject right) => left.CompareTo(right.Value) >= 0; + + public static bool operator <(Int32ValueObject left, global::System.String right) => left.Value.CompareTo(right) < 0; + public static bool operator >(Int32ValueObject left, global::System.String right) => left.Value.CompareTo(right) > 0; + public static bool operator <=(Int32ValueObject left, global::System.String right) => left.Value.CompareTo(right) <= 0; + public static bool operator >=(Int32ValueObject left, global::System.String right) => left.Value.CompareTo(right) >= 0; + +} +"""; + + [Fact] + public void TestAugment() + { + TestHelpers.TestGenerator(Input, "Int32ValueObject.g.cs", Output); + } +} diff --git a/tests/TransparentValueObjects.Tests/ValueObjectIncrementalSourceGeneratorTests/Augments/HasRandomValueGenerator_WithUnmanagedAndRandom.cs b/tests/TransparentValueObjects.Tests/ValueObjectIncrementalSourceGeneratorTests/Augments/HasRandomValueGenerator_WithUnmanagedAndRandom.cs new file mode 100644 index 0000000..1524130 --- /dev/null +++ b/tests/TransparentValueObjects.Tests/ValueObjectIncrementalSourceGeneratorTests/Augments/HasRandomValueGenerator_WithUnmanagedAndRandom.cs @@ -0,0 +1,113 @@ +using Xunit; + +namespace TransparentValueObjects.Tests.ValueObjectIncrementalSourceGeneratorTests.Augments; + +public class HasRandomValueGenerator_WithUnmanagedAndRandom +{ + private const string Input = +""" +using TransparentValueObjects.Generated; +using TransparentValueObjects.Augments; + +namespace TestNamespace; + +[ValueObject] +public readonly partial struct Int32ValueObject : IHasUnmanagedRandomValueGenerator +{ + public static Random GetRandom() => Random.Shared; +} +"""; + + private const string Output = +""" +// +#nullable enable +namespace TestNamespace; + +[global::System.Diagnostics.DebuggerDisplay("{Value}")] +[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage(Justification = "Auto-generated.")] +readonly partial struct Int32ValueObject : + global::TransparentValueObjects.Augments.IValueObject, + global::System.IEquatable, + global::System.IEquatable, + global::System.IComparable +{ + public readonly global::System.String Value; + + public static global::System.Type InnerValueType => typeof(global::System.String); + + [global::System.Obsolete($"Use Int32ValueObject.{nameof(From)} instead.", error: true)] + public Int32ValueObject() + { + throw new global::System.InvalidOperationException($"Use Int32ValueObject.{nameof(From)} instead."); + } + + private Int32ValueObject(global::System.String value) + { + Value = value; + } + + public static Int32ValueObject From(global::System.String value) => new(value); + + public override int GetHashCode() => Value.GetHashCode(); + + public override string ToString() => Value.ToString(); + + public bool Equals(Int32ValueObject other) => Equals(other.Value); + public bool Equals(global::System.String? other) => Value.Equals(other); + public bool Equals(Int32ValueObject other, global::System.Collections.Generic.IEqualityComparer comparer) => comparer.Equals(Value, other.Value); + public override bool Equals(object? obj) + { + if (obj is null) return false; + if (obj is Int32ValueObject value) return Equals(value); + if (obj is global::System.String innerValue) return Equals(innerValue); + return false; + } + + public static bool operator ==(Int32ValueObject left, Int32ValueObject right) => left.Equals(right); + public static bool operator !=(Int32ValueObject left, Int32ValueObject right) => !left.Equals(right); + + public static bool operator ==(Int32ValueObject left, global::System.String right) => left.Equals(right); + public static bool operator !=(Int32ValueObject left, global::System.String right) => !left.Equals(right); + + public static bool operator ==(global::System.String left, Int32ValueObject right) => right.Equals(left); + public static bool operator !=(global::System.String left, Int32ValueObject right) => !right.Equals(left); + + public static explicit operator Int32ValueObject(global::System.String value) => From(value); + public static explicit operator global::System.String(Int32ValueObject value) => value.Value; + + public static Int32ValueObject NewRandomValue() + { + var random = GetRandom(); + var size = global::System.Runtime.CompilerServices.Unsafe.SizeOf(); + global::System.Span bytes = stackalloc byte[size]; + random.NextBytes(bytes); + var id = global::System.Runtime.InteropServices.MemoryMarshal.Cast(bytes)[0]; + return Int32ValueObject.From(id); + } + + public global::System.Int32 CompareTo(Int32ValueObject other) => Value.CompareTo(other); + public static bool operator <(Int32ValueObject left, Int32ValueObject right) => left.Value.CompareTo(right.Value) < 0; + public static bool operator >(Int32ValueObject left, Int32ValueObject right) => left.Value.CompareTo(right.Value) > 0; + public static bool operator <=(Int32ValueObject left, Int32ValueObject right) => left.Value.CompareTo(right.Value) <= 0; + public static bool operator >=(Int32ValueObject left, Int32ValueObject right) => left.Value.CompareTo(right.Value) >= 0; + + public static bool operator <(global::System.String left, Int32ValueObject right) => left.CompareTo(right.Value) < 0; + public static bool operator >(global::System.String left, Int32ValueObject right) => left.CompareTo(right.Value) > 0; + public static bool operator <=(global::System.String left, Int32ValueObject right) => left.CompareTo(right.Value) <= 0; + public static bool operator >=(global::System.String left, Int32ValueObject right) => left.CompareTo(right.Value) >= 0; + + public static bool operator <(Int32ValueObject left, global::System.String right) => left.Value.CompareTo(right) < 0; + public static bool operator >(Int32ValueObject left, global::System.String right) => left.Value.CompareTo(right) > 0; + public static bool operator <=(Int32ValueObject left, global::System.String right) => left.Value.CompareTo(right) <= 0; + public static bool operator >=(Int32ValueObject left, global::System.String right) => left.Value.CompareTo(right) >= 0; + +} +"""; + + [Fact] + public void TestAugment() + { + TestHelpers.TestGenerator(Input, "Int32ValueObject.g.cs", Output); + } +}