From c27d667a6de1185c20600fc67ce4ee6793db2f3c Mon Sep 17 00:00:00 2001 From: Gregory Nikolaishvili Date: Sat, 30 May 2026 16:13:34 +0400 Subject: [PATCH] Upgrade dependencies and refactor codebase Upgraded package versions in `Directory.Packages.props` to ensure compatibility and improve functionality. Enhanced `clean.bat` with better logging and comments. Refactored `Executor.cs` and `CompilationExt.cs` to handle single-property cases and improve modifier handling. Removed `RoslynExt.cs` as redundant. Added `Modifiers` property to `PropertyDetails` and introduced a new `SinglePropertyChoice` class. Simplified `DateTypeChoice.cs`, `TwoSameTypeChoice.cs`, and `TwoValueTypeChoice.cs`. Added a new test case `GenerateSinglePropertyChoice` to validate single-property behavior. --- Directory.Packages.props | 17 ++++--- clean.bat | 26 ++++++++-- src/AltaSoft.Choice.Generator/Executor.cs | 14 ++++-- .../Extensions/CompilationExt.cs | 48 +++++++------------ .../Extensions/RoslynExt.cs | 48 ------------------- .../Models/PropertyDetails.cs | 17 ++++--- .../ChoiceGeneratorTests.cs | 15 ++++++ .../DateTypeChoice.cs | 25 +++++----- .../SinglePropertyChoice.cs | 9 ++++ .../TwoSameTypeChoice.cs | 2 - .../TwoValueTypeChoice.cs | 26 +++++----- 11 files changed, 113 insertions(+), 134 deletions(-) delete mode 100644 src/AltaSoft.Choice.Generator/Extensions/RoslynExt.cs create mode 100644 tests/AltaSoft.ChoiceGenerator.Tests/SinglePropertyChoice.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 8ce6066..77eab94 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -2,17 +2,16 @@ true - - - - - + + + + - + - - + + - + \ No newline at end of file diff --git a/clean.bat b/clean.bat index 97b2bab..95285a9 100644 --- a/clean.bat +++ b/clean.bat @@ -1,6 +1,24 @@ -FOR /F "tokens=*" %%G IN ('DIR /B /AD /S bin') DO RMDIR /S /Q "%%G" -FOR /F "tokens=*" %%G IN ('DIR /B /AD /S obj') DO RMDIR /S /Q "%%G" -FOR /F "tokens=*" %%G IN ('DIR /B /AD /S log') DO RMDIR /S /Q "%%G" +@echo off -rem nuget locals all -clear +:: Delete all bin directories and their contents recursively +FOR /F "tokens=*" %%G IN ('DIR /B /AD /S bin') DO ( + ECHO Deleting directory and contents: "%%G" + RMDIR /S /Q "%%G" +) + +:: Delete all obj directories and their contents recursively +FOR /F "tokens=*" %%G IN ('DIR /B /AD /S obj') DO ( + ECHO Deleting directory and contents: "%%G" + RMDIR /S /Q "%%G" +) + +:: Delete all log directories and their contents recursively +FOR /F "tokens=*" %%G IN ('DIR /B /AD /S log') DO ( + ECHO Deleting directory and contents: "%%G" + RMDIR /S /Q "%%G" +) + +@echo on + +:: rem nuget locals all -clear diff --git a/src/AltaSoft.Choice.Generator/Executor.cs b/src/AltaSoft.Choice.Generator/Executor.cs index 354c734..bd38259 100644 --- a/src/AltaSoft.Choice.Generator/Executor.cs +++ b/src/AltaSoft.Choice.Generator/Executor.cs @@ -94,9 +94,10 @@ private static SourceCodeBuilder Process(INamedTypeSymbol typeSymbol, List ") - .Append(fieldName).AppendLine(";"); + .Append(fieldName).AppendLine(";"); sb.AppendIfNotEmpty(p.SetterAccessibility.GetPropertyAccessibilityString()).AppendLine("set") .OpenBracket() @@ -210,12 +212,14 @@ private static PropertyDetails ProcessProperty(IPropertySymbol propertySymbol) var typeFullName = propertySymbol.Type.GetFullName(); var propertyName = propertySymbol.Name; + var modifiers = propertySymbol.GetModifiers(); return new PropertyDetails( name: propertyName, typeName: typeFullName.Replace("?", ""), @namespace: propertySymbol.ContainingNamespace.ToDisplayString(), xmlNameValue: xmlElementName, + modifiers: modifiers, summary: propertySymbol.GetSummaryText(), getterAccessibility: propertySymbol.GetMethod?.DeclaredAccessibility ?? Accessibility.NotApplicable, setterAccessibility: propertySymbol.SetMethod?.DeclaredAccessibility ?? Accessibility.NotApplicable, diff --git a/src/AltaSoft.Choice.Generator/Extensions/CompilationExt.cs b/src/AltaSoft.Choice.Generator/Extensions/CompilationExt.cs index e263bbd..416f50d 100644 --- a/src/AltaSoft.Choice.Generator/Extensions/CompilationExt.cs +++ b/src/AltaSoft.Choice.Generator/Extensions/CompilationExt.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -92,57 +91,45 @@ public static IEnumerable GetMembersOfType(this ITypeSymbol? s /// /// Gets the modifiers for the named type symbol. /// - /// The named type symbol to retrieve modifiers from. - /// The modifiers as a string, or null if the type is null or has no modifiers. - public static string? GetModifiers(this INamedTypeSymbol? self) + public static string? GetModifiers(this INamedTypeSymbol self) { - var declaringSyntax = self?.DeclaringSyntaxReferences; - if (self is null || declaringSyntax is null or { Length: 0 }) + var declaringSyntax = self.DeclaringSyntaxReferences; + if (declaringSyntax is { Length: 0 }) return null; foreach (var syntax in declaringSyntax) { - if (syntax.GetSyntax() is TypeDeclarationSyntax typeDeclaration && string.Equals(typeDeclaration.GetClassName(), self.GetClassNameWithArguments(), StringComparison.Ordinal)) + if (syntax.GetSyntax() is TypeDeclarationSyntax typeDeclaration) { var modifiers = typeDeclaration.Modifiers.ToString(); return modifiers; } } - return null; } - #endregion Accessibility /// - /// Gets the class name including generic arguments as a string. + /// Gets the modifiers for the property type symbol. /// - /// The named type symbol to get the class name from. - /// The class name including generic arguments as a string. - public static string GetClassNameWithArguments(this INamedTypeSymbol? type) + public static string GetModifiers(this IPropertySymbol self) { - if (type is null) - return string.Empty; - - var builder = new StringBuilder(type.Name); + var declaringSyntax = self.DeclaringSyntaxReferences; + if (declaringSyntax is { Length: 0 }) + return "public"; - if (type.TypeArguments.Length == 0) - return builder.ToString(); - - builder.Append('<'); - for (var index = 0; index < type.TypeArguments.Length; index++) + foreach (var syntax in declaringSyntax) { - var arg = type.TypeArguments[index]; - builder.Append(arg.Name); - - if (index != type.TypeArguments.Length - 1) - builder.Append(", "); + if (syntax.GetSyntax() is PropertyDeclarationSyntax propertyDeclarationSyntax) + { + return propertyDeclarationSyntax.Modifiers.ToString(); + } } + return "public"; + } - builder.Append('>'); + #endregion Accessibility - return builder.ToString(); - } #pragma warning disable S1643 public static string GetFullName(this ITypeSymbol type) { @@ -183,6 +170,7 @@ public static string GetFullName(this ITypeSymbol type) return ns + '.' + friendlyName; } #pragma warning restore S1643 + /// /// Determines whether the specified type symbol is a Nullable<T> value type and, if so, provides the underlying value type symbol. /// diff --git a/src/AltaSoft.Choice.Generator/Extensions/RoslynExt.cs b/src/AltaSoft.Choice.Generator/Extensions/RoslynExt.cs deleted file mode 100644 index ad2dc85..0000000 --- a/src/AltaSoft.Choice.Generator/Extensions/RoslynExt.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Microsoft.CodeAnalysis.CSharp.Syntax; - -// https://www.meziantou.net/working-with-types-in-a-roslyn-analyzer.htm - -namespace AltaSoft.Choice.Generator.Extensions; - -/// -/// A collection of extension methods for working with Roslyn syntax and symbols. -/// -internal static class RoslynExt -{ - /// - /// Gets the namespace of the specified type declaration syntax. - /// - /// The type declaration syntax to retrieve the namespace from. - /// The namespace of the type declaration or null if not found. - public static string? GetNamespace(this TypeDeclarationSyntax self) - { - return self.Parent is not BaseNamespaceDeclarationSyntax ns ? null : ns.GetNamespace(); - } - - /// - /// Gets the namespace from the specified base namespace declaration syntax. - /// - /// The base namespace declaration syntax to retrieve the namespace from. - /// The namespace from the base namespace declaration. - public static string GetNamespace(this BaseNamespaceDeclarationSyntax self) => self.Name.ToString(); - - /// - /// Gets the fully qualified name of the specified type declaration syntax, including its namespace. - /// - /// The type declaration syntax to retrieve the fully qualified name from. - /// The fully qualified name of the type declaration. - public static string GetClassFullName(this TypeDeclarationSyntax self) - { - return self.GetNamespace() + "." + self.GetClassName(); - } - - /// - /// Gets the name of the class specified in the type declaration syntax. - /// - /// The type declaration syntax to retrieve the class name from. - /// The name of the class. - public static string GetClassName(this TypeDeclarationSyntax proxy) - { - return proxy.Identifier.Text + proxy.TypeParameterList?.ToFullString(); - } -} diff --git a/src/AltaSoft.Choice.Generator/Models/PropertyDetails.cs b/src/AltaSoft.Choice.Generator/Models/PropertyDetails.cs index 1f42306..ee696a3 100644 --- a/src/AltaSoft.Choice.Generator/Models/PropertyDetails.cs +++ b/src/AltaSoft.Choice.Generator/Models/PropertyDetails.cs @@ -25,6 +25,11 @@ internal sealed class PropertyDetails /// internal string XmlNameValue { get; private set; } + /// + /// The access modifiers for the property, such as "public", "private", etc. + /// + public string Modifiers { get; set; } + /// /// The summary documentation for the property. /// @@ -52,22 +57,16 @@ internal bool IsDateOnly() { return TypeSymbol.IsValueType && TypeSymbol.GetFullName().Replace("?", "") == "System.DateOnly"; } + /// /// Initializes a new instance of the class. /// - /// The name of the property. - /// The type name of the property. - /// The namespace of the property. - /// The XML name value of the property. - /// The summary documentation for the property. - /// The access modifiers for the property's getter. - /// The access modifiers for the property's setter. - /// The type symbol of the property. public PropertyDetails( string name, string typeName, string @namespace, string xmlNameValue, + string modifiers, string? summary, Accessibility getterAccessibility, Accessibility setterAccessibility, @@ -77,10 +76,10 @@ public PropertyDetails( TypeName = typeName; Namespace = @namespace; XmlNameValue = xmlNameValue; + Modifiers = modifiers; Summary = summary; GetterAccessibility = getterAccessibility; SetterAccessibility = setterAccessibility; TypeSymbol = typeSymbol; } - } diff --git a/tests/AltaSoft.ChoiceGenerator.Tests/ChoiceGeneratorTests.cs b/tests/AltaSoft.ChoiceGenerator.Tests/ChoiceGeneratorTests.cs index aa09a64..892b41d 100644 --- a/tests/AltaSoft.ChoiceGenerator.Tests/ChoiceGeneratorTests.cs +++ b/tests/AltaSoft.ChoiceGenerator.Tests/ChoiceGeneratorTests.cs @@ -299,5 +299,20 @@ public void ImplicitConversions_ShouldConvertCorrectly() Assert.Equal("1234", choice.Proprietary?.Other); } + + [Fact] + public void GenerateSinglePropertyChoice() + { + var choice = SinglePropertyChoice.CreateAsValue("value"); + + Assert.Equal(SinglePropertyChoice.ChoiceOf.Value, choice.ChoiceType); + Assert.Equal("value", choice.Value); + + var matched = choice.Match(_ => "ok"); + Assert.Equal("ok", matched); + + var switched = choice.Match(x => "matched"); + Assert.Equal("matched", switched); + } } diff --git a/tests/AltaSoft.ChoiceGenerator.Tests/DateTypeChoice.cs b/tests/AltaSoft.ChoiceGenerator.Tests/DateTypeChoice.cs index d3009b0..8ef77d8 100644 --- a/tests/AltaSoft.ChoiceGenerator.Tests/DateTypeChoice.cs +++ b/tests/AltaSoft.ChoiceGenerator.Tests/DateTypeChoice.cs @@ -1,19 +1,18 @@ using System; using AltaSoft.Choice; -namespace AltaSoft.ChoiceGenerator.Tests +namespace AltaSoft.ChoiceGenerator.Tests; + +[Choice] +public sealed partial record DateTypeChoice { - [Choice] - public sealed partial record DateTypeChoice - { - /// - /// Specifies the authorisation, in a coded form. - /// - public partial DateOnly? OnlyDate { get; set; } + /// + /// Specifies the authorisation, in a coded form. + /// + public partial DateOnly? OnlyDate { get; set; } - /// - /// Specifies the authorisation, in a free text form. - /// - public partial DateTime? DateTimeChoice { get; set; } - } + /// + /// Specifies the authorisation, in a free text form. + /// + public partial DateTime? DateTimeChoice { get; set; } } diff --git a/tests/AltaSoft.ChoiceGenerator.Tests/SinglePropertyChoice.cs b/tests/AltaSoft.ChoiceGenerator.Tests/SinglePropertyChoice.cs new file mode 100644 index 0000000..c96725c --- /dev/null +++ b/tests/AltaSoft.ChoiceGenerator.Tests/SinglePropertyChoice.cs @@ -0,0 +1,9 @@ +using AltaSoft.Choice; + +namespace AltaSoft.ChoiceGenerator.Tests; + +[Choice] +public sealed partial class SinglePropertyChoice +{ + public required partial string Value { get; set; } +} diff --git a/tests/AltaSoft.ChoiceGenerator.Tests/TwoSameTypeChoice.cs b/tests/AltaSoft.ChoiceGenerator.Tests/TwoSameTypeChoice.cs index 689dea1..b2f2a11 100644 --- a/tests/AltaSoft.ChoiceGenerator.Tests/TwoSameTypeChoice.cs +++ b/tests/AltaSoft.ChoiceGenerator.Tests/TwoSameTypeChoice.cs @@ -3,11 +3,9 @@ namespace AltaSoft.ChoiceGenerator.Tests; [Choice] - public sealed partial class TwoSameTypeChoice { public partial string? StringChoiceOne { get; set; } public partial string? StringChoiceTwo { get; set; } - } diff --git a/tests/AltaSoft.ChoiceGenerator.Tests/TwoValueTypeChoice.cs b/tests/AltaSoft.ChoiceGenerator.Tests/TwoValueTypeChoice.cs index de39feb..00d734f 100644 --- a/tests/AltaSoft.ChoiceGenerator.Tests/TwoValueTypeChoice.cs +++ b/tests/AltaSoft.ChoiceGenerator.Tests/TwoValueTypeChoice.cs @@ -1,20 +1,18 @@ using AltaSoft.Choice; using AltaSoft.ChoiceGenerator.Tests.OtherNamespace; -namespace AltaSoft.ChoiceGenerator.Tests -{ - [Choice] - public sealed partial record TwoValueTypeChoice - { - /// - /// Specifies the authorisation, in a coded form. - /// - public partial Authorisation1Code? Code { get; set; } +namespace AltaSoft.ChoiceGenerator.Tests; - /// - /// Specifies the authorisation, in a free text form. - /// - public partial int? Integer { get; set; } +[Choice] +public sealed partial record TwoValueTypeChoice +{ + /// + /// Specifies the authorisation, in a coded form. + /// + public partial Authorisation1Code? Code { get; set; } - } + /// + /// Specifies the authorisation, in a free text form. + /// + public partial int? Integer { get; set; } }