static (SVersion?Version, int FMajor, int FMinor, string?Error, bool FourtPartLost) TryMatchFloatingVersion(ref ReadOnlySpan <char> s)
        {
            // Handling the marvelous "" (empty string), that is like '*'.
            if (s.Length == 0)
            {
                return(null, -1, 0, null, false);
            }
            var version = SVersion.TryParse(ref s);

            if (version.IsValid)
            {
                // If only the 3 first parts have been read: launch SkipExtraPartsAndPrereleaseIfAny on the head to skip
                // potential extra parts and prerelease.
                return(version, 0, 0, null, SkipExtraPartsAndPrereleaseIfAny(ref s));
            }
            int major, minor = -1;

            if (TryMatchXStarInt(ref s, out major))
            {
                if (major >= 0)
                {
                    if (s.Length > 0 && TryMatch(ref s, '.'))
                    {
                        if (s.Length == 0 || !TryMatchXStarInt(ref s, out minor))
                        {
                            return(null, 0, 0, "Expecting minor number or *.", false);
                        }
                        if (minor >= 0)
                        {
                            // If a fourth part caused the version parse to fail, handle it here.
                            if (s.Length > 0 && TryMatch(ref s, '.') && s.Length > 0 && TryMatchNonNegativeInt(ref s, out int patch))
                            {
                                return(SVersion.Create(major, minor, patch), 0, 0, null, SkipExtraPartsAndPrereleaseIfAny(ref s));
                            }
                        }
                    }
                }
                else
                {
                    minor = 0;
                }
                // Forgetting any trailing "X.Y.*" since it is like "X.Y".
                // Even if the npm grammar allows "3.*-alpha" or "3.1.*+meta", we ignores this: https://semver.npmjs.com/ selects nothing.
                // We consider this stupid trail as being FourthPartLost.
                return(null, major, minor, null, SkipExtraPartsAndPrereleaseIfAny(ref s));
            }
            return(null, 0, 0, version.ErrorMessage, false);
        }
        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);
        }
Exemple #3
0
        static SVersion TryParseVersion(ref ReadOnlySpan <char> s, out bool fourthPartLost)
        {
            Debug.Assert(s.Length > 0);
            fourthPartLost = false;
            var v = SVersion.TryParse(ref s);

            if (v.IsValid)
            {
                // If only the 3 first parts have been read...
                fourthPartLost = SkipExtraPartsAndPrereleaseIfAny(ref s);
            }
            else
            {
                if (TryMatchNonNegativeInt(ref s, out int major))
                {
                    if (s.Length == 0 || !TryMatch(ref s, '.'))
                    {
                        return(SVersion.Create(major, 0, 0));
                    }
                    if (!TryMatchNonNegativeInt(ref s, out int minor))
                    {
                        return(new SVersion("Expected Nuget minor part.", null));
                    }
                    // Try to save the fourth part: in such case the patch is read.
                    int patch = 0;
                    if (s.Length > 0 && TryMatch(ref s, '.') &&
                        s.Length > 0 && TryMatchNonNegativeInt(ref s, out patch) &&
                        s.Length > 0 && TryMatch(ref s, '.') &&
                        s.Length > 0 && TryMatchNonNegativeInt(ref s, out int _))
                    {
                        fourthPartLost = true;
                    }
                    return(SVersion.Create(major, minor, patch));
                }
            }
            return(v);
        }