diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/SMAPI.Tests/Utilities/SemanticVersionTests.cs | 35 | ||||
-rw-r--r-- | src/SMAPI.Toolkit.CoreInterfaces/ISemanticVersion.cs | 38 | ||||
-rw-r--r-- | src/SMAPI.Toolkit/SemanticVersion.cs | 61 | ||||
-rw-r--r-- | src/SMAPI.sln.DotSettings | 1 | ||||
-rw-r--r-- | src/SMAPI/SemanticVersion.cs | 31 |
5 files changed, 91 insertions, 75 deletions
diff --git a/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs b/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs index 599ac839..fedadba6 100644 --- a/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs +++ b/src/SMAPI.Tests/Utilities/SemanticVersionTests.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Diagnostics.CodeAnalysis; using Newtonsoft.Json; @@ -63,10 +61,10 @@ namespace SMAPI.Tests.Utilities [TestCase("apple")] [TestCase("-apple")] [TestCase("-5")] - public void Constructor_FromString_WithInvalidValues(string input) + public void Constructor_FromString_WithInvalidValues(string? input) { if (input == null) - this.AssertAndLogException<ArgumentNullException>(() => new SemanticVersion(input)); + this.AssertAndLogException<ArgumentNullException>(() => new SemanticVersion(input!)); else this.AssertAndLogException<FormatException>(() => new SemanticVersion(input)); } @@ -93,7 +91,7 @@ namespace SMAPI.Tests.Utilities [TestCase("1.2.3.4-some-tag.4 ")] public void Constructor_FromString_Standard_DisallowsNonStandardVersion(string input) { - Assert.Throws<FormatException>(() => new SemanticVersion(input)); + Assert.Throws<FormatException>(() => _ = new SemanticVersion(input)); } /// <summary>Assert the parsed version when constructed from standard parts.</summary> @@ -112,7 +110,7 @@ namespace SMAPI.Tests.Utilities [TestCase(1, 2, 3, "some-tag.4 ", null, ExpectedResult = "1.2.3-some-tag.4")] [TestCase(1, 2, 3, "some-tag.4 ", "build.004", ExpectedResult = "1.2.3-some-tag.4+build.004")] [TestCase(1, 2, 0, null, "3.4.5-build.004", ExpectedResult = "1.2.0+3.4.5-build.004")] - public string Constructor_FromParts(int major, int minor, int patch, string prerelease, string build) + public string Constructor_FromParts(int major, int minor, int patch, string? prerelease, string? build) { // act ISemanticVersion version = new SemanticVersion(major, minor, patch, prerelease, build); @@ -222,11 +220,11 @@ namespace SMAPI.Tests.Utilities // null [TestCase("1.0.0", null, ExpectedResult = 1)] // null is always less than any value per CompareTo remarks - public int CompareTo(string versionStrA, string versionStrB) + public int CompareTo(string versionStrA, string? versionStrB) { // arrange ISemanticVersion versionA = new SemanticVersion(versionStrA); - ISemanticVersion versionB = versionStrB != null + ISemanticVersion? versionB = versionStrB != null ? new SemanticVersion(versionStrB) : null; @@ -270,11 +268,11 @@ namespace SMAPI.Tests.Utilities // null [TestCase("1.0.0", null, ExpectedResult = false)] // null is always less than any value per CompareTo remarks - public bool IsOlderThan(string versionStrA, string versionStrB) + public bool IsOlderThan(string versionStrA, string? versionStrB) { // arrange ISemanticVersion versionA = new SemanticVersion(versionStrA); - ISemanticVersion versionB = versionStrB != null + ISemanticVersion? versionB = versionStrB != null ? new SemanticVersion(versionStrB) : null; @@ -319,11 +317,11 @@ namespace SMAPI.Tests.Utilities // null [TestCase("1.0.0", null, ExpectedResult = true)] // null is always less than any value per CompareTo remarks - public bool IsNewerThan(string versionStrA, string versionStrB) + public bool IsNewerThan(string versionStrA, string? versionStrB) { // arrange ISemanticVersion versionA = new SemanticVersion(versionStrA); - ISemanticVersion versionB = versionStrB != null + ISemanticVersion? versionB = versionStrB != null ? new SemanticVersion(versionStrB) : null; @@ -356,13 +354,13 @@ namespace SMAPI.Tests.Utilities [TestCase("1.0-beta.2", "1.0-beta.10", "1.0-beta.3", ExpectedResult = false)] [TestCase("1.0-beta-2", "1.0-beta-10", "1.0-beta-3", ExpectedResult = false)] [TestCase("1.0.0", "1.0.0", null, ExpectedResult = false)] // null is always less than any value per CompareTo remarks - public bool IsBetween(string versionStr, string lowerStr, string upperStr) + public bool IsBetween(string versionStr, string? lowerStr, string? upperStr) { // arrange - ISemanticVersion lower = lowerStr != null + ISemanticVersion? lower = lowerStr != null ? new SemanticVersion(lowerStr) : null; - ISemanticVersion upper = upperStr != null + ISemanticVersion? upper = upperStr != null ? new SemanticVersion(upperStr) : null; ISemanticVersion version = new SemanticVersion(versionStr); @@ -436,7 +434,7 @@ namespace SMAPI.Tests.Utilities /// <param name="prerelease">The prerelease tag.</param> /// <param name="build">The build metadata.</param> /// <param name="nonStandard">Whether the version should be marked as non-standard.</param> - private void AssertParts(ISemanticVersion version, int major, int minor, int patch, string prerelease, string build, bool nonStandard) + private void AssertParts(ISemanticVersion version, int major, int minor, int patch, string? prerelease, string? build, bool nonStandard) { Assert.AreEqual(major, version.MajorVersion, "The major version doesn't match."); Assert.AreEqual(minor, version.MinorVersion, "The minor version doesn't match."); @@ -449,9 +447,8 @@ namespace SMAPI.Tests.Utilities /// <summary>Assert that the expected exception type is thrown, and log the action output and thrown exception.</summary> /// <typeparam name="T">The expected exception type.</typeparam> /// <param name="action">The action which may throw the exception.</param> - /// <param name="message">The message to log if the expected exception isn't thrown.</param> [SuppressMessage("ReSharper", "UnusedParameter.Local", Justification = "The message argument is deliberately only used in precondition checks since this is an assertion method.")] - private void AssertAndLogException<T>(Func<object> action, string message = null) + private void AssertAndLogException<T>(Func<object> action) where T : Exception { this.AssertAndLogException<T>(() => @@ -466,7 +463,7 @@ namespace SMAPI.Tests.Utilities /// <param name="action">The action which may throw the exception.</param> /// <param name="message">The message to log if the expected exception isn't thrown.</param> [SuppressMessage("ReSharper", "UnusedParameter.Local", Justification = "The message argument is deliberately only used in precondition checks since this is an assertion method.")] - private void AssertAndLogException<T>(Action action, string message = null) + private void AssertAndLogException<T>(Action action, string? message = null) where T : Exception { try diff --git a/src/SMAPI.Toolkit.CoreInterfaces/ISemanticVersion.cs b/src/SMAPI.Toolkit.CoreInterfaces/ISemanticVersion.cs index 52cec52e..7998272f 100644 --- a/src/SMAPI.Toolkit.CoreInterfaces/ISemanticVersion.cs +++ b/src/SMAPI.Toolkit.CoreInterfaces/ISemanticVersion.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; namespace StardewModdingAPI @@ -20,10 +18,10 @@ namespace StardewModdingAPI int PatchVersion { get; } /// <summary>An optional prerelease tag.</summary> - string PrereleaseTag { get; } + string? PrereleaseTag { get; } /// <summary>Optional build metadata. This is ignored when determining version precedence.</summary> - string BuildMetadata { get; } + string? BuildMetadata { get; } /********* @@ -34,32 +32,38 @@ namespace StardewModdingAPI /// <summary>Get whether this version is older than the specified version.</summary> /// <param name="other">The version to compare with this instance.</param> - bool IsOlderThan(ISemanticVersion other); + /// <remarks>Although the <paramref name="other"/> parameter is nullable, it isn't optional. A <c>null</c> version is considered earlier than every possible valid version, so passing <c>null</c> to <paramref name="other"/> will always return false.</remarks> + bool IsOlderThan(ISemanticVersion? other); /// <summary>Get whether this version is older than the specified version.</summary> - /// <param name="other">The version to compare with this instance.</param> + /// <param name="other">The version to compare with this instance. A null value is never older.</param> /// <exception cref="FormatException">The specified version is not a valid semantic version.</exception> - bool IsOlderThan(string other); + /// <remarks>Although the <paramref name="other"/> parameter is nullable, it isn't optional. A <c>null</c> version is considered earlier than every possible valid version, so passing <c>null</c> to <paramref name="other"/> will always return false.</remarks> + bool IsOlderThan(string? other); /// <summary>Get whether this version is newer than the specified version.</summary> - /// <param name="other">The version to compare with this instance.</param> - bool IsNewerThan(ISemanticVersion other); + /// <param name="other">The version to compare with this instance. A null value is always older.</param> + /// <remarks>Although the <paramref name="other"/> parameter is nullable, it isn't optional. A <c>null</c> version is considered earlier than every possible valid version, so passing <c>null</c> to <paramref name="other"/> will always return true.</remarks> + bool IsNewerThan(ISemanticVersion? other); /// <summary>Get whether this version is newer than the specified version.</summary> - /// <param name="other">The version to compare with this instance.</param> + /// <param name="other">The version to compare with this instance. A null value is always older.</param> /// <exception cref="FormatException">The specified version is not a valid semantic version.</exception> - bool IsNewerThan(string other); + /// <remarks>Although the <paramref name="other"/> parameter is nullable, it isn't optional. A <c>null</c> version is considered earlier than every possible valid version, so passing <c>null</c> to <paramref name="other"/> will always return true.</remarks> + bool IsNewerThan(string? other); /// <summary>Get whether this version is between two specified versions (inclusively).</summary> - /// <param name="min">The minimum version.</param> - /// <param name="max">The maximum version.</param> - bool IsBetween(ISemanticVersion min, ISemanticVersion max); + /// <param name="min">The minimum version. A null value is always older.</param> + /// <param name="max">The maximum version. A null value is never newer.</param> + /// <remarks>Although the <paramref name="min"/> and <paramref name="max"/> parameters are nullable, they are not optional. A <c>null</c> version is considered earlier than every possible valid version. For example, passing <c>null</c> to <paramref name="max"/> will always return false, since no valid version can be earlier than <c>null</c>.</remarks> + bool IsBetween(ISemanticVersion? min, ISemanticVersion? max); /// <summary>Get whether this version is between two specified versions (inclusively).</summary> - /// <param name="min">The minimum version.</param> - /// <param name="max">The maximum version.</param> + /// <param name="min">The minimum version. A null value is always older.</param> + /// <param name="max">The maximum version. A null value is never newer.</param> /// <exception cref="FormatException">One of the specified versions is not a valid semantic version.</exception> - bool IsBetween(string min, string max); + /// <remarks>Although the <paramref name="min"/> and <paramref name="max"/> parameters are nullable, they are not optional. A <c>null</c> version is considered earlier than every possible valid version. For example, passing <c>null</c> to <paramref name="max"/> will always return false, since no valid version can be earlier than <c>null</c>.</remarks> + bool IsBetween(string? min, string? max); /// <summary>Get a string representation of the version.</summary> string ToString(); diff --git a/src/SMAPI.Toolkit/SemanticVersion.cs b/src/SMAPI.Toolkit/SemanticVersion.cs index 41ae12eb..cea8c447 100644 --- a/src/SMAPI.Toolkit/SemanticVersion.cs +++ b/src/SMAPI.Toolkit/SemanticVersion.cs @@ -1,6 +1,5 @@ -#nullable disable - using System; +using System.Diagnostics.CodeAnalysis; using System.Text.RegularExpressions; using StardewModdingAPI.Toolkit.Framework; @@ -40,10 +39,10 @@ namespace StardewModdingAPI.Toolkit public int PlatformRelease { get; } /// <inheritdoc /> - public string PrereleaseTag { get; } + public string? PrereleaseTag { get; } /// <inheritdoc /> - public string BuildMetadata { get; } + public string? BuildMetadata { get; } /********* @@ -56,7 +55,7 @@ namespace StardewModdingAPI.Toolkit /// <param name="platformRelease">The platform-specific version (if applicable).</param> /// <param name="prereleaseTag">An optional prerelease tag.</param> /// <param name="buildMetadata">Optional build metadata. This is ignored when determining version precedence.</param> - public SemanticVersion(int major, int minor, int patch, int platformRelease = 0, string prereleaseTag = null, string buildMetadata = null) + public SemanticVersion(int major, int minor, int patch, int platformRelease = 0, string? prereleaseTag = null, string? buildMetadata = null) { this.MajorVersion = major; this.MinorVersion = minor; @@ -106,7 +105,7 @@ namespace StardewModdingAPI.Toolkit } /// <inheritdoc /> - public int CompareTo(ISemanticVersion other) + public int CompareTo(ISemanticVersion? other) { return other == null ? 1 @@ -114,7 +113,7 @@ namespace StardewModdingAPI.Toolkit } /// <inheritdoc /> - public bool Equals(ISemanticVersion other) + public bool Equals(ISemanticVersion? other) { return other != null && this.CompareTo(other) == 0; } @@ -126,15 +125,15 @@ namespace StardewModdingAPI.Toolkit } /// <inheritdoc /> - public bool IsOlderThan(ISemanticVersion other) + public bool IsOlderThan(ISemanticVersion? other) { return this.CompareTo(other) < 0; } /// <inheritdoc /> - public bool IsOlderThan(string other) + public bool IsOlderThan(string? other) { - ISemanticVersion otherVersion = other != null + ISemanticVersion? otherVersion = other != null ? new SemanticVersion(other, allowNonStandard: true) : null; @@ -142,15 +141,15 @@ namespace StardewModdingAPI.Toolkit } /// <inheritdoc /> - public bool IsNewerThan(ISemanticVersion other) + public bool IsNewerThan(ISemanticVersion? other) { return this.CompareTo(other) > 0; } /// <inheritdoc /> - public bool IsNewerThan(string other) + public bool IsNewerThan(string? other) { - ISemanticVersion otherVersion = other != null + ISemanticVersion? otherVersion = other != null ? new SemanticVersion(other, allowNonStandard: true) : null; @@ -158,18 +157,18 @@ namespace StardewModdingAPI.Toolkit } /// <inheritdoc /> - public bool IsBetween(ISemanticVersion min, ISemanticVersion max) + public bool IsBetween(ISemanticVersion? min, ISemanticVersion? max) { return this.CompareTo(min) >= 0 && this.CompareTo(max) <= 0; } /// <inheritdoc /> - public bool IsBetween(string min, string max) + public bool IsBetween(string? min, string? max) { - ISemanticVersion minVersion = min != null + ISemanticVersion? minVersion = min != null ? new SemanticVersion(min, allowNonStandard: true) : null; - ISemanticVersion maxVersion = max != null + ISemanticVersion? maxVersion = max != null ? new SemanticVersion(max, allowNonStandard: true) : null; @@ -199,7 +198,12 @@ namespace StardewModdingAPI.Toolkit /// <param name="version">The version string.</param> /// <param name="parsed">The parsed representation.</param> /// <returns>Returns whether parsing the version succeeded.</returns> - public static bool TryParse(string version, out ISemanticVersion parsed) + public static bool TryParse(string? version, +#if NET5_0_OR_GREATER + [NotNullWhen(true)] +#endif + out ISemanticVersion? parsed + ) { return SemanticVersion.TryParse(version, allowNonStandard: false, out parsed); } @@ -209,8 +213,19 @@ namespace StardewModdingAPI.Toolkit /// <param name="allowNonStandard">Whether to allow non-standard extensions to semantic versioning.</param> /// <param name="parsed">The parsed representation.</param> /// <returns>Returns whether parsing the version succeeded.</returns> - public static bool TryParse(string version, bool allowNonStandard, out ISemanticVersion parsed) + public static bool TryParse(string? version, bool allowNonStandard, +#if NET5_0_OR_GREATER + [NotNullWhen(true)] +#endif + out ISemanticVersion? parsed + ) { + if (version == null) + { + parsed = null; + return false; + } + try { parsed = new SemanticVersion(version, allowNonStandard); @@ -229,7 +244,7 @@ namespace StardewModdingAPI.Toolkit *********/ /// <summary>Get a normalized prerelease or build tag.</summary> /// <param name="tag">The tag to normalize.</param> - private string GetNormalizedTag(string tag) + private string? GetNormalizedTag(string? tag) { tag = tag?.Trim(); return !string.IsNullOrWhiteSpace(tag) ? tag : null; @@ -241,7 +256,7 @@ namespace StardewModdingAPI.Toolkit /// <param name="otherPatch">The patch version to compare with this instance.</param> /// <param name="otherPlatformRelease">The non-standard platform release to compare with this instance.</param> /// <param name="otherTag">The prerelease tag to compare with this instance.</param> - private int CompareTo(int otherMajor, int otherMinor, int otherPatch, int otherPlatformRelease, string otherTag) + private int CompareTo(int otherMajor, int otherMinor, int otherPatch, int otherPlatformRelease, string? otherTag) { const int same = 0; const int curNewer = 1; @@ -270,8 +285,8 @@ namespace StardewModdingAPI.Toolkit return curOlder; // compare two prerelease tag values - string[] curParts = this.PrereleaseTag.Split('.', '-'); - string[] otherParts = otherTag.Split('.', '-'); + string[] curParts = this.PrereleaseTag?.Split('.', '-') ?? Array.Empty<string>(); + string[] otherParts = otherTag?.Split('.', '-') ?? Array.Empty<string>(); int length = Math.Max(curParts.Length, otherParts.Length); for (int i = 0; i < length; i++) { diff --git a/src/SMAPI.sln.DotSettings b/src/SMAPI.sln.DotSettings index b85185d5..5b35c615 100644 --- a/src/SMAPI.sln.DotSettings +++ b/src/SMAPI.sln.DotSettings @@ -73,5 +73,6 @@ <s:Boolean x:Key="/Default/UserDictionary/Words/=tilesheets/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=tilesheet_0027s/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=unloadable/@EntryIndexedValue">True</s:Boolean> + <s:Boolean x:Key="/Default/UserDictionary/Words/=versioning/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=virally/@EntryIndexedValue">True</s:Boolean> </wpf:ResourceDictionary>
\ No newline at end of file diff --git a/src/SMAPI/SemanticVersion.cs b/src/SMAPI/SemanticVersion.cs index 4e484633..7a6cdcdd 100644 --- a/src/SMAPI/SemanticVersion.cs +++ b/src/SMAPI/SemanticVersion.cs @@ -1,6 +1,5 @@ -#nullable disable - using System; +using System.Diagnostics.CodeAnalysis; using Newtonsoft.Json; namespace StardewModdingAPI @@ -28,10 +27,10 @@ namespace StardewModdingAPI public int PatchVersion => this.Version.PatchVersion; /// <inheritdoc /> - public string PrereleaseTag => this.Version.PrereleaseTag; + public string? PrereleaseTag => this.Version.PrereleaseTag; /// <inheritdoc /> - public string BuildMetadata => this.Version.BuildMetadata; + public string? BuildMetadata => this.Version.BuildMetadata; /********* @@ -43,7 +42,7 @@ namespace StardewModdingAPI /// <param name="patchVersion">The patch version for backwards-compatible bug fixes.</param> /// <param name="prereleaseTag">An optional prerelease tag.</param> /// <param name="buildMetadata">Optional build metadata. This is ignored when determining version precedence.</param> - public SemanticVersion(int majorVersion, int minorVersion, int patchVersion, string prereleaseTag = null, string buildMetadata = null) + public SemanticVersion(int majorVersion, int minorVersion, int patchVersion, string? prereleaseTag = null, string? buildMetadata = null) : this(majorVersion, minorVersion, patchVersion, 0, prereleaseTag, buildMetadata) { } /// <summary>Construct an instance.</summary> @@ -54,7 +53,7 @@ namespace StardewModdingAPI /// <param name="platformRelease">The platform-specific version (if applicable).</param> /// <param name="buildMetadata">Optional build metadata. This is ignored when determining version precedence.</param> [JsonConstructor] - internal SemanticVersion(int majorVersion, int minorVersion, int patchVersion, int platformRelease, string prereleaseTag = null, string buildMetadata = null) + internal SemanticVersion(int majorVersion, int minorVersion, int patchVersion, int platformRelease, string? prereleaseTag = null, string? buildMetadata = null) : this(new Toolkit.SemanticVersion(majorVersion, minorVersion, patchVersion, platformRelease, prereleaseTag, buildMetadata)) { } /// <summary>Construct an instance.</summary> @@ -93,49 +92,49 @@ namespace StardewModdingAPI /// <inheritdoc /> /// <remarks>The implementation is defined by Semantic Version 2.0 (https://semver.org/).</remarks> - public int CompareTo(ISemanticVersion other) + public int CompareTo(ISemanticVersion? other) { return this.Version.CompareTo(other); } /// <inheritdoc /> - public bool IsOlderThan(ISemanticVersion other) + public bool IsOlderThan(ISemanticVersion? other) { return this.Version.IsOlderThan(other); } /// <inheritdoc /> - public bool IsOlderThan(string other) + public bool IsOlderThan(string? other) { return this.Version.IsOlderThan(other); } /// <inheritdoc /> - public bool IsNewerThan(ISemanticVersion other) + public bool IsNewerThan(ISemanticVersion? other) { return this.Version.IsNewerThan(other); } /// <inheritdoc /> - public bool IsNewerThan(string other) + public bool IsNewerThan(string? other) { return this.Version.IsNewerThan(other); } /// <inheritdoc /> - public bool IsBetween(ISemanticVersion min, ISemanticVersion max) + public bool IsBetween(ISemanticVersion? min, ISemanticVersion? max) { return this.Version.IsBetween(min, max); } /// <inheritdoc /> - public bool IsBetween(string min, string max) + public bool IsBetween(string? min, string? max) { return this.Version.IsBetween(min, max); } /// <inheritdoc /> - public bool Equals(ISemanticVersion other) + public bool Equals(ISemanticVersion? other) { return other != null && this.CompareTo(other) == 0; } @@ -156,9 +155,9 @@ namespace StardewModdingAPI /// <param name="version">The version string.</param> /// <param name="parsed">The parsed representation.</param> /// <returns>Returns whether parsing the version succeeded.</returns> - public static bool TryParse(string version, out ISemanticVersion parsed) + public static bool TryParse(string version, [NotNullWhen(true)] out ISemanticVersion? parsed) { - if (Toolkit.SemanticVersion.TryParse(version, out ISemanticVersion versionImpl)) + if (Toolkit.SemanticVersion.TryParse(version, out ISemanticVersion? versionImpl)) { parsed = new SemanticVersion(versionImpl); return true; |