2using System.Collections.Immutable;
6using System.Text.RegularExpressions;
15 internal readonly
struct RuntimeVersion : IComparable<RuntimeVersion>
27 static readonly
string VNUM =
"(?<VNUM>[1-9][0-9]*(?:(?:\\.0)*\\.[1-9][0-9]*)*)";
28 static readonly
string PRE =
"(?:-(?<PRE>[a-zA-Z0-9]+))?";
29 static readonly
string BUILD =
"(?:(?<PLUS>\\+)(?<BUILD>0|[1-9][0-9]*)?)?";
30 static readonly
string OPT =
"(?:-(?<OPT>[-a-zA-Z0-9.]+))?";
31 static readonly
string VSTR_FORMAT = VNUM + PRE + BUILD + OPT;
39 public static readonly Regex
VSTR_PATTERN =
new Regex(VSTR_FORMAT, RegexOptions.Compiled);
49 public static RuntimeVersion Parse(
string value)
51 if (IsSimpleNumber(value.AsSpan()))
52 return new RuntimeVersion([
int.Parse(value)],
null,
null,
null);
55 if (m.Success ==
false)
56 throw new ArgumentException(
"Invalid version string: '" + value.ToString() +
"'", nameof(value));
60 var version = ImmutableArray.CreateBuilder<
int>(split.Length);
61 for (
int i = 0; i < split.Length; i++)
62 version.Add(
int.Parse(split[i]));
65 var pre = preGroup.Success ? preGroup.Value :
null;
68 var build = buildGroup.Success ? (
int?)
int.Parse(buildGroup.Value) : null;
71 var optional = optionalGroup.Success ? optionalGroup.Value :
null;
78 if (optional is not
null)
81 throw new ArgumentException($
"'+' found with pre-release and optional components: '{value}'");
85 throw new ArgumentException($
"'+' found with neither build or optional components: '{value}'");
90 if (optional is not
null && pre is
null)
91 throw new ArgumentException($
"optional component must be preceded by a pre-release component or '+': '{value}'");
95 return new RuntimeVersion(version.DrainToImmutable(), pre, build, optional);
103 static bool IsSimpleNumber(ReadOnlySpan<char> s)
105 for (
int i = 0; i < s.Length; i++)
108 var lowerBound = (i > 0) ?
'0' :
'1';
109 if (c < lowerBound || c >
'9')
116 readonly ImmutableArray<int> _version;
117 readonly
string? _pre;
118 readonly
int? _build;
119 readonly
string? _optional;
128 public RuntimeVersion(ImmutableArray<int> version,
string? pre,
int? build,
string? optional)
133 _optional = optional;
139 public readonly
int Feature => _version[0];
144 public readonly
int Interim => _version.Length > 1 ? _version[1] : 0;
149 public readonly
int Update => _version.Length > 2 ? _version[2] : 0;
154 public ImmutableArray<int> Version => _version;
159 public readonly
string? Pre => _pre;
164 public readonly
int? Build => _build;
169 public readonly
string? Optional => _optional;
172 public override readonly
bool Equals(
object? obj)
174 return obj is RuntimeVersion other && Equals(other);
182 public readonly
bool Equals(RuntimeVersion other)
185 _version.SequenceEqual(other._version) &&
186 _pre == other._pre &&
187 _build == other._build;
191 public override readonly
int GetHashCode()
193 var hc =
new HashCode();
195 foreach (var i
in _version)
202 return hc.ToHashCode();
206 public int CompareTo(RuntimeVersion other)
208 return CompareTo(other,
false);
217 readonly
int CompareTo(RuntimeVersion other,
bool ignoreOptional)
219 int ret = CompareVersion(other);
223 ret = ComparePre(other);
227 ret = CompareBuild(other);
231 if (ignoreOptional ==
false)
232 return CompareOptional(other);
242 readonly
int CompareVersion(RuntimeVersion other)
244 int size = _version.Length;
245 int oSize = other._version.Length;
246 int min = Math.Min(size, oSize);
247 for (
int i = 0; i < min; i++)
249 int val = _version[i];
250 int oVal = other._version[i];
263 readonly
int ComparePre(RuntimeVersion other)
267 if (other._pre is not
null)
272 if (other._pre is
null)
275 if (Regex.IsMatch(_pre,
"\\d+"))
277 return Regex.IsMatch(other._pre,
"\\d+")
278 ? (BigInteger.Parse(_pre)).CompareTo(BigInteger.Parse(other._pre))
283 return Regex.IsMatch(other._pre,
"\\d+")
285 : _pre.CompareTo(other._pre);
297 readonly
int CompareBuild(RuntimeVersion obj)
299 if (obj._build is not
null)
301 return _build is not
null
302 ? _build.Value.CompareTo(obj._build.Value)
305 else if (_build is not
null)
318 readonly
int CompareOptional(RuntimeVersion other)
320 if (_optional is
null)
322 if (other._optional is not
null)
327 if (other._optional is
null)
330 return _optional.CompareTo(other._optional);
337 public override readonly
string? ToString()
339 var sb =
new ValueStringBuilder(16);
340 sb.Append(
string.Join(
".", _version));
342 if (_pre is not
null)
348 if (_build is not
null)
351 sb.Append(_build.ToString());
353 if (_optional is not
null)
356 sb.Append(_optional);
361 if (_optional is not
null)
363 sb.Append(_pre is not
null ?
"-" :
"+-");
364 sb.Append(_optional);
368 return sb.ToString();
Various utilities for parsing versions.
static readonly string VNUM_GROUP
static readonly string PRE_GROUP
static readonly string OPT_GROUP
static readonly string BUILD_GROUP
static readonly string PLUS_GROUP
static readonly Regex VSTR_PATTERN