/// <summary>
        /// Parses a JSON Pointer from a string.
        /// </summary>
        /// <param name="source">The source string.</param>
        /// <param name="pointerKind">(optional) Restricts the kind of pointer.  <see cref="JsonPointerKind.Unspecified"/> (default) allows both.</param>
        /// <returns>A JSON Pointer.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception>
        /// <exception cref="PointerParseException"><paramref name="source"/> does not contain a valid pointer or contains a pointer of the wrong kind.</exception>
        public static JsonPointer Parse(string source, JsonPointerKind pointerKind = JsonPointerKind.Unspecified)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            if (source == string.Empty)
            {
                return(Empty);
            }
            if (source == "#")
            {
                return(UrlEmpty);
            }

            bool isUriEncoded;
            var  parts = source.Split('/');
            var  i     = 0;

            if (parts[0] == "#" || parts[0] == string.Empty)
            {
                isUriEncoded = parts[0] == "#";
                i++;
            }
            else
            {
                throw new PointerParseException("Pointer must start with either `#` or `/` or be empty");
            }

            switch (pointerKind)
            {
            case JsonPointerKind.Unspecified:
                break;

            case JsonPointerKind.Plain:
                if (isUriEncoded)
                {
                    throw new PointerParseException("Pointer is URI-encoded, but plain was expected.");
                }
                break;

            case JsonPointerKind.UriEncoded:
                if (!isUriEncoded)
                {
                    throw new PointerParseException("Pointer is plain, but URI-encoded was expected.");
                }
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(pointerKind), pointerKind, null);
            }

            var segments = new PointerSegment[parts.Length - i];

            for (; i < parts.Length; i++)
            {
                segments[i - 1] = PointerSegment.Parse(parts[i], isUriEncoded);
            }

            return(new JsonPointer
            {
                _source = source,
                _segments = segments,
                Kind = isUriEncoded ? JsonPointerKind.UriEncoded : JsonPointerKind.Plain
            });
        }
        /// <summary>
        /// Parses a JSON Pointer from a string.
        /// </summary>
        /// <param name="source">The source string.</param>
        /// <param name="pointer">The resulting pointer.</param>
        /// <param name="pointerKind">(optional) Restricts the kind of pointer.  <see cref="JsonPointerKind.Unspecified"/> (default) allows both.</param>
        /// <returns><code>true</code> if the parse was successful; <code>false</code> otherwise.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception>
        public static bool TryParse(string source, out JsonPointer pointer, JsonPointerKind pointerKind = JsonPointerKind.Unspecified)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            if (source == string.Empty)
            {
                pointer = Empty;
                return(true);
            }
            if (source == "#")
            {
                pointer = UrlEmpty;
                return(true);
            }

            bool isUriEncoded;
            var  parts = source.Split('/');
            var  i     = 0;

            if (parts[0] == "#" || parts[0] == string.Empty)
            {
                isUriEncoded = parts[0] == "#";
                i++;
            }
            else
            {
                pointer = default;
                return(false);
            }

            switch (pointerKind)
            {
            case JsonPointerKind.Unspecified:
                break;

            case JsonPointerKind.Plain:
                if (isUriEncoded)
                {
                    pointer = default;
                    return(false);
                }
                break;

            case JsonPointerKind.UriEncoded:
                if (!isUriEncoded)
                {
                    pointer = default;
                    return(false);
                }
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(pointerKind), pointerKind, null);
            }

            var segments = new PointerSegment[parts.Length - i];

            for (; i < parts.Length; i++)
            {
                var part = parts[i];
                if (!PointerSegment.TryParse(part, isUriEncoded, out var segment))
                {
                    pointer = default;
                    return(false);
                }

                segments[i - 1] = segment;
            }

            pointer = new JsonPointer
            {
                _source   = source,
                _segments = segments,
                Kind      = isUriEncoded ? JsonPointerKind.UriEncoded : JsonPointerKind.Plain
            };
            return(true);
        }