using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text.RegularExpressions; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("SemanticVersioning")] [assembly: AssemblyTrademark("")] [assembly: InternalsVisibleTo("SemanticVersioning.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010019351d4288017757df1b69b4d0da9a775e6eec498ec93d209d6db4d62e9962476c8da01545cc47335cdc39ba803f4db368ce5f2fdd6cd395196f3328f9039dccdeb3c0f9aece7b8751cd3bc2cb2297d4f463a376eff61b7295b96af9b9faf3eef6005dc967a7a97431cc42cff72e60f05797f3e16186f8fbaf26074e96a2b5e1")] [assembly: ComVisible(false)] [assembly: Guid("a3ff1b6d-68bb-4a0a-a487-858aaa8e3573")] [assembly: AssemblyCopyright("Copyright 2016 Adam Reeve")] [assembly: AssemblyDescription("This library implements the Semantic Versioning 2.0.0 specification and the version range specification used by npm.")] [assembly: AssemblyFileVersion("2.0.2.0")] [assembly: AssemblyInformationalVersion("2.0.2")] [assembly: AssemblyTitle("SemanticVersioning")] [assembly: AssemblyVersion("2.0.2.0")] namespace System { internal static class Tuple { public static Tuple Create(T1 item1, T2 item2) { return new Tuple(item1, item2); } } [Serializable] internal class Tuple { public readonly T1 Item1; public readonly T2 Item2; private static readonly IEqualityComparer Item1Cpmparer = EqualityComparer.Default; private static readonly IEqualityComparer Item2Comparer = EqualityComparer.Default; public Tuple(T1 _item1, T2 _item2) { Item1 = _item1; Item2 = _item2; } public override string ToString() { return string.Format("<{0},`1,}>", Item1, Item2); } private static bool IsNull(object obj) { return obj == null; } public static bool operator ==(Tuple a, Tuple b) { if (IsNull(a) && !IsNull(b)) { return false; } if (!IsNull(a) && IsNull(b)) { return false; } if (IsNull(a) && IsNull(b)) { return true; } if (a.Item1.Equals(b.Item1)) { return a.Item2.Equals(b.Item2); } return false; } public static bool operator !=(Tuple a, Tuple b) { return !(a == b); } } } namespace SemanticVersioning { internal class Comparator : IEquatable { public enum Operator { Equal, LessThan, LessThanOrEqual, GreaterThan, GreaterThanOrEqual, GreaterThanOrEqualIncludingPrereleases, LessThanExcludingPrereleases } public readonly Operator ComparatorType; public readonly Version Version; private const string pattern = "\n \\s*\n ([=<>]*) # Comparator type (can be empty)\n \\s*\n ([0-9a-zA-Z\\-\\+\\.\\*]+) # Version (potentially partial version)\n \\s*\n "; public Comparator(string input) { Match match = new Regex(string.Format("^{0}$", "\n \\s*\n ([=<>]*) # Comparator type (can be empty)\n \\s*\n ([0-9a-zA-Z\\-\\+\\.\\*]+) # Version (potentially partial version)\n \\s*\n "), RegexOptions.IgnorePatternWhitespace).Match(input); if (!match.Success) { throw new ArgumentException($"Invalid comparator string: {input}"); } ComparatorType = ParseComparatorType(match.Groups[1].Value); PartialVersion partialVersion = new PartialVersion(match.Groups[2].Value); if (!partialVersion.IsFull()) { switch (ComparatorType) { case Operator.LessThanOrEqual: ComparatorType = Operator.LessThan; if (!partialVersion.Major.HasValue) { ComparatorType = Operator.GreaterThanOrEqual; Version = new Version(0, 0, 0); } else if (!partialVersion.Minor.HasValue) { Version = new Version(partialVersion.Major.Value + 1, 0, 0); } else { Version = new Version(partialVersion.Major.Value, partialVersion.Minor.Value + 1, 0); } break; case Operator.GreaterThan: ComparatorType = Operator.GreaterThanOrEqualIncludingPrereleases; if (!partialVersion.Major.HasValue) { ComparatorType = Operator.LessThan; Version = new Version(0, 0, 0); } else if (!partialVersion.Minor.HasValue) { Version = new Version(partialVersion.Major.Value + 1, 0, 0); } else { Version = new Version(partialVersion.Major.Value, partialVersion.Minor.Value + 1, 0); } break; case Operator.LessThan: ComparatorType = Operator.LessThanExcludingPrereleases; Version = partialVersion.ToZeroVersion(); break; case Operator.GreaterThanOrEqual: ComparatorType = Operator.GreaterThanOrEqualIncludingPrereleases; Version = partialVersion.ToZeroVersion(); break; default: Version = partialVersion.ToZeroVersion(); break; } } else { Version = partialVersion.ToZeroVersion(); } } public Comparator(Operator comparatorType, Version comparatorVersion) { if (comparatorVersion == null) { throw new NullReferenceException("Null comparator version"); } ComparatorType = comparatorType; Version = comparatorVersion; } public static Tuple TryParse(string input) { Match match = new Regex(string.Format("^{0}", "\n \\s*\n ([=<>]*) # Comparator type (can be empty)\n \\s*\n ([0-9a-zA-Z\\-\\+\\.\\*]+) # Version (potentially partial version)\n \\s*\n "), RegexOptions.IgnorePatternWhitespace).Match(input); if (!match.Success) { return null; } return Tuple.Create(match.Length, new Comparator(match.Value)); } private static Operator ParseComparatorType(string input) { if (input != null) { if (input == null || input.Length != 0) { switch (input) { case "=": break; case "<": return Operator.LessThan; case "<=": return Operator.LessThanOrEqual; case ">": return Operator.GreaterThan; case ">=": return Operator.GreaterThanOrEqual; default: goto IL_005b; } } return Operator.Equal; } goto IL_005b; IL_005b: throw new ArgumentException($"Invalid comparator type: {input}"); } public bool IsSatisfied(Version version) { switch (ComparatorType) { case Operator.Equal: return version == Version; case Operator.LessThan: return version < Version; case Operator.LessThanOrEqual: return version <= Version; case Operator.GreaterThan: return version > Version; case Operator.GreaterThanOrEqual: return version >= Version; case Operator.GreaterThanOrEqualIncludingPrereleases: if (!(version >= Version)) { if (version.IsPreRelease) { return version.BaseVersion() == Version; } return false; } return true; case Operator.LessThanExcludingPrereleases: if (version < Version) { if (version.IsPreRelease) { return !(version.BaseVersion() == Version); } return true; } return false; default: throw new InvalidOperationException("Comparator type not recognised."); } } public bool Intersects(Comparator other) { Func func = (Comparator c) => c.ComparatorType == Operator.GreaterThan || c.ComparatorType == Operator.GreaterThanOrEqual || c.ComparatorType == Operator.GreaterThanOrEqualIncludingPrereleases; Func func2 = (Comparator c) => c.ComparatorType == Operator.LessThan || c.ComparatorType == Operator.LessThanOrEqual || c.ComparatorType == Operator.LessThanExcludingPrereleases; Func func3 = (Comparator c) => c.ComparatorType == Operator.GreaterThanOrEqual || c.ComparatorType == Operator.GreaterThanOrEqualIncludingPrereleases || c.ComparatorType == Operator.Equal || c.ComparatorType == Operator.LessThanOrEqual; if (Version > other.Version && (func2(this) || func(other))) { return true; } if (Version < other.Version && (func(this) || func2(other))) { return true; } if (Version == other.Version && ((func3(this) && func3(other)) || (func2(this) && func2(other)) || (func(this) && func(other)))) { return true; } return false; } public override string ToString() { string text = null; switch (ComparatorType) { case Operator.Equal: text = "="; break; case Operator.LessThan: case Operator.LessThanExcludingPrereleases: text = "<"; break; case Operator.LessThanOrEqual: text = "<="; break; case Operator.GreaterThan: text = ">"; break; case Operator.GreaterThanOrEqual: case Operator.GreaterThanOrEqualIncludingPrereleases: text = ">="; break; default: throw new InvalidOperationException("Comparator type not recognised."); } return $"{text}{Version}"; } public bool Equals(Comparator other) { if (other == null) { return false; } if (ComparatorType == other.ComparatorType) { return Version == other.Version; } return false; } public override bool Equals(object other) { return Equals(other as Comparator); } public override int GetHashCode() { return new { ComparatorType, Version }.GetHashCode(); } } internal class ComparatorSet : IEquatable { private readonly List _comparators; public ComparatorSet(string spec) { _comparators = new List(); spec = spec.Trim(); if (spec == "") { spec = "*"; } int num = 0; int length = spec.Length; while (num < length) { int num2 = num; Func>[] array = new Func>[4] { Desugarer.HyphenRange, Desugarer.TildeRange, Desugarer.CaretRange, Desugarer.StarRange }; for (int i = 0; i < array.Length; i++) { Tuple tuple = array[i](spec.Substring(num)); if (tuple != null) { num += tuple.Item1; _comparators.AddRange(tuple.Item2); } } Tuple tuple2 = Comparator.TryParse(spec.Substring(num)); if (tuple2 != null) { num += tuple2.Item1; _comparators.Add(tuple2.Item2); } if (num == num2) { throw new ArgumentException($"Invalid range specification: \"{spec}\""); } } } private ComparatorSet(IEnumerable comparators) { _comparators = comparators.ToList(); } public bool IsSatisfied(Version version, bool includePrerelease = false) { bool flag = _comparators.All((Comparator c) => c.IsSatisfied(version)); if (version.PreRelease != null && !includePrerelease) { if (flag) { return _comparators.Any((Comparator c) => c.Version.PreRelease != null && c.Version.BaseVersion() == version.BaseVersion()); } return false; } return flag; } public ComparatorSet Intersect(ComparatorSet other) { Func predicate = (Comparator c) => c.ComparatorType == Comparator.Operator.GreaterThan || c.ComparatorType == Comparator.Operator.GreaterThanOrEqual || c.ComparatorType == Comparator.Operator.GreaterThanOrEqualIncludingPrereleases; Func predicate2 = (Comparator c) => c.ComparatorType == Comparator.Operator.LessThan || c.ComparatorType == Comparator.Operator.LessThanOrEqual || c.ComparatorType == Comparator.Operator.LessThanExcludingPrereleases; Func operatorOrdering = (Comparator.Operator op) => op switch { Comparator.Operator.LessThanExcludingPrereleases => 0, Comparator.Operator.LessThan => 1, Comparator.Operator.LessThanOrEqual => 2, Comparator.Operator.GreaterThan => 0, Comparator.Operator.GreaterThanOrEqual => 1, Comparator.Operator.GreaterThanOrEqualIncludingPrereleases => 2, _ => throw new ArgumentOutOfRangeException("op", op, "Unexpected comparator operator"), }; Comparator comparator = (from c in _comparators.Concat(other._comparators).Where(predicate) orderby c.Version descending, operatorOrdering(c.ComparatorType) select c).FirstOrDefault(); Comparator comparator2 = (from c in _comparators.Concat(other._comparators).Where(predicate2) orderby c.Version, operatorOrdering(c.ComparatorType) select c).FirstOrDefault(); if (comparator != null && comparator2 != null && !comparator.Intersects(comparator2)) { return null; } List equalityVersions = (from c in _comparators.Concat(other._comparators) where c.ComparatorType == Comparator.Operator.Equal select c.Version).ToList(); if (equalityVersions.Count > 1 && equalityVersions.Any((Version v) => v != equalityVersions[0])) { return null; } if (equalityVersions.Count > 0) { if (comparator != null && !comparator.IsSatisfied(equalityVersions[0])) { return null; } if (comparator2 != null && !comparator2.IsSatisfied(equalityVersions[0])) { return null; } return new ComparatorSet(new List { new Comparator(Comparator.Operator.Equal, equalityVersions[0]) }); } List list = new List(); if (comparator != null) { list.Add(comparator); } if (comparator2 != null) { list.Add(comparator2); } if (list.Count <= 0) { return null; } return new ComparatorSet(list); } public bool Equals(ComparatorSet other) { if (other == null) { return false; } return new HashSet(_comparators).SetEquals(other._comparators); } public override bool Equals(object other) { return Equals(other as ComparatorSet); } public override string ToString() { return string.Join(" ", _comparators.Select((Comparator c) => c.ToString()).ToArray()); } public override int GetHashCode() { return _comparators.Aggregate(0, (int accum, Comparator next) => accum ^ next.GetHashCode()); } } internal static class Desugarer { private const string versionChars = "[0-9a-zA-Z\\-\\+\\.\\*]"; public static Tuple TildeRange(string spec) { Match match = new Regex(string.Format("^\\s*~\\s*({0}+)\\s*", "[0-9a-zA-Z\\-\\+\\.\\*]")).Match(spec); if (!match.Success) { return null; } Version version = null; Version version2 = null; PartialVersion partialVersion = new PartialVersion(match.Groups[1].Value); if (partialVersion.Minor.HasValue) { version = partialVersion.ToZeroVersion(); version2 = new Version(partialVersion.Major.Value, partialVersion.Minor.Value + 1, 0); } else { version = partialVersion.ToZeroVersion(); version2 = new Version(partialVersion.Major.Value + 1, 0, 0); } return Tuple.Create(match.Length, MinMaxComparators(version, version2)); } public static Tuple CaretRange(string spec) { Match match = new Regex(string.Format("^\\s*\\^\\s*({0}+)\\s*", "[0-9a-zA-Z\\-\\+\\.\\*]")).Match(spec); if (!match.Success) { return null; } Version version = null; Version version2 = null; PartialVersion partialVersion = new PartialVersion(match.Groups[1].Value); if (partialVersion.Major.Value > 0) { version = partialVersion.ToZeroVersion(); version2 = new Version(partialVersion.Major.Value + 1, 0, 0); } else if (!partialVersion.Minor.HasValue) { version = partialVersion.ToZeroVersion(); version2 = new Version(partialVersion.Major.Value + 1, 0, 0); } else if (!partialVersion.Patch.HasValue) { version = partialVersion.ToZeroVersion(); version2 = new Version(0, partialVersion.Minor.Value + 1, 0); } else if (partialVersion.Minor > 0) { version = partialVersion.ToZeroVersion(); version2 = new Version(0, partialVersion.Minor.Value + 1, 0); } else { version = partialVersion.ToZeroVersion(); version2 = new Version(0, 0, partialVersion.Patch.Value + 1); } return Tuple.Create(match.Length, MinMaxComparators(version, version2, Comparator.Operator.GreaterThanOrEqualIncludingPrereleases)); } public static Tuple HyphenRange(string spec) { Match match = new Regex(string.Format("^\\s*({0}+)\\s+\\-\\s+({0}+)\\s*", "[0-9a-zA-Z\\-\\+\\.\\*]")).Match(spec); if (!match.Success) { return null; } PartialVersion partialVersion = null; PartialVersion partialVersion2 = null; try { partialVersion = new PartialVersion(match.Groups[1].Value); partialVersion2 = new PartialVersion(match.Groups[2].Value); } catch (ArgumentException) { return null; } Version minVersion = partialVersion.ToZeroVersion(); Comparator.Operator maxOperator = (partialVersion2.IsFull() ? Comparator.Operator.LessThanOrEqual : Comparator.Operator.LessThanExcludingPrereleases); Version maxVersion = null; if (partialVersion2.Major.HasValue) { maxVersion = ((!partialVersion2.Minor.HasValue) ? new Version(partialVersion2.Major.Value + 1, 0, 0) : (partialVersion2.Patch.HasValue ? partialVersion2.ToZeroVersion() : new Version(partialVersion2.Major.Value, partialVersion2.Minor.Value + 1, 0))); } return Tuple.Create(match.Length, MinMaxComparators(minVersion, maxVersion, Comparator.Operator.GreaterThanOrEqualIncludingPrereleases, maxOperator)); } public static Tuple StarRange(string spec) { Match match = new Regex(string.Format("^\\s*=?\\s*({0}+)\\s*", "[0-9a-zA-Z\\-\\+\\.\\*]")).Match(spec); if (!match.Success) { return null; } PartialVersion partialVersion = null; try { partialVersion = new PartialVersion(match.Groups[1].Value); } catch (ArgumentException) { return null; } if (partialVersion.IsFull()) { return null; } Version version = null; Version maxVersion = null; if (!partialVersion.Major.HasValue) { version = partialVersion.ToZeroVersion(); } else if (!partialVersion.Minor.HasValue) { version = partialVersion.ToZeroVersion(); maxVersion = new Version(partialVersion.Major.Value + 1, 0, 0); } else { version = partialVersion.ToZeroVersion(); maxVersion = new Version(partialVersion.Major.Value, partialVersion.Minor.Value + 1, 0); } return Tuple.Create(match.Length, MinMaxComparators(version, maxVersion)); } private static Comparator[] MinMaxComparators(Version minVersion, Version maxVersion, Comparator.Operator minOperator = Comparator.Operator.GreaterThanOrEqual, Comparator.Operator maxOperator = Comparator.Operator.LessThanExcludingPrereleases) { Comparator comparator = new Comparator(minOperator, minVersion); if (!(maxVersion == null)) { Comparator comparator2 = new Comparator(maxOperator, maxVersion); return new Comparator[2] { comparator, comparator2 }; } return new Comparator[1] { comparator }; } } internal class PartialVersion { private static Regex regex = new Regex("^\n [v=\\s]*\n (\\d+|[Xx\\*]) # major version\n (\n \\.\n (\\d+|[Xx\\*]) # minor version\n (\n \\.\n (\\d+|[Xx\\*]) # patch version\n (\\-?([0-9A-Za-z\\-\\.]+))? # pre-release version\n (\\+([0-9A-Za-z\\-\\.]+))? # build version (ignored)\n )?\n )?\n $", RegexOptions.IgnorePatternWhitespace); public int? Major { get; set; } public int? Minor { get; set; } public int? Patch { get; set; } public string PreRelease { get; set; } public PartialVersion(string input) { string[] source = new string[3] { "X", "x", "*" }; if (input.Trim() == "") { return; } Match match = regex.Match(input); if (!match.Success) { throw new ArgumentException($"Invalid version string: \"{input}\""); } if (source.Contains(match.Groups[1].Value)) { Major = null; } else { Major = int.Parse(match.Groups[1].Value); } if (match.Groups[2].Success) { if (source.Contains(match.Groups[3].Value)) { Minor = null; } else { Minor = int.Parse(match.Groups[3].Value); } } if (match.Groups[4].Success) { if (source.Contains(match.Groups[5].Value)) { Patch = null; } else { Patch = int.Parse(match.Groups[5].Value); } } if (match.Groups[6].Success) { PreRelease = match.Groups[7].Value; } } public Version ToZeroVersion() { return new Version(Major.GetValueOrDefault(), Minor.GetValueOrDefault(), Patch.GetValueOrDefault(), PreRelease); } public bool IsFull() { if (Major.HasValue && Minor.HasValue) { return Patch.HasValue; } return false; } } internal static class PreReleaseVersion { private class Identifier { public bool IsNumeric { get; set; } public int IntValue { get; set; } public string Value { get; set; } public Identifier(string input) { Value = input; SetNumeric(); } public string Clean() { if (!IsNumeric) { return Value; } return IntValue.ToString(); } private void SetNumeric() { int result; bool flag = int.TryParse(Value, out result); IsNumeric = flag && result >= 0; IntValue = result; } } public static int Compare(string a, string b) { if (a == null && b == null) { return 0; } if (a == null) { return 1; } if (b == null) { return -1; } foreach (int item in IdentifierComparisons(Identifiers(a), Identifiers(b))) { if (item != 0) { return item; } } return 0; } public static string Clean(string input) { IEnumerable source = from i in Identifiers(input) select i.Clean(); return string.Join(".", source.ToArray()); } private static IEnumerable Identifiers(string input) { string[] array = input.Split(new char[1] { '.' }); foreach (string input2 in array) { yield return new Identifier(input2); } } private static IEnumerable IdentifierComparisons(IEnumerable aIdentifiers, IEnumerable bIdentifiers) { foreach (Tuple item3 in ZipIdentifiers(aIdentifiers, bIdentifiers)) { Identifier item = item3.Item1; Identifier item2 = item3.Item2; if (item == item2) { yield return 0; } else if (item == null) { yield return -1; } else if (item2 == null) { yield return 1; } else if (item.IsNumeric && item2.IsNumeric) { yield return item.IntValue.CompareTo(item2.IntValue); } else if (!item.IsNumeric && !item2.IsNumeric) { yield return string.CompareOrdinal(item.Value, item2.Value); } else if (item.IsNumeric && !item2.IsNumeric) { yield return -1; } else { yield return 1; } } } private static IEnumerable> ZipIdentifiers(IEnumerable first, IEnumerable second) { using IEnumerator ie1 = first.GetEnumerator(); using IEnumerator ie2 = second.GetEnumerator(); while (ie1.MoveNext()) { if (ie2.MoveNext()) { yield return Tuple.Create(ie1.Current, ie2.Current); } else { yield return Tuple.Create(ie1.Current, null); } } while (ie2.MoveNext()) { yield return Tuple.Create(null, ie2.Current); } } } public class Range : IEquatable { private readonly ComparatorSet[] _comparatorSets; private readonly string _rangeSpec; public Range(string rangeSpec, bool loose = false) { _rangeSpec = rangeSpec; string[] source = rangeSpec.Split(new string[1] { "||" }, StringSplitOptions.None); _comparatorSets = source.Select((string s) => new ComparatorSet(s)).ToArray(); } private Range(IEnumerable comparatorSets) { _comparatorSets = comparatorSets.ToArray(); _rangeSpec = string.Join(" || ", _comparatorSets.Select((ComparatorSet cs) => cs.ToString()).ToArray()); } public bool IsSatisfied(Version version, bool includePrerelease = false) { return _comparatorSets.Any((ComparatorSet s) => s.IsSatisfied(version, includePrerelease)); } public bool IsSatisfied(string versionString, bool loose = false, bool includePrerelease = false) { try { Version version = new Version(versionString, loose); return IsSatisfied(version, includePrerelease); } catch (ArgumentException) { return false; } } public IEnumerable Satisfying(IEnumerable versions, bool includePrerelease = false) { return versions.Where((Version v) => IsSatisfied(v, includePrerelease)); } public IEnumerable Satisfying(IEnumerable versions, bool loose = false, bool includePrerelease = false) { return versions.Where((string v) => IsSatisfied(v, loose, includePrerelease)); } public Version MaxSatisfying(IEnumerable versions, bool includePrerelease = false) { return Satisfying(versions, includePrerelease).Max(); } public string MaxSatisfying(IEnumerable versionStrings, bool loose = false, bool includePrerelease = false) { IEnumerable versions = ValidVersions(versionStrings, loose); Version version = MaxSatisfying(versions, includePrerelease); if (!(version == null)) { return version.ToString(); } return null; } public Range Intersect(Range other) { List list = (from cs in _comparatorSets.SelectMany((ComparatorSet thisCs) => other._comparatorSets.Select(thisCs.Intersect)) where cs != null select cs).ToList(); if (list.Count == 0) { return new Range("<0.0.0"); } return new Range(list); } public override string ToString() { return _rangeSpec; } public bool Equals(Range other) { if ((object)other == null) { return false; } return new HashSet(_comparatorSets).SetEquals(other._comparatorSets); } public override bool Equals(object other) { return Equals(other as Range); } public static bool operator ==(Range a, Range b) { return a?.Equals(b) ?? ((object)b == null); } public static bool operator !=(Range a, Range b) { return !(a == b); } public override int GetHashCode() { return _comparatorSets.Aggregate(0, (int accum, ComparatorSet next) => accum ^ next.GetHashCode()); } public static bool IsSatisfied(string rangeSpec, string versionString, bool loose = false, bool includePrerelease = false) { return new Range(rangeSpec).IsSatisfied(versionString, loose, includePrerelease); } public static IEnumerable Satisfying(string rangeSpec, IEnumerable versions, bool loose = false, bool includePrerelease = false) { return new Range(rangeSpec).Satisfying(versions, loose, includePrerelease); } public static string MaxSatisfying(string rangeSpec, IEnumerable versionStrings, bool loose = false, bool includePrerelease = false) { return new Range(rangeSpec).MaxSatisfying(versionStrings, loose: false, includePrerelease); } public static Range Parse(string rangeSpec, bool loose = false) { return new Range(rangeSpec, loose); } public static bool TryParse(string rangeSpec, out Range result) { return TryParse(rangeSpec, loose: false, out result); } public static bool TryParse(string rangeSpec, bool loose, out Range result) { try { result = Parse(rangeSpec, loose); return true; } catch { result = null; return false; } } private IEnumerable ValidVersions(IEnumerable versionStrings, bool loose) { foreach (string versionString in versionStrings) { Version version = null; try { version = new Version(versionString, loose); } catch (ArgumentException) { } if (version != null) { yield return version; } } } } public class Version : IComparable, IComparable, IEquatable { private readonly string _inputString; private readonly int _major; private readonly int _minor; private readonly int _patch; private readonly string _preRelease; private readonly string _build; private static Regex strictRegex = new Regex("^\n \\s*v?\n ([0-9]|[1-9][0-9]+) # major version\n \\.\n ([0-9]|[1-9][0-9]+) # minor version\n \\.\n ([0-9]|[1-9][0-9]+) # patch version\n (\\-([0-9A-Za-z\\-\\.]+))? # pre-release version\n (\\+([0-9A-Za-z\\-\\.]+))? # build metadata\n \\s*\n $", RegexOptions.IgnorePatternWhitespace); private static Regex looseRegex = new Regex("^\n [v=\\s]*\n (\\d+) # major version\n \\.\n (\\d+) # minor version\n \\.\n (\\d+) # patch version\n (\\-?([0-9A-Za-z\\-\\.]+))? # pre-release version\n (\\+([0-9A-Za-z\\-\\.]+))? # build metadata\n \\s*\n $", RegexOptions.IgnorePatternWhitespace); public int Major => _major; public int Minor => _minor; public int Patch => _patch; public string PreRelease => _preRelease; public string Build => _build; public bool IsPreRelease => !string.IsNullOrEmpty(_preRelease); public Version(string input, bool loose = false) { _inputString = input; Match match = (loose ? looseRegex : strictRegex).Match(input); if (!match.Success) { throw new ArgumentException($"Invalid version string: {input}"); } _major = int.Parse(match.Groups[1].Value); _minor = int.Parse(match.Groups[2].Value); _patch = int.Parse(match.Groups[3].Value); if (match.Groups[4].Success) { string value = match.Groups[5].Value; string text = PreReleaseVersion.Clean(value); if (!loose && value != text) { throw new ArgumentException($"Invalid pre-release version: {value}"); } _preRelease = text; } if (match.Groups[6].Success) { _build = match.Groups[7].Value; } } public Version(int major, int minor, int patch, string preRelease = null, string build = null) { _major = major; _minor = minor; _patch = patch; _preRelease = preRelease; _build = build; } public Version BaseVersion() { return new Version(Major, Minor, Patch); } public override string ToString() { return _inputString ?? Clean(); } public string Clean() { string text = ((PreRelease == null) ? "" : $"-{PreReleaseVersion.Clean(PreRelease)}"); string text2 = ((Build == null) ? "" : $"+{Build}"); return $"{Major}.{Minor}.{Patch}{text}{text2}"; } public override int GetHashCode() { int num = 17; num = num * 23 + Major.GetHashCode(); num = num * 23 + Minor.GetHashCode(); num = num * 23 + Patch.GetHashCode(); if (PreRelease != null) { num = num * 23 + PreRelease.GetHashCode(); } return num; } public bool Equals(Version other) { if ((object)other == null) { return false; } return CompareTo(other) == 0; } public int CompareTo(object obj) { if (obj != null) { if (obj is Version other) { return CompareTo(other); } throw new ArgumentException("Object is not a Version"); } return 1; } public int CompareTo(Version other) { if ((object)other == null) { return 1; } foreach (int item in PartComparisons(other)) { if (item != 0) { return item; } } return PreReleaseVersion.Compare(PreRelease, other.PreRelease); } private IEnumerable PartComparisons(Version other) { yield return Major.CompareTo(other.Major); yield return Minor.CompareTo(other.Minor); yield return Patch.CompareTo(other.Patch); } public override bool Equals(object other) { return Equals(other as Version); } public static Version Parse(string input, bool loose = false) { return new Version(input, loose); } public static bool TryParse(string input, out Version result) { return TryParse(input, loose: false, out result); } public static bool TryParse(string input, bool loose, out Version result) { try { result = Parse(input, loose); return true; } catch { result = null; return false; } } public static bool operator ==(Version a, Version b) { return a?.Equals(b) ?? ((object)b == null); } public static bool operator !=(Version a, Version b) { return !(a == b); } public static bool operator >(Version a, Version b) { if ((object)a == null) { return false; } return a.CompareTo(b) > 0; } public static bool operator >=(Version a, Version b) { if ((object)a == null) { if ((object)b != null) { return false; } return true; } return a.CompareTo(b) >= 0; } public static bool operator <(Version a, Version b) { if ((object)a == null) { if ((object)b != null) { return true; } return false; } return a.CompareTo(b) < 0; } public static bool operator <=(Version a, Version b) { if ((object)a == null) { return true; } return a.CompareTo(b) <= 0; } } }