From cd60530c0332b5e48e10686181a1ee7b17b8bb92 Mon Sep 17 00:00:00 2001 From: Gregory Nikolaishvili Date: Mon, 1 Jun 2026 12:07:06 +0400 Subject: [PATCH] Enhance single-property handling in Executor class Updated `Executor` to optimize handling of single-property cases: - Adjusted private field generation and property setters. - Updated `ShouldSerialize` to return `true` for single-property cases. - Improved `ProcessMatch` and `ProcessSwitch` methods. Renamed `SinglePropertyChoice` to `SinglePropertyStringChoice` and updated related tests. Added new classes `SinglePropertyDateOnlyChoice` and `SinglePropertyIntChoice` with required `Value` properties. Incremented `Version` in `Directory.Build.props` to `2.1.4`. --- Directory.Build.props | 2 +- src/AltaSoft.Choice.Generator/Executor.cs | 43 +++++++++++++------ .../ChoiceGeneratorTests.cs | 4 +- .../SinglePropertyChoice.cs | 2 +- .../SinglePropertyDateOnlyChoice.cs | 9 ++++ .../SinglePropertyIntChoice.cs | 9 ++++ 6 files changed, 52 insertions(+), 17 deletions(-) create mode 100644 tests/AltaSoft.ChoiceGenerator.Tests/SinglePropertyDateOnlyChoice.cs create mode 100644 tests/AltaSoft.ChoiceGenerator.Tests/SinglePropertyIntChoice.cs diff --git a/Directory.Build.props b/Directory.Build.props index 383c9dd..279f9d9 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -10,7 +10,7 @@ Choice generator ALTA Software llc. Copyright © 2024 ALTA Software llc. - 2.0.0 + 2.1.4 diff --git a/src/AltaSoft.Choice.Generator/Executor.cs b/src/AltaSoft.Choice.Generator/Executor.cs index bd38259..2e82cc1 100644 --- a/src/AltaSoft.Choice.Generator/Executor.cs +++ b/src/AltaSoft.Choice.Generator/Executor.cs @@ -100,7 +100,12 @@ private static SourceCodeBuilder Process(INamedTypeSymbol typeSymbol, List x.Name != p.Name).Select(v => $"{v.Name.ToFieldName()} = null;")) - .Append("ChoiceType = ChoiceOf.").Append(p.Name).AppendLine(";") - .CloseBracket() - .CloseBracket(); + .Append(fieldName); + if (isOnly1Property) + sb.AppendLine(" = value;"); + else + sb.AppendLine(" = value ?? throw new InvalidOperationException(\"Choice value cannot be null\");"); + sb.AppendLines(processedProperties.Where(x => x.Name != p.Name).Select(v => $"{v.Name.ToFieldName()} = null;")) + .Append("ChoiceType = ChoiceOf.").Append(p.Name).AppendLine(";") + .CloseBracket() + .CloseBracket(); sb.NewLine(); @@ -179,9 +188,12 @@ private static SourceCodeBuilder Process(INamedTypeSymbol typeSymbol, Listtrue if has a value; otherwise, false."); sb.AppendLine("[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]"); - sb.Append("public bool ShouldSerialize").Append(p).Append("() => ").Append(p) - .AppendLine(".HasValue;") - .NewLine(); + sb.Append("public bool ShouldSerialize").Append(p).Append("() => "); + if (isOnly1Property) + sb.AppendLine("true;"); + else + sb.Append(p).AppendLine(".HasValue;"); + sb.NewLine(); } sb.AppendSummary("Choice enumeration"); @@ -252,7 +264,6 @@ private static bool ProcessImplicitOperators(SourceCodeBuilder sb, string typeNa private static void ProcessMatch(SourceCodeBuilder sb, List processedProperties) { - sb.AppendSummary("Applies the appropriate function based on the current choice type"); sb.AppendTypeParamDescription("TResult", "The return type of the provided match functions"); processedProperties.ForEach(x => @@ -276,10 +287,13 @@ private static void ProcessMatch(SourceCodeBuilder sb, List pro sb.AppendLine("return ChoiceType switch") .OpenBracket(); + + var isOnlyProperty = processedProperties.Count == 1; + foreach (var prop in processedProperties) { sb.Append($"ChoiceOf.{prop.Name} => match").Append($"{prop.Name}({prop.Name}!") - .AppendIf(prop.TypeSymbol.IsValueType, ".Value") + .AppendIf(!isOnlyProperty && prop.TypeSymbol.IsValueType, ".Value") .AppendLine("),"); } @@ -291,7 +305,6 @@ private static void ProcessMatch(SourceCodeBuilder sb, List pro private static void ProcessSwitch(SourceCodeBuilder sb, List processedProperties) { - sb.AppendSummary("Applies the appropriate Action based on the current choice type"); processedProperties.ForEach(x => { @@ -314,11 +327,14 @@ private static void ProcessSwitch(SourceCodeBuilder sb, List pr sb.AppendLine("switch (ChoiceType)") .OpenBracket(); + + var isOnlyProperty = processedProperties.Count == 1; + foreach (var prop in processedProperties) { sb.AppendSwitchCase($"ChoiceOf.{prop.Name}") .Append($"match{prop.Name}({prop.Name}!") - .AppendIf(prop.TypeSymbol.IsValueType, ".Value") + .AppendIf(!isOnlyProperty && prop.TypeSymbol.IsValueType, ".Value") .AppendLine(");") .AppendLine("return;") .CloseSwitchCase() @@ -345,3 +361,4 @@ private static void ProcessSwitch(SourceCodeBuilder sb, List pr "System.Xml.Schema" ]; } + diff --git a/tests/AltaSoft.ChoiceGenerator.Tests/ChoiceGeneratorTests.cs b/tests/AltaSoft.ChoiceGenerator.Tests/ChoiceGeneratorTests.cs index 892b41d..2049c5c 100644 --- a/tests/AltaSoft.ChoiceGenerator.Tests/ChoiceGeneratorTests.cs +++ b/tests/AltaSoft.ChoiceGenerator.Tests/ChoiceGeneratorTests.cs @@ -303,9 +303,9 @@ public void ImplicitConversions_ShouldConvertCorrectly() [Fact] public void GenerateSinglePropertyChoice() { - var choice = SinglePropertyChoice.CreateAsValue("value"); + var choice = SinglePropertyStringChoice.CreateAsValue("value"); - Assert.Equal(SinglePropertyChoice.ChoiceOf.Value, choice.ChoiceType); + Assert.Equal(SinglePropertyStringChoice.ChoiceOf.Value, choice.ChoiceType); Assert.Equal("value", choice.Value); var matched = choice.Match(_ => "ok"); diff --git a/tests/AltaSoft.ChoiceGenerator.Tests/SinglePropertyChoice.cs b/tests/AltaSoft.ChoiceGenerator.Tests/SinglePropertyChoice.cs index c96725c..530c677 100644 --- a/tests/AltaSoft.ChoiceGenerator.Tests/SinglePropertyChoice.cs +++ b/tests/AltaSoft.ChoiceGenerator.Tests/SinglePropertyChoice.cs @@ -3,7 +3,7 @@ namespace AltaSoft.ChoiceGenerator.Tests; [Choice] -public sealed partial class SinglePropertyChoice +public sealed partial class SinglePropertyStringChoice { public required partial string Value { get; set; } } diff --git a/tests/AltaSoft.ChoiceGenerator.Tests/SinglePropertyDateOnlyChoice.cs b/tests/AltaSoft.ChoiceGenerator.Tests/SinglePropertyDateOnlyChoice.cs new file mode 100644 index 0000000..aa444dc --- /dev/null +++ b/tests/AltaSoft.ChoiceGenerator.Tests/SinglePropertyDateOnlyChoice.cs @@ -0,0 +1,9 @@ +using AltaSoft.Choice; + +namespace AltaSoft.ChoiceGenerator.Tests; + +[Choice] +public sealed partial class SinglePropertyDateOnlyChoice +{ + public required partial string Value { get; set; } +} diff --git a/tests/AltaSoft.ChoiceGenerator.Tests/SinglePropertyIntChoice.cs b/tests/AltaSoft.ChoiceGenerator.Tests/SinglePropertyIntChoice.cs new file mode 100644 index 0000000..96e1c94 --- /dev/null +++ b/tests/AltaSoft.ChoiceGenerator.Tests/SinglePropertyIntChoice.cs @@ -0,0 +1,9 @@ +using AltaSoft.Choice; + +namespace AltaSoft.ChoiceGenerator.Tests; + +[Choice] +public sealed partial class SinglePropertyIntChoice +{ + public required partial int Value { get; set; } +}