From 18f3c3635878403a13c237828069a8da85f5d3b6 Mon Sep 17 00:00:00 2001 From: Steven Maillet Date: Fri, 31 Oct 2025 12:27:58 -0700 Subject: [PATCH] Updated dependencies. * This generally impacts only the testing as the test adapter and framework are bumped. * Resolved MSTEST* analyzer warnings by adjusting code to use new APIs * Added parallelization optimizations for the test runs. - on a 24 Core machine this amounted to ~70% improvement in completion time - Though the total time was under 1s, it is now under 100ms * Adjusted repo wide targets to ensure `NuGet` forlder exists - This ensures that it exists for any restore so that it is considered as a valid source of packages. - This is useful for debugging/testing of CI variants of dependent packages * Made comparison CaseSensitive static class public so it is usable by callers that need explicit handling of potentially mixed sources. - This can lead to unexpected results even if consistent. The use of case in SemVer is sadly ambiguos and different major public repos (NuGet and npm as examples) have chosen different behaviors so users need to be clear on the behavior. Use of explicit overrides allows such a thing in the case of mixed case expectation. - Renamed `ResourcesExtensions.AsFormat()` -> `ResourcesExtensions.ParseAsFormat()` for greater clarity of intent/behavior. * Added use of `StringSyntaxAttribute` to resource extensions. * Added check for formatter support of ordering to ensure it reports an error message that hopefully makes sense. * General re-formatting and docs clarifications --- Directory.Build.targets | 6 +- Directory.Packages.props | 6 +- .../AssemblyInfo.cs | 10 +++ .../CSemVerCITests.cs | 16 ++-- .../CSemVerTests.cs | 16 ++-- src/Ubiquity.NET.Versioning.UT/SemVerTests.cs | 22 +++--- .../ValidationWithTask.cs | 2 +- .../Comparison/SemVerComparer.cs | 2 +- .../Properties/Resources.Designer.cs | 11 ++- .../Properties/Resources.resx | 6 +- .../Properties/ResourcesExtensions.cs | 14 ++-- src/Ubiquity.NET.Versioning/SemVer.cs | 74 ++++++++++--------- 12 files changed, 105 insertions(+), 80 deletions(-) diff --git a/Directory.Build.targets b/Directory.Build.targets index 621ae18..9cf7f21 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,6 +1,6 @@ - - - + + + diff --git a/Directory.Packages.props b/Directory.Packages.props index 68837f3..b9bbe36 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -18,9 +18,9 @@ - - - + + + diff --git a/src/Ubiquity.NET.Versioning.UT/AssemblyInfo.cs b/src/Ubiquity.NET.Versioning.UT/AssemblyInfo.cs index a97502f..2a0120e 100644 --- a/src/Ubiquity.NET.Versioning.UT/AssemblyInfo.cs +++ b/src/Ubiquity.NET.Versioning.UT/AssemblyInfo.cs @@ -8,6 +8,8 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; +using Microsoft.VisualStudio.TestTools.UnitTesting; + // In SDK-style projects such as this one, several assembly attributes that were historically // defined in this file are now automatically added during build and populated with // values defined in project properties. For details of which attributes are included @@ -24,3 +26,11 @@ [assembly: CLSCompliant( false )] [assembly: ExcludeFromCodeCoverage] + +// Resolve MSTEST001 - explicitly declare parallelization +// There are enough tests at the method level to warrant some +// level of parallelization. However, the class level is the +// optimal granularity for these tests. The method level ends +// up with more overhead per thread since the test methods are +// so small, diminishing the value of parallelization. +[assembly: Parallelize( Scope = ExecutionScope.ClassLevel )] diff --git a/src/Ubiquity.NET.Versioning.UT/CSemVerCITests.cs b/src/Ubiquity.NET.Versioning.UT/CSemVerCITests.cs index d15c00e..81858c8 100644 --- a/src/Ubiquity.NET.Versioning.UT/CSemVerCITests.cs +++ b/src/Ubiquity.NET.Versioning.UT/CSemVerCITests.cs @@ -205,8 +205,8 @@ public void CompareToTestsNoPrelNoMeta( ) var ver_name_1_same = new CSemVerCI( new CSemVer( 20, 1, 4 ), "BuildIndex01", "BuildName01" ); var ver_name_2 = new CSemVerCI( new CSemVer( 20, 1, 4 ), "BuildIndex01", "BuildName02" ); - Assert.IsTrue(ver_name_1.CompareTo(ver_name_2) < 0); - Assert.IsTrue(ver_name_2.CompareTo(ver_name_1) > 0); + Assert.IsLessThan( 0, ver_name_1.CompareTo( ver_name_2 ) ); + Assert.IsGreaterThan( 0, ver_name_2.CompareTo( ver_name_1 ) ); Assert.AreEqual(0, ver_name_1.CompareTo( ver_name_1_same )); Assert.AreEqual(0, ver_name_1_same.CompareTo( ver_name_1 )); @@ -221,8 +221,8 @@ public void CompareToTestsNoPrelWithMeta( ) var ver_name_1_same = new CSemVerCI( new CSemVer( 20, 1, 4, null, [ "buildMeta" ] ), "BuildIndex01", "BuildName01" ); var ver_name_2 = new CSemVerCI( new CSemVer( 20, 1, 4, null, [ "buildMeta" ] ), "BuildIndex01", "BuildName02" ); - Assert.IsTrue(ver_name_1.CompareTo(ver_name_2) < 0); - Assert.IsTrue(ver_name_2.CompareTo(ver_name_1) > 0); + Assert.IsLessThan( 0, ver_name_1.CompareTo( ver_name_2 ) ); + Assert.IsGreaterThan( 0, ver_name_2.CompareTo( ver_name_1 ) ); Assert.AreEqual(0, ver_name_1.CompareTo( ver_name_1_same )); Assert.AreEqual(0, ver_name_1_same.CompareTo( ver_name_1 )); @@ -238,8 +238,8 @@ public void CompareToTestsWithPrelNoMeta( ) var ver_name_1_same = new CSemVerCI( new CSemVer( 1, 2, 3, alpha_0_1 ), "BuildIndex01", "BuildName01" ); var ver_name_2 = new CSemVerCI( new CSemVer( 1, 2, 3, alpha_0_1 ), "BuildIndex01", "BuildName02" ); - Assert.IsTrue(ver_name_1.CompareTo(ver_name_2) < 0); - Assert.IsTrue(ver_name_2.CompareTo(ver_name_1) > 0); + Assert.IsLessThan( 0, ver_name_1.CompareTo( ver_name_2 ) ); + Assert.IsGreaterThan( 0, ver_name_2.CompareTo( ver_name_1 ) ); Assert.AreEqual(0, ver_name_1.CompareTo( ver_name_1_same )); Assert.AreEqual(0, ver_name_1_same.CompareTo( ver_name_1 )); @@ -257,8 +257,8 @@ public void CompareToTestsWithPrelWithMeta( ) var ver_name_1_same = new CSemVerCI( new CSemVer( 1, 2, 3, alpha_0_1, ["BuildMeta", "MoreMeta"] ), "BuildIndex01", "BuildName01" ); var ver_name_2 = new CSemVerCI( new CSemVer( 1, 2, 3, alpha_0_1, ["SomeOhterMeta"] ), "BuildIndex01", "BuildName02" ); - Assert.IsTrue(ver_name_1.CompareTo(ver_name_2) < 0); - Assert.IsTrue(ver_name_2.CompareTo(ver_name_1) > 0); + Assert.IsLessThan( 0, ver_name_1.CompareTo( ver_name_2 ) ); + Assert.IsGreaterThan( 0, ver_name_2.CompareTo( ver_name_1 ) ); Assert.AreEqual(0, ver_name_1.CompareTo( ver_name_1_same )); Assert.AreEqual(0, ver_name_1_same.CompareTo( ver_name_1 )); diff --git a/src/Ubiquity.NET.Versioning.UT/CSemVerTests.cs b/src/Ubiquity.NET.Versioning.UT/CSemVerTests.cs index 95f5bca..a2ef886 100644 --- a/src/Ubiquity.NET.Versioning.UT/CSemVerTests.cs +++ b/src/Ubiquity.NET.Versioning.UT/CSemVerTests.cs @@ -26,7 +26,7 @@ public void CSemVerTest( ) Assert.AreEqual( 3, ver.Patch ); Assert.IsFalse( ver.IsPrerelease ); Assert.IsFalse( ver.PrereleaseVersion.HasValue); - Assert.AreEqual( 0, ver.BuildMeta.Length); + Assert.IsEmpty( ver.BuildMeta); var preRelInfo = new PrereleaseVersion(1, 2, 3); ImmutableArray expectedMeta = ["buildMeta"]; @@ -48,7 +48,7 @@ public void DefaultConstructorTests( ) Assert.AreEqual( 0, ver.Patch ); Assert.IsFalse( ver.IsPrerelease ); Assert.IsFalse( ver.PrereleaseVersion.HasValue); - Assert.AreEqual( 0, ver.BuildMeta.Length); + Assert.IsEmpty( ver.BuildMeta); } [TestMethod] @@ -80,11 +80,11 @@ public void CompareToTest( ) var val = new CSemVer(1,2,3); var val2 = new CSemVer(1,2,3); var valp1 = new CSemVer(1,2,4); // "+1" - Assert.IsTrue(val.CompareTo(valm1) > 0, "[CompareTo] val > (val -1)"); - Assert.IsTrue(valm1.CompareTo(val) < 0, "[CompareTo] (val - 1) < val"); + Assert.IsGreaterThan( 0, val.CompareTo( valm1 ), "[CompareTo] val > (val -1)"); + Assert.IsLessThan( 0, valm1.CompareTo( val ), "[CompareTo] (val - 1) < val"); Assert.AreEqual(0, val.CompareTo(val2), "[CompareTo] val == val"); - Assert.IsTrue(val.CompareTo(valp1) < 0, "[CompareTo] val < (val + 1)"); - Assert.IsTrue(valp1.CompareTo(val) > 0, "[CompareTo] (val + 1) > val"); + Assert.IsLessThan( 0, val.CompareTo( valp1 ), "[CompareTo] val < (val + 1)"); + Assert.IsGreaterThan( 0, valp1.CompareTo( val ), "[CompareTo] (val + 1) > val"); // Ensure operator variants are correct // (They should internally use CompareTo, this verifies correct behavior @@ -149,7 +149,7 @@ public static void VerifyOrderedVersion( Assert.AreEqual(number, ver.PrereleaseVersion.Value.Number, exp); Assert.AreEqual(fix, ver.PrereleaseVersion.Value.Fix, exp); Assert.IsNotNull(ver.BuildMeta, $"non-nullable property should not be null for '{exp}'"); - Assert.AreEqual(0, ver.BuildMeta.Length, $"non-nullable property should be empty if not set for '{exp}'"); + Assert.IsEmpty( ver.BuildMeta, $"non-nullable property should be empty if not set for '{exp}'"); Assert.AreEqual(orderedVersion, ver.OrderedVersion , $"builds should have the same ordered version number provided for '{exp}'"); } @@ -168,7 +168,7 @@ public static void VerifyOrderedVersion( Assert.IsFalse(ver.PrereleaseVersion.HasValue, exp); Assert.IsFalse(ver.IsPrerelease, exp); Assert.IsNotNull(ver.BuildMeta, $"non-nullable property should not be null for '{exp}'"); - Assert.AreEqual(0, ver.BuildMeta.Length, $"non-nullable property should be empty if not set for '{exp}'"); + Assert.IsEmpty( ver.BuildMeta, $"non-nullable property should be empty if not set for '{exp}'"); Assert.AreEqual(orderedVersion, ver.OrderedVersion , $"should have the same ordered version number as provided for '{exp}'"); } } diff --git a/src/Ubiquity.NET.Versioning.UT/SemVerTests.cs b/src/Ubiquity.NET.Versioning.UT/SemVerTests.cs index a84f366..2a3a5cd 100644 --- a/src/Ubiquity.NET.Versioning.UT/SemVerTests.cs +++ b/src/Ubiquity.NET.Versioning.UT/SemVerTests.cs @@ -25,23 +25,23 @@ public void SemVerTest( ) Assert.AreEqual( 1, sv1.Major ); Assert.AreEqual( 2, sv1.Minor ); Assert.AreEqual( 3, sv1.Patch ); - Assert.AreEqual( 0, sv1.PreRelease.Length ); - Assert.AreEqual( 0, sv1.BuildMeta.Length ); + Assert.IsEmpty( sv1.PreRelease); + Assert.IsEmpty( sv1.BuildMeta); var sv2 = new SemVer(1, 2, 3, AlphaNumericOrdering.CaseSensitive, ["pre-rel"]); Assert.AreEqual( 1, sv2.Major ); Assert.AreEqual( 2, sv2.Minor ); Assert.AreEqual( 3, sv2.Patch ); - Assert.AreEqual( 1, sv2.PreRelease.Length ); + Assert.HasCount( 1, sv2.PreRelease ); Assert.AreEqual( "pre-rel", sv2.PreRelease[ 0 ] ); - Assert.AreEqual( 0, sv2.BuildMeta.Length ); + Assert.IsEmpty( sv2.BuildMeta); var sv3 = new SemVer(1, 2, 3, AlphaNumericOrdering.CaseSensitive, [], ["meta"]); Assert.AreEqual( 1, sv3.Major ); Assert.AreEqual( 2, sv3.Minor ); Assert.AreEqual( 3, sv3.Patch ); - Assert.AreEqual( 0, sv3.PreRelease.Length ); - Assert.AreEqual( 1, sv3.BuildMeta.Length ); + Assert.IsEmpty( sv3.PreRelease); + Assert.HasCount( 1, sv3.BuildMeta ); Assert.AreEqual( "meta", sv3.BuildMeta[ 0 ] ); } @@ -73,12 +73,12 @@ public void SortOrderIng_is_CaseSensitive_works_as_expected( ) var lhs = sample[i-1]; var rhs = sample[i]; - Assert.IsTrue( lhs.CompareTo( rhs ) < 0, $"'{lhs}' should compare less than '{rhs}'" ); + Assert.IsLessThan( 0, lhs.CompareTo( rhs ), $"'{lhs}' should compare less than '{rhs}'" ); // inverted should produce correct results too. - Assert.IsTrue( rhs.CompareTo( lhs ) > 0, $"'{rhs}' should compare greater than '{lhs}'" ); + Assert.IsGreaterThan( 0, rhs.CompareTo( lhs ), $"'{rhs}' should compare greater than '{lhs}'" ); - Assert.IsTrue( lhs.CompareTo( null ) > 0, "Any version should compare greater than null" ); + Assert.IsGreaterThan( 0, lhs.CompareTo( null ), "Any version should compare greater than null" ); #pragma warning disable IDE0002 // Simplify Member Access // Simplification results in a different API call! Test is for a specific case Assert.IsTrue( SemVer.Equals( null, null ) ); @@ -163,8 +163,8 @@ private static void ValidateEquavalent( SemVer expected, SemVer actual, string i Assert.AreEqual( expected.Major, actual.Major, $"Major should match for '{input}'" ); Assert.AreEqual( expected.Minor, actual.Minor, $"Minor should match for '{input}'" ); Assert.AreEqual( expected.Patch, actual.Patch, $"Patch should match for '{input}'" ); - Assert.AreEqual( expected.PreRelease.Length, actual.PreRelease.Length, $"PreRelease.Count should match for '{input}'" ); - Assert.AreEqual( expected.BuildMeta.Length, actual.BuildMeta.Length, $"BuildMeta.Count should match for '{input}'" ); + Assert.HasCount( expected.PreRelease.Length, actual.PreRelease, $"PreRelease.Count should match for '{input}'" ); + Assert.HasCount( expected.BuildMeta.Length, actual.BuildMeta, $"BuildMeta.Count should match for '{input}'" ); for(int i = 0; i < expected.PreRelease.Length; ++i) { Assert.AreEqual( expected.PreRelease[ i ], actual.PreRelease[ i ], $"PreRelease[{i}] should match for '{input}'" ); diff --git a/src/Ubiquity.NET.Versioning.UT/ValidationWithTask.cs b/src/Ubiquity.NET.Versioning.UT/ValidationWithTask.cs index f68f568..c056597 100644 --- a/src/Ubiquity.NET.Versioning.UT/ValidationWithTask.cs +++ b/src/Ubiquity.NET.Versioning.UT/ValidationWithTask.cs @@ -102,7 +102,7 @@ public void ValidateRepoAssemblyVersion( ) // Assert.AreEqual( expectedVersion.Index, actualCI.Index); Assert.AreEqual( expectedVersion.Name, actualCI.Name); - Assert.AreEqual( expectedVersion.PreRelease.Length, actualCI.PreRelease.Length, "prerelease sequence should have matching element count" ); + Assert.HasCount( expectedVersion.PreRelease.Length, actualCI.PreRelease, "prerelease sequence should have matching element count" ); } else { diff --git a/src/Ubiquity.NET.Versioning/Comparison/SemVerComparer.cs b/src/Ubiquity.NET.Versioning/Comparison/SemVerComparer.cs index b013277..563f02a 100644 --- a/src/Ubiquity.NET.Versioning/Comparison/SemVerComparer.cs +++ b/src/Ubiquity.NET.Versioning/Comparison/SemVerComparer.cs @@ -22,7 +22,7 @@ namespace Ubiquity.NET.Versioning.Comparison /// [SuppressMessage( "StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "File name reflects purpose of these classes" )] [SuppressMessage( "StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Tightly coupled types share file scoped implementations" )] - internal static class CaseSensitive + public static class CaseSensitive { /// Gets a comparer that compares the values of pre-release identifier /// diff --git a/src/Ubiquity.NET.Versioning/Properties/Resources.Designer.cs b/src/Ubiquity.NET.Versioning/Properties/Resources.Designer.cs index 6f737b0..f4e7df3 100644 --- a/src/Ubiquity.NET.Versioning/Properties/Resources.Designer.cs +++ b/src/Ubiquity.NET.Versioning/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace Ubiquity.NET.Versioning.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { @@ -114,6 +114,15 @@ internal static string odd_file_versions_are_reserved_for_CI { } } + /// + /// Looks up a localized string similar to Unrecognized format provider. Provider must provide formatting for {0}. + /// + internal static string Unrecognized_format_provider_Provider_must_provide_formatting_for_0 { + get { + return ResourceManager.GetString("Unrecognized_format_provider_Provider_must_provide_formatting_for_0", resourceCulture); + } + } + /// /// Looks up a localized string similar to '{0}' must be in range {1}. {2}. /// diff --git a/src/Ubiquity.NET.Versioning/Properties/Resources.resx b/src/Ubiquity.NET.Versioning/Properties/Resources.resx index 9acf521..90d1d3d 100644 --- a/src/Ubiquity.NET.Versioning/Properties/Resources.resx +++ b/src/Ubiquity.NET.Versioning/Properties/Resources.resx @@ -145,4 +145,8 @@ '{0}' must be in range {1}. {2} {0} - Value name; {1} - range notation for the value; {2} post message text (Usually a spec reference) - + + Unrecognized format provider. Provider must provide formatting for {0} + Format provider for a parse of a SemVer string doesn't report the ordering required. + + \ No newline at end of file diff --git a/src/Ubiquity.NET.Versioning/Properties/ResourcesExtensions.cs b/src/Ubiquity.NET.Versioning/Properties/ResourcesExtensions.cs index 1ddbf0b..02d3741 100644 --- a/src/Ubiquity.NET.Versioning/Properties/ResourcesExtensions.cs +++ b/src/Ubiquity.NET.Versioning/Properties/ResourcesExtensions.cs @@ -13,7 +13,7 @@ namespace Ubiquity.NET.Versioning.Properties { internal static class ResourcesExtensions { - internal static CompositeFormat AsFormat([NotNull] this string? self) + internal static CompositeFormat ParseAsFormat([NotNull][StringSyntax(StringSyntaxAttribute.CompositeFormat)] this string? self) { ArgumentException.ThrowIfNullOrWhiteSpace(self); return CompositeFormat.Parse(self); @@ -37,22 +37,22 @@ internal static string Format([NotNull]this CompositeFormat return string.Format(CultureInfo.CurrentCulture, self, arg0, arg1, arg3); } - internal static string Format([NotNull]this string? self, TArg0 arg0) + internal static string Format([NotNull][StringSyntax( StringSyntaxAttribute.CompositeFormat )] this string? self, TArg0 arg0) { ArgumentException.ThrowIfNullOrWhiteSpace(self); - return string.Format(CultureInfo.CurrentCulture, self.AsFormat(), arg0); + return string.Format(CultureInfo.CurrentCulture, self.ParseAsFormat(), arg0); } - internal static string Format([NotNull]this string? self, TArg0 arg0, TArg1 arg1) + internal static string Format([NotNull][StringSyntax( StringSyntaxAttribute.CompositeFormat )] this string? self, TArg0 arg0, TArg1 arg1) { ArgumentException.ThrowIfNullOrWhiteSpace(self); - return string.Format(CultureInfo.CurrentCulture, self.AsFormat(), arg0, arg1); + return string.Format(CultureInfo.CurrentCulture, self.ParseAsFormat(), arg0, arg1); } - internal static string Format([NotNull]this string? self, TArg0 arg0, TArg1 arg1, TArg3 arg3) + internal static string Format([NotNull][StringSyntax( StringSyntaxAttribute.CompositeFormat )] this string? self, TArg0 arg0, TArg1 arg1, TArg3 arg3) { ArgumentException.ThrowIfNullOrWhiteSpace(self); - return string.Format(CultureInfo.CurrentCulture, self.AsFormat(), arg0, arg1, arg3); + return string.Format(CultureInfo.CurrentCulture, self.ParseAsFormat(), arg0, arg1, arg3); } } } diff --git a/src/Ubiquity.NET.Versioning/SemVer.cs b/src/Ubiquity.NET.Versioning/SemVer.cs index 0b9109e..932ba83 100644 --- a/src/Ubiquity.NET.Versioning/SemVer.cs +++ b/src/Ubiquity.NET.Versioning/SemVer.cs @@ -5,7 +5,6 @@ // ----------------------------------------------------------------------- using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Numerics; @@ -73,17 +72,17 @@ public SemVer( if(!preRel.IsDefaultOrEmpty) { - PreRelease = ValidateElementsWithParser(preRel, SemVerGrammar.PreReleaseIdentifier); + PreRelease = ValidateElementsWithParser( preRel, SemVerGrammar.PreReleaseIdentifier ); } if(!build.IsDefaultOrEmpty) { - BuildMeta = ValidateElementsWithParser(build, SemVerGrammar.BuildIdentifier); + BuildMeta = ValidateElementsWithParser( build, SemVerGrammar.BuildIdentifier ); } - if( sortOrdering == AlphaNumericOrdering.None) + if(sortOrdering == AlphaNumericOrdering.None) { - throw new ArgumentException("Sort ordering of 'None' is an invalid value", nameof(sortOrdering)); + throw new ArgumentException( "Sort ordering of 'None' is an invalid value", nameof( sortOrdering ) ); } AlphaNumericOrdering = sortOrdering; @@ -121,13 +120,13 @@ public SemVer( public override string ToString( ) { var bldr = new StringBuilder($"{Major}.{Minor}.{Patch}"); - if( PreRelease.Length > 0 ) + if(PreRelease.Length > 0) { bldr.Append( '-' ) .AppendJoin( '.', PreRelease ); } - if( BuildMeta.Length > 0 ) + if(BuildMeta.Length > 0) { bldr.Append( '+' ) .AppendJoin( '.', BuildMeta ); @@ -136,6 +135,8 @@ public override string ToString( ) return bldr.ToString(); } + #region Relational operators + /// /// The for both sides does not match /// @@ -153,35 +154,33 @@ public int CompareTo( SemVer? other ) return 1; } - if (AlphaNumericOrdering != other.AlphaNumericOrdering) + if(AlphaNumericOrdering != other.AlphaNumericOrdering) { - throw new InvalidOperationException("SemVer values have different AlphaNumericOrdering, direct comparison is not supported."); + throw new InvalidOperationException( "SemVer values have different AlphaNumericOrdering, direct comparison is not supported." ); } return AlphaNumericOrdering == AlphaNumericOrdering.CaseSensitive - ? Comparison.CaseSensitive.SemVer.Compare(this, other) - : Comparison.CaseInsensitive.SemVer.Compare(this, other); + ? Comparison.CaseSensitive.SemVer.Compare( this, other ) + : Comparison.CaseInsensitive.SemVer.Compare( this, other ); } - #region Relational operators - /// public override bool Equals( object? obj ) { - return obj is SemVer v && Equals(v); + return obj is SemVer v && Equals( v ); } /// public override int GetHashCode( ) { // NOTE: Build meta does not contribute to ordering and therefore does not contribute to the hash - return HashCode.Combine(AlphaNumericOrdering, Major, Minor, Patch, PreRelease); + return HashCode.Combine( AlphaNumericOrdering, Major, Minor, Patch, PreRelease ); } /// public bool Equals( SemVer? other ) { - return CompareTo(other) == 0; + return CompareTo( other ) == 0; } /// @@ -255,19 +254,25 @@ public static SemVer Parse( string s, IFormatProvider? provider ) /// public static bool TryParse( [NotNullWhen( true )] string? s, IFormatProvider? provider, [MaybeNullWhen( false )] out SemVer result ) { - return TryParseIncludeDerived(s, provider, out result, out _); + return TryParseIncludeDerived( s, provider, out result, out _ ); } /// Tries to parse a string into a semantic version providing details of any failures /// Input string to parse - /// Formatting provider to use + /// Formatting provider to use [ or ] /// Resulting if parse succeeds /// Exception data for any errors or if parse succeeded /// if string successfully parsed or if not /// - /// This does NOT consider derived types. It is used by the general parsing as well as parsing of derived types - /// themselves. + /// This does NOT consider derived types. It is used by the general parsing as well as parsing of derived types + /// themselves. + /// The sort ordering for alpha numeric identifies in the version is specified by the . + /// It must provide a value for the in it's + /// otherwise an exception occurs. + /// /// + /// One or more of the arguments provided is + /// The does not support the format internal static bool TryParse( [NotNull] string? s, IFormatProvider? provider, @@ -278,8 +283,14 @@ internal static bool TryParse( ArgumentNullException.ThrowIfNull( s ); provider ??= SemVerFormatProvider.CaseSensitive; + if(provider is null) + { + string msg = Resources.Unrecognized_format_provider_Provider_must_provide_formatting_for_0.Format( nameof( AlphaNumericOrdering ) ); + throw new ArgumentException( msg, nameof(provider) ); + } + IResult parseResult = SemVerGrammar.SemanticVersion(provider.GetOrdering()).TryParse(s); - if(parseResult.Failed(out ex)) + if(parseResult.Failed( out ex )) { result = default; return false; @@ -300,11 +311,11 @@ private static bool TryParseIncludeDerived( result = default; ex = default; - if(TryParse(s, provider, out SemVer? baseVer, out ex)) + if(TryParse( s, provider, out SemVer? baseVer, out ex )) { // if caller expects case sensitivity then CSmeVer[-CI] are off the table. // Those are explicitly case insensitive. - if( provider.IsCaseSensitive()) + if(provider.IsCaseSensitive()) { result = baseVer; return true; @@ -312,13 +323,13 @@ private static bool TryParseIncludeDerived( // expect case insensitive so consider CSemVer/CSemVerCI - if(CSemVer.TryFrom(baseVer, out CSemVer? ver, out ex)) + if(CSemVer.TryFrom( baseVer, out CSemVer? ver, out ex )) { result = ver; return true; } - if(CSemVerCI.TryFrom(baseVer, out CSemVerCI? ciVer, out ex)) + if(CSemVerCI.TryFrom( baseVer, out CSemVerCI? ciVer, out ex )) { result = ciVer; return true; @@ -333,19 +344,10 @@ private static bool TryParseIncludeDerived( #endregion - private static ImmutableArray ValidateElementsWithParser( - IEnumerable? value, - Parser parser, - [CallerArgumentExpression(nameof(value))] string? exp = null - ) - { - return value is null ? [] : ValidateElementsWithParser( [.. value], parser, exp); - } - private static ImmutableArray ValidateElementsWithParser( ImmutableArray? value, Parser parser, - [CallerArgumentExpression(nameof(value))] string? exp = null + [CallerArgumentExpression( nameof( value ) )] string? exp = null ) { if(value is null) @@ -358,7 +360,7 @@ private static ImmutableArray ValidateElementsWithParser( IResult parseResult = parser.TryParse(part); if(!parseResult.WasSuccessful) { - throw new FormatException( Resources.exp_0_contains_invalid_value_1_2.Format(exp, part, parseResult.Message) ); + throw new FormatException( Resources.exp_0_contains_invalid_value_1_2.Format( exp, part, parseResult.Message ) ); } }