Example #1
0
 /// <summary>
 /// Initializes a new version range on a valid <see cref="Base"/> version.
 /// </summary>
 /// <param name="version">The base version that must be valid.</param>
 /// <param name="r">The lock to apply.</param>
 /// <param name="minQuality">The minimal quality to accept.</param>
 public SVersionBound(SVersion?version = null, SVersionLock r = SVersionLock.None, PackageQuality minQuality = PackageQuality.None)
 {
     _base = version ?? SVersion.ZeroVersion;
     if (!_base.IsValid)
     {
         throw new ArgumentException("Must be valid. Error: " + _base.ErrorMessage, nameof(version));
     }
     if (minQuality == PackageQuality.Stable && r == SVersionLock.LockPatch)
     {
         r = SVersionLock.Lock;
     }
     _minQuality = minQuality;
     Lock        = r;
 }
 /// <summary>
 /// Tries to parse one of the <see cref="SVersionLock"/> terms (the <paramref name="head"/> must be at the start, no trimming is done).
 /// Note that match is case insensitive and that all "Lock" wan be written as "Locked".
 /// The <paramref name="head"/> is forwarded right after the match: the head may be on any kind of character.
 /// </summary>
 /// <param name="head">The string to parse.</param>
 /// <param name="l">The read lock.</param>
 /// <returns>True on success, false otherwise.</returns>
 public static bool TryMatch(ref ReadOnlySpan <char> head, out SVersionLock l)
 {
     l = SVersionLock.None;
     if (head.Length == 0)
     {
         return(false);
     }
     if (!head.StartsWith(nameof(SVersionLock.Lock), StringComparison.OrdinalIgnoreCase))
     {
         if (head.StartsWith(nameof(SVersionLock.None), StringComparison.OrdinalIgnoreCase))
         {
             head = head.Slice(4);
             return(true);
         }
         return(false);
     }
     head = head.Slice(4);
     if (head.StartsWith("ed", StringComparison.OrdinalIgnoreCase))
     {
         head = head.Slice(2);
     }
     if (head.StartsWith("major", StringComparison.OrdinalIgnoreCase))
     {
         head = head.Slice(5);
         l    = SVersionLock.LockMajor;
         return(true);
     }
     if (head.StartsWith("minor", StringComparison.OrdinalIgnoreCase))
     {
         head = head.Slice(5);
         l    = SVersionLock.LockMinor;
         return(true);
     }
     if (head.StartsWith("patch", StringComparison.OrdinalIgnoreCase))
     {
         head = head.Slice(5);
         l    = SVersionLock.LockPatch;
         return(true);
     }
     l = SVersionLock.Lock;
     return(true);
 }
Example #3
0
        /// <summary>
        /// Tries to parse "<see cref="SVersionLock"/>[,<see cref="PackageQuality"/>]" or "<see cref="PackageQuality"/>[,<see cref="SVersionLock"/>]".
        /// Note that match is case insensitive, that white spaces are silently ignored, that all "Lock" wan be written as "Locked" and that "rc" is
        /// a synonym of <see cref="PackageQuality.ReleaseCandidate"/>.
        /// The <paramref name="head"/> is forwarded right after the match: the head may be on any kind of character.
        /// </summary>
        /// <param name="head">The string to parse (leading and internal white spaces are skipped).</param>
        /// <param name="l">The read lock.</param>
        /// <param name="q">The read quality.</param>
        /// <returns>True on success, false otherwise.</returns>
        public static bool TryParseLockAndMinQuality(ref ReadOnlySpan <char> head,
                                                     out SVersionLock l,
                                                     out PackageQuality q)
        {
            var sSaved = head;

            l = SVersionLock.None;
            q = PackageQuality.None;
            if (head.Length == 0)
            {
                return(false);
            }
            if (SVersionLockExtension.TryMatch(ref Trim(ref head), out l))
            {
                if (TryMatch(ref Trim(ref head), ','))
                {
                    // This handles None,None: if the first read is "None", then it could have been the "None" of the quality:
                    // if we don't match a Quality we give a second chance to the Lock.
                    if (!PackageQualityExtension.TryMatch(ref Trim(ref head), out q) && l == SVersionLock.None)
                    {
                        SVersionLockExtension.TryMatch(ref Trim(ref head), out l);
                    }
                    return(true);
                }
                return(true);
            }
            if (PackageQualityExtension.TryMatch(ref Trim(ref head), out q))
            {
                if (TryMatch(ref Trim(ref head), ','))
                {
                    SVersionLockExtension.TryMatch(ref Trim(ref head), out l);
                    return(true);
                }
                return(true);
            }
            head = sSaved;
            return(false);
        }
Example #4
0
        /// <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);
        }
Example #5
0
 /// <summary>
 /// Sets a lock by returning this or a new <see cref="SVersionBound"/>.
 /// </summary>
 /// <param name="r">The lock to set.</param>
 /// <returns>This or a new range.</returns>
 public SVersionBound SetLock(SVersionLock r) => r != Lock ? new SVersionBound(Base, r, MinQuality) : this;
 /// <summary>
 /// Tries to parse one of the <see cref="SVersionLock"/> terms (the <paramref name="head"/> must be at the start, no trimming is done).
 /// Note that match is case insensitive and that all "Lock" wan be written as "Locked".
 /// </summary>
 /// <param name="head">The string to parse.</param>
 /// <param name="l">The read lock.</param>
 /// <returns>True on success, false otherwise.</returns>
 public static bool TryMatch(ReadOnlySpan <char> head, out SVersionLock l) => TryMatch(ref head, out l);
 /// <summary>
 /// Intersects this lock with another one: the strongest wins, merging <see cref="SVersionLock.LockMinor"/> with <see cref="SVersionLock.Lock"/>
 /// results in <see cref="SVersionLock.Lock"/>.
 /// </summary>
 /// <param name="this">This lock.</param>
 /// <param name="other">The other lock.</param>
 /// <returns>The strongest of the two.</returns>
 public static SVersionLock Intersect(this SVersionLock @this, SVersionLock other)
 {
     return(@this > other ? @this : other);
 }
 /// <summary>
 /// Merges this lock with another one: the weakest wins, merging <see cref="SVersionLock.LockMinor"/> with <see cref="SVersionLock.Lock"/>
 /// results in <see cref="SVersionLock.LockMinor"/>.
 /// </summary>
 /// <param name="this">This lock.</param>
 /// <param name="other">The other lock.</param>
 /// <returns>The weakest of the two.</returns>
 public static SVersionLock Union(this SVersionLock @this, SVersionLock other)
 {
     return(@this < other ? @this : other);
 }
        static (ParseResult Result, bool IsFloatingMinor) TryMatchRangeAlone(ref ReadOnlySpan <char> s, SVersionLock defaultBound, bool includePrerelease)
        {
            var r = TryMatchFloatingVersion(ref s);

            if (r.Error != null)
            {
                return(new ParseResult(r.Error), false);
            }

            PackageQuality quality = includePrerelease ? PackageQuality.None : PackageQuality.Stable;

            if (r.Version != null)
            {
                // As soon as a prerelease appears, this can only be an approximation (with one exception - see below) since for npm:
                //
                // "For example, the range >1.2.3-alpha.3 would be allowed to match the version 1.2.3-alpha.7, but it
                //  would not be satisfied by 3.4.5-alpha.9, even though 3.4.5-alpha.9 is technically "greater than"
                //  1.2.3-alpha.3 according to the SemVer sort rules. The version range only accepts prerelease tags
                //  on the 1.2.3 version. The version 3.4.5 would satisfy the range, because it does not have a prerelease
                //  flag, and 3.4.5 is greater than 1.2.3-alpha.7."
                //
                // We also set the MinQuality to CI (otherwise alpha.7 will not be authorized for alpha.3) regardless of the includePrerelease.
                // Moreover, if we are coming from the ~ (tilde range), the lock is on the patch, not on the minor, and this exactly matches the
                // npm behavior.
                //
                bool isApproximated = !includePrerelease;
                if (r.Version.IsPrerelease)
                {
                    quality = PackageQuality.None;
                    if (defaultBound == SVersionLock.LockMinor)
                    {
                        defaultBound   = SVersionLock.LockPatch;
                        isApproximated = false;
                    }
                }
                return(new ParseResult(new SVersionBound(r.Version, defaultBound, quality), isApproximated, r.FourtPartLost), false);
            }
            if (r.FMajor < 0)
            {
                return(new ParseResult(new SVersionBound(_000Version, SVersionLock.None, quality), isApproximated: !includePrerelease, false), false);
            }
            if (r.FMinor < 0)
            {
                return(new ParseResult(new SVersionBound(SVersion.Create(r.FMajor, 0, 0), SVersionLock.LockMajor, quality), isApproximated: !includePrerelease, false), true);
            }
            return(new ParseResult(new SVersionBound(SVersion.Create(r.FMajor, r.FMinor, 0), SVersionLock.LockMinor, quality), isApproximated: !includePrerelease, false), false);
        }
Example #10
0
 /// <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);
Example #11
0
 public void parse_SVersionBound_with_defaults(string p, string expected, SVersionLock defL, PackageQuality defQ)
 {
     SVersionBound.TryParse(p, out var b, defL, defQ).Should().BeTrue();
     b.ToString().Should().Be(expected);
 }