/// <summary> /// Initializes a new valid <see cref="ParseResult"/>. /// </summary> /// <param name="result">The version bound.</param> /// <param name="isApproximated">Whether the version bound is an approximation.</param> /// <param name="fourthPartLost">Whether a 4th (or more) part (like in "1.2.3.4") has been ignored.</param> public ParseResult(SVersionBound result, bool isApproximated, bool fourthPartLost) { Result = result; IsApproximated = isApproximated; FourthPartLost = fourthPartLost; Error = null; }
/// <summary> /// Tries to parse a version bound: it is a <see cref="SVersion.TryParse(ref ReadOnlySpan{char}, bool, bool, bool)"/> that may be /// followed by an optional bracketed "[<see cref="TryParseLockAndMinQuality"/>]". /// The head is forwarded right after the match: on success, the head may be on any kind of character. /// </summary> /// <param name="head">The string to parse (leading and internal white spaces between tokens are skipped).</param> /// <param name="bound">The result. This is <see cref="SVersionBound.None"/> on error.</param> /// <param name="defaultLock">Default lock to apply if none is provided.</param> /// <param name="defaultQuality">Default quality to apply if none is provided.</param> /// <returns>True on success, false otherwise.</returns> public static bool TryParse(ref ReadOnlySpan <char> head, out SVersionBound bound, SVersionLock defaultLock = SVersionLock.None, PackageQuality defaultQuality = PackageQuality.None) { var sHead = head; bound = SVersionBound.None; var v = SVersion.TryParse(ref Trim(ref head), checkBuildMetaDataSyntax: false); if (!v.IsValid) { head = sHead; return(false); } SVersionLock l = SVersionLock.None; PackageQuality q = PackageQuality.None; if (Trim(ref head).Length > 0 && TryMatch(ref head, '[')) { // Allows empty []. Note that TryParseLockAndMinQuality calls Trim. TryParseLockAndMinQuality(ref head, out l, out q); // Match the closing ] if it's here. Ignores it if it's not here. if (Trim(ref head).Length > 0) { TryMatch(ref head, ']'); } } if (l == SVersionLock.None) { l = defaultLock; } if (q == PackageQuality.None) { q = defaultQuality; } bound = new SVersionBound(v, l, q); return(true); }
static ParseResult TryMatchHeadRange(ref ReadOnlySpan <char> s, bool includePrerelease) { // Handling the marvelous "" (empty string), that is like '*'. if (s.Length == 0) { return(new ParseResult(new SVersionBound(_000Version, SVersionLock.None, includePrerelease ? PackageQuality.None : PackageQuality.Stable), isApproximated: !includePrerelease, false)); } if (TryMatch(ref s, '>')) { bool isApproximated = !TryMatch(ref s, '='); return(TryMatchRangeAlone(ref Trim(ref s), SVersionLock.None, includePrerelease).Result.EnsureIsApproximated(isApproximated)); } if (TryMatch(ref s, '<')) { if (TryMatch(ref s, '=')) { return(TryMatchRangeAlone(ref Trim(ref s), SVersionLock.Lock, includePrerelease).Result.EnsureIsApproximated(true)); } // We totally ignore any '<'... var forget = TryMatchRangeAlone(ref Trim(ref s), SVersionLock.None, includePrerelease).Result; return(forget.Error != null ? forget : new ParseResult(SVersionBound.All.SetMinQuality(includePrerelease ? PackageQuality.CI : PackageQuality.Stable), true, forget.FourthPartLost)); } if (TryMatch(ref s, '~')) { return(TryMatchRangeAlone(ref Trim(ref s), SVersionLock.LockMinor, includePrerelease).Result); } if (TryMatch(ref s, '^')) { var r = TryMatchRangeAlone(ref Trim(ref s), SVersionLock.LockMajor, includePrerelease); if (r.Result.Error == null) { bool noMoreApproximation = false; SVersionBound v = r.Result.Result; if (v.Base.Major == 0) { if (v.Lock == SVersionLock.LockMajor && !r.IsFloatingMinor) { v = v.SetLock(SVersionLock.LockMinor); if (v.Base.Minor == 0 && v.Lock == SVersionLock.LockMinor) { v = v.SetLock(SVersionLock.LockPatch); noMoreApproximation = true; } } } else { // Major != 0. if (v.Lock > SVersionLock.LockMajor) { v = v.SetLock(SVersionLock.LockMajor); } } r.Result = r.Result.SetResult(v); if (noMoreApproximation) { r.Result = r.Result.ClearApproximated(); } } return(r.Result); } // '=' prefix is optional. if (TryMatch(ref s, '=')) { Trim(ref s); } return(TryMatchRangeAlone(ref s, SVersionLock.Lock, includePrerelease).Result); }
/// <summary> /// Applies a new <see cref="Result"/> and returns this or a new result. /// </summary> /// <param name="result">The new result.</param> /// <returns>This or a new result.</returns> public ParseResult SetResult(SVersionBound result) => result.Equals(Result) ? this : new ParseResult(result, IsApproximated, FourthPartLost);
/// <summary> /// Tries to parse a version bound: it is a <see cref="SVersion.TryParse(ref ReadOnlySpan{char}, bool, bool, bool)"/> that may be /// followed by an optional bracketed "[<see cref="TryParseLockAndMinQuality"/>]". /// </summary> /// <param name="head">The string to parse (leading and internal white spaces between tokens are skipped).</param> /// <param name="bound">The result. This is <see cref="SVersionBound.None"/> on error.</param> /// <param name="defaultLock">The <see cref="SVersionLock"/> to use when no lock appears in the string to parse.</param> /// <param name="defaultQuality">The <see cref="PackageQuality"/> to use when no lock appears in the string to parse.</param> /// <returns>True on success, false otherwise.</returns> public static bool TryParse(ReadOnlySpan <char> head, out SVersionBound bound, SVersionLock defaultLock = SVersionLock.None, PackageQuality defaultQuality = PackageQuality.None) => TryParse(ref head, out bound, defaultLock, defaultQuality);
/// <summary> /// Initializes a new <see cref="ParseResult"/> on error. /// </summary> /// <param name="error">The error message.</param> public ParseResult(string error) { Result = SVersionBound.None; IsApproximated = FourthPartLost = false; Error = error ?? throw new ArgumentNullException(nameof(error)); }