Пример #1
0
        static void InlineAssertInvariants(CSVersion v)
        {
#if DEBUG
            if (!_alreadyInCheck && v.IsValid)
            {
                _alreadyInCheck = true;
                try
                {
                    if (v.IsLongForm)
                    {
                        Debug.Assert(v.NormalizedText == ComputeLongFormVersion(v.Major, v.Minor, v.Patch, v.PrereleaseNameIdx, v.PrereleaseNumber, v.PrereleasePatch, v.BuildMetaData));
                    }
                    else
                    {
                        Debug.Assert(v.NormalizedText == ComputeShortFormVersion(v.Major, v.Minor, v.Patch, v.PrereleaseNameIdx, v.PrereleaseNumber, v.PrereleasePatch, v.BuildMetaData));
                    }
                    //// Systematically checks that a valid CSVersion can be parsed back in Long or Short form.
                    Debug.Assert(SVersion.TryParse(v.ToString(CSVersionFormat.Normalized)).Equals(v.ToNormalizedForm()));
                    Debug.Assert(SVersion.TryParse(v.ToString(CSVersionFormat.LongForm)).Equals(v.ToLongForm()));
                }
                finally
                {
                    _alreadyInCheck = false;
                }
            }
#endif
        }
Пример #2
0
        /// <summary>
        /// Parses the specified string to a constrained semantic version and throws an <see cref="ArgumentException"/>
        /// it the resulting <see cref="SVersion"/> is not a <see cref="CSVersion"/> or <see cref="SVersion.IsValid"/> is false.
        /// </summary>
        /// <param name="s">The string to parse.</param>
        /// <param name="checkBuildMetaDataSyntax">False to opt-out of strict <see cref="SVersion.BuildMetaData"/> compliance.</param>
        /// <returns>The CSVersion object.</returns>
        public static CSVersion Parse(string s, bool checkBuildMetaDataSyntax = true)
        {
            SVersion sv = SVersion.TryParse(s, true, checkBuildMetaDataSyntax);

            if (!sv.IsValid)
            {
                throw new ArgumentException(sv.ErrorMessage, nameof(s));
            }
            return(sv as CSVersion ?? throw new ArgumentException("Not a CSVersion.", nameof(s)));
        }
Пример #3
0
        /// <summary>
        /// Initializes a new <see cref="InformationalVersion"/> by parsing a string.
        /// This never throws: <see cref="IsValidSyntax"/> may be false and <see cref="ParseErrorMessage"/> exposes
        /// the error message.
        /// </summary>
        /// <param name="informationalVersion">Informational version. Can be null.</param>
        public InformationalVersion(string?informationalVersion)
        {
            if ((OriginalInformationalVersion = informationalVersion) != null)
            {
                Match m = _rV7.Match(informationalVersion);
                if (!m.Success)
                {
                    m = _rV6.Match(informationalVersion);
                }
                if (!m.Success)
                {
                    m = _rOld.Match(informationalVersion);
                }
                if (m.Success)
                {
                    RawVersion = m.Groups[2].Value;
                    CommitSha  = m.Groups[3].Value;
                    var v = SVersion.TryParse(RawVersion);
                    Version = v.AsCSVersion != null?v.AsCSVersion.ToNormalizedForm() : v;

                    if (DateTime.TryParseExact(m.Groups[4].Value, "u", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out DateTime t))
                    {
                        CommitDate = t;
                        if (t.Kind != DateTimeKind.Utc)
                        {
                            ParseErrorMessage = $"The CommitDate must be Utc: {m.Groups[4].Value} must be {DateTime.SpecifyKind( t, DateTimeKind.Utc ):u}.";
                        }
                        else if (!Version.IsValid)
                        {
                            ParseErrorMessage = "The SemVersion is invalid: " + Version.ErrorMessage;
                        }
                        else if (CommitSha.Length != 40 || !CommitSha.All(IsHexDigit))
                        {
                            ParseErrorMessage = "The CommitSha is invalid (must be 40 hex digit).";
                        }
                        else
                        {
                            IsValidSyntax = true;
                        }
                    }
                    else
                    {
                        ParseErrorMessage = "The CommitDate is invalid.It must be a UTC DateTime in \"u\" format.";
                    }
                }
                else
                {
                    ParseErrorMessage = "The String to parse does not match the standard CSemVer informational version pattern.";
                }
            }
            else
            {
                ParseErrorMessage = "String to parse is null.";
            }
        }
Пример #4
0
        /// <summary>
        /// Parses the specified string to a constrained semantic version and returns a <see cref="CSVersion"/> that
        /// may not be <see cref="SVersion.IsValid"/>.
        /// </summary>
        /// <param name="s">The string to parse.</param>
        /// <param name="checkBuildMetaDataSyntax">False to opt-out of strict <see cref="SVersion.BuildMetaData"/> compliance.</param>
        /// <returns>The CSVersion object that may not be <see cref="SVersion.IsValid"/>.</returns>
        public static CSVersion TryParse(string s, bool checkBuildMetaDataSyntax = true)
        {
            SVersion sv = SVersion.TryParse(s, true, checkBuildMetaDataSyntax);

            if (sv is CSVersion v)
            {
                return(v);
            }
            Debug.Assert(sv.IsValid == (sv.ErrorMessage == null));
            return(new CSVersion(sv.ErrorMessage ?? "Not a CSVersion.", s));
        }
        /// <summary>
        /// Standard TryParse pattern that returns a boolean rather than the resulting <see cref="CSVersion"/>.
        /// See <see cref="TryParse(string,bool)"/>.
        /// </summary>
        /// <param name="s">String to parse.</param>
        /// <param name="v">Resulting version.</param>
        /// <param name="checkBuildMetaDataSyntax">False to opt-out of strict <see cref="SVersion.BuildMetaData"/> compliance.</param>
        /// <returns>True on success, false otherwise.</returns>
        public static bool TryParse(string s, out CSVersion v, bool checkBuildMetaDataSyntax = true)
        {
            v = null;
            SVersion sv = SVersion.TryParse(s, true, checkBuildMetaDataSyntax);

            if (!sv.IsValid)
            {
                return(false);
            }
            v = sv as CSVersion;
            return(v != null);
        }
        /// <summary>
        /// Parses the specified string to a constrained semantic version and returns a <see cref="CSVersion"/> that
        /// may not be <see cref="SVersion.IsValid"/>.
        /// </summary>
        /// <param name="s">The string to parse.</param>
        /// <param name="checkBuildMetaDataSyntax">False to opt-out of strict <see cref="SVersion.BuildMetaData"/> compliance.</param>
        /// <returns>The CSVersion object that may not be <see cref="SVersion.IsValid"/>.</returns>
        public static CSVersion TryParse(string s, bool checkBuildMetaDataSyntax = true)
        {
            SVersion sv = SVersion.TryParse(s, true, checkBuildMetaDataSyntax);

            if (sv is CSVersion v)
            {
                return(v);
            }
            if (!sv.IsValid)
            {
                new CSVersion(sv.ErrorMessage, s);
            }
            return(new CSVersion("Not a CSVersion.", s));
        }
        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);
        }
Пример #8
0
        static void InlineAssertInvariants(CSVersion v)
        {
#if DEBUG
            if (!_alreadyInCheck && v.IsValid)
            {
                _alreadyInCheck = true;
                try
                {
                    //// Systematically checks that a valid CSVersion can be parsed back in Long or Short form.
                    Debug.Assert(SVersion.TryParse(v.ToString(CSVersionFormat.Normalized)).Equals(v));
                    Debug.Assert(SVersion.TryParse(v.ToString(CSVersionFormat.ShortForm)).Equals(v));
                }
                finally
                {
                    _alreadyInCheck = false;
                }
            }
#endif
        }
Пример #9
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);
        }
Пример #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"/>]".
        /// 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);
        }