From 917ca51abeafee2712c7688c7f9467c7b81cfb40 Mon Sep 17 00:00:00 2001 From: Steve Dunn Date: Tue, 16 Jul 2024 09:55:47 +0100 Subject: [PATCH] Add normalization support for implicit casting The code is updated to integrate support for normalization when casting implicitly. During this implicit casting process, the 'NormalizeInput' method is now called if it is available in the respective class. Corresponding test cases have been added to validate this behavior. --- src/Vogen/GenerateCastingOperators.cs | 25 ++++++++++++---- tests/ConsumerTests/BugFix624.cs | 36 +++++++++++++++++++++++ tests/ConsumerTests/Casting/ForClasses.cs | 16 ++++++++++ tests/ConsumerTests/Casting/ForStructs.cs | 17 +++++++++++ tests/ConsumerTests/Casting/Types.cs | 12 ++++++++ 5 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 tests/ConsumerTests/BugFix624.cs diff --git a/src/Vogen/GenerateCastingOperators.cs b/src/Vogen/GenerateCastingOperators.cs index 63013b04ef..3bd42647d1 100644 --- a/src/Vogen/GenerateCastingOperators.cs +++ b/src/Vogen/GenerateCastingOperators.cs @@ -41,12 +41,25 @@ public static string GenerateImplementations(VoWorkItem item, TypeDeclarationSyn if (item.Config.FromPrimitiveCasting == CastOperator.Implicit) { - sb.AppendLine( - $$""" - public static implicit operator {{className}}({{itemUnderlyingType}} value) { - return new {{className}}(value); - } - """); + if (item.NormalizeInputMethod is not null) + { + sb.AppendLine( + $$""" + public static implicit operator {{className}}({{itemUnderlyingType}} value) { + return new {{className}}({{className}}.NormalizeInput(value)); + } + """); + + } + else + { + sb.AppendLine( + $$""" + public static implicit operator {{className}}({{itemUnderlyingType}} value) { + return new {{className}}(value); + } + """); + } } if (sb.Length == 0) diff --git a/tests/ConsumerTests/BugFix624.cs b/tests/ConsumerTests/BugFix624.cs new file mode 100644 index 0000000000..3458d75c64 --- /dev/null +++ b/tests/ConsumerTests/BugFix624.cs @@ -0,0 +1,36 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; +using SystemTextJsonSerializer = System.Text.Json.JsonSerializer; + +namespace ConsumerTests.BugFixTests.BugFix639; + +[ValueObject(fromPrimitiveCasting: CastOperator.Implicit)] +public partial class C_With +{ + private static string NormalizeInput(string input) => input.ToUpper(); +} + +[ValueObject(fromPrimitiveCasting: CastOperator.Implicit)] +public partial class C_Without; + +/// +/// Fixes bug https://github.com/SteveDunn/Vogen/issues/639 where any +/// `NormalizeInput` method was not called when implicitly converting a primitive to a value object +/// +public class Tests +{ + [Fact] + public void Should_call_if_present() + { + C_With vo = "abc"; + vo.Value.Should().Be("ABC"); + } + + [Fact] + public void Should_not_call_if_not_present() + { + C_Without vo = "abc"; + vo.Value.Should().Be("abc"); + } +} diff --git a/tests/ConsumerTests/Casting/ForClasses.cs b/tests/ConsumerTests/Casting/ForClasses.cs index edf0632f48..7893e17216 100644 --- a/tests/ConsumerTests/Casting/ForClasses.cs +++ b/tests/ConsumerTests/Casting/ForClasses.cs @@ -46,4 +46,20 @@ public void Implicit_both_ways() Class_implicit_both_ways vo2 = prim; vo2.Should().Be(vo); } + + [Fact] + public void Implicit_both_ways_with_normalization() + { + using var _ = new AssertionScope(); + + var vo = Class_implicit_both_ways_with_normalization.From("abc"); + + string prim = vo; + + prim.Should().Be(vo.Value); + + Class_implicit_both_ways_with_normalization vo2 = prim; + vo2.Should().Be(vo); + vo2.Value.Should().Be("ABC"); + } } diff --git a/tests/ConsumerTests/Casting/ForStructs.cs b/tests/ConsumerTests/Casting/ForStructs.cs index 9e505fce8a..d358482404 100644 --- a/tests/ConsumerTests/Casting/ForStructs.cs +++ b/tests/ConsumerTests/Casting/ForStructs.cs @@ -46,4 +46,21 @@ public void Implicit_both_ways() Struct_implicit_both_ways vo2 = prim; vo2.Should().Be(vo); } + + [Fact] + public void Implicit_both_ways_with_normalization() + { + using var _ = new AssertionScope(); + + var vo = Struct_implicit_both_ways_with_normalization.From("abc"); + + string prim = vo; + + prim.Should().Be(vo.Value); + + Struct_implicit_both_ways_with_normalization vo2 = prim; + vo2.Should().Be(vo); + vo2.Value.Should().Be("ABC"); + } + } diff --git a/tests/ConsumerTests/Casting/Types.cs b/tests/ConsumerTests/Casting/Types.cs index 566700d95b..e4875a347e 100644 --- a/tests/ConsumerTests/Casting/Types.cs +++ b/tests/ConsumerTests/Casting/Types.cs @@ -15,6 +15,12 @@ public partial class Class_implicit_both_ways { } +[ValueObject(typeof(string), toPrimitiveCasting: CastOperator.Implicit, fromPrimitiveCasting: CastOperator.Implicit)] +public partial class Class_implicit_both_ways_with_normalization +{ + private static string NormalizeInput(string input) => input.ToUpper(); +} + [ValueObject(toPrimitiveCasting: CastOperator.None, fromPrimitiveCasting: CastOperator.None)] public partial class Class_default_generic { @@ -35,6 +41,12 @@ public partial class Struct_implicit_both_ways { } +[ValueObject(typeof(string), toPrimitiveCasting: CastOperator.Implicit, fromPrimitiveCasting: CastOperator.Implicit)] +public partial class Struct_implicit_both_ways_with_normalization +{ + private static string NormalizeInput(string input) => input.ToUpper(); +} + [ValueObject(toPrimitiveCasting: CastOperator.None, fromPrimitiveCasting: CastOperator.None)] public partial class Struct_default_generic {