From ea65b2b7df78fd075caecac96937e9870e06e646 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 21 Dec 2016 11:45:52 -0500 Subject: correct semantic version pre-release label precedence (#195) --- src/StardewModdingAPI/SemanticVersion.cs | 59 ++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 14 deletions(-) (limited to 'src/StardewModdingAPI') diff --git a/src/StardewModdingAPI/SemanticVersion.cs b/src/StardewModdingAPI/SemanticVersion.cs index b3d9ee4a..cf435aaa 100644 --- a/src/StardewModdingAPI/SemanticVersion.cs +++ b/src/StardewModdingAPI/SemanticVersion.cs @@ -48,25 +48,56 @@ namespace StardewModdingAPI /// Get an integer indicating whether this version precedes (less than 0), supercedes (more than 0), or is equivalent to (0) the specified version. /// The version to compare with this instance. + /// The implementation is defined by Semantic Version 2.0 (http://semver.org/). public int CompareTo(ISemanticVersion other) { - // compare version numbers + const int same = 0; + const int curNewer = 1; + const int curOlder = -1; + + // compare stable versions if (this.MajorVersion != other.MajorVersion) - return this.MajorVersion - other.MajorVersion; + return this.MajorVersion.CompareTo(other.MajorVersion); if (this.MinorVersion != other.MinorVersion) - return this.MinorVersion - other.MinorVersion; + return this.MinorVersion.CompareTo(other.MinorVersion); if (this.PatchVersion != other.PatchVersion) - return this.PatchVersion - other.PatchVersion; - - // stable version (without tag) supercedes prerelease (with tag) - bool curHasTag = !string.IsNullOrWhiteSpace(this.Build); - bool otherHasTag = !string.IsNullOrWhiteSpace(other.Build); - if (!curHasTag && otherHasTag) - return 1; - if (curHasTag && !otherHasTag) - return -1; - - // else compare by string + return this.PatchVersion.CompareTo(other.PatchVersion); + if (this.Build == other.Build) + return same; + + // stable supercedes pre-release + bool curIsStable = string.IsNullOrWhiteSpace(this.Build); + bool otherIsStable = string.IsNullOrWhiteSpace(other.Build); + if (curIsStable) + return curNewer; + if (otherIsStable) + return curOlder; + + // compare two pre-release tag values + string[] curParts = this.Build.Split('.'); + string[] otherParts = other.Build.Split('.'); + for (int i = 0; i < curParts.Length; i++) + { + // longer prerelease tag supercedes if otherwise equal + if (otherParts.Length <= i) + return curNewer; + + // compare if different + if (curParts[i] != otherParts[i]) + { + // compare numerically if possible + { + int curNum, otherNum; + if (int.TryParse(curParts[i], out curNum) && int.TryParse(otherParts[i], out otherNum)) + return curNum.CompareTo(otherNum); + } + + // else compare lexically + return string.Compare(curParts[i], otherParts[i], StringComparison.OrdinalIgnoreCase); + } + } + + // fallback (this should never happen) return string.Compare(this.ToString(), other.ToString(), StringComparison.InvariantCultureIgnoreCase); } -- cgit