/// <summary>
        /// Creates an <see cref="OpenApiUrlTreeNode"/> from a collection of <see cref="OpenApiDocument"/>.
        /// </summary>
        /// <param name="sources">Dictionary of labels and their corresponding <see cref="OpenApiDocument"/> objects.</param>
        /// <returns>The created <see cref="OpenApiUrlTreeNode"/>.</returns>
        public static OpenApiUrlTreeNode CreateOpenApiUrlTreeNode(Dictionary <string, OpenApiDocument> sources)
        {
            var rootNode = OpenApiUrlTreeNode.Create();

            foreach (var source in sources)
            {
                rootNode.Attach(source.Value, source.Key);
            }
            return(rootNode);
        }
        /// <summary>
        /// Assembles the constituent properties of an <see cref="OpenApiUrlTreeNode"/> node.
        /// </summary>
        /// <param name="segments">IEnumerable subdirectories of a relative path.</param>
        /// <param name="pathItem">Path Item object that describes the operations available on an OpenAPI path.</param>
        /// <param name="label">A name tag for labelling the <see cref="OpenApiUrlTreeNode"/> node.</param>
        /// <param name="currentPath">The relative path of a node.</param>
        /// <returns>An <see cref="OpenApiUrlTreeNode"/> node with all constituent properties assembled.</returns>
        private OpenApiUrlTreeNode Attach(IEnumerable <string> segments,
                                          OpenApiPathItem pathItem,
                                          string label,
                                          string currentPath)
        {
            var segment = segments.FirstOrDefault();

            if (string.IsNullOrEmpty(segment))
            {
                if (PathItems.ContainsKey(label))
                {
                    throw new ArgumentException("A duplicate label already exists for this node.", nameof(label));
                }

                Path = currentPath;
                PathItems.Add(label, pathItem);
                return(this);
            }

            // If the child segment has already been defined, then insert into it
            if (Children.ContainsKey(segment))
            {
                var newPath = currentPath + PathSeparator + segment;

                return(Children[segment].Attach(segments: segments.Skip(1),
                                                pathItem: pathItem,
                                                label: label,
                                                currentPath: newPath));
            }
            else
            {
                var newPath = currentPath + PathSeparator + segment;

                var node = new OpenApiUrlTreeNode(segment)
                {
                    Path = newPath
                };

                Children[segment] = node;

                return(node.Attach(segments: segments.Skip(1),
                                   pathItem: pathItem,
                                   label: label,
                                   currentPath: newPath));
            }
        }
        private static IDictionary <OperationType, OpenApiOperation> GetOpenApiOperations(OpenApiUrlTreeNode rootNode, string relativeUrl, string label)
        {
            if (relativeUrl.Equals("/", StringComparison.Ordinal) && rootNode.HasOperations(label))
            {
                return(rootNode.PathItems[label].Operations);
            }

            var urlSegments = relativeUrl.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);

            IDictionary <OperationType, OpenApiOperation> operations = null;

            var targetChild = rootNode;

            /* This will help keep track of whether we've skipped a segment
             * in the target url due to a possible parameter naming mismatch
             * with the corresponding OpenApiUrlTreeNode target child segment.
             */
            var parameterNameOffset = 0;

            for (var i = 0; i < urlSegments?.Length; i++)
            {
                var tempTargetChild = targetChild?.Children?
                                      .FirstOrDefault(x => x.Key.Equals(urlSegments[i],
                                                                        StringComparison.OrdinalIgnoreCase)).Value;

                // Segment name mismatch
                if (tempTargetChild == null)
                {
                    if (i == 0)
                    {
                        /* If no match and we are at the 1st segment of the relative url,
                         * exit; no need to continue matching subsequent segments.
                         */
                        break;
                    }

                    /* Attempt to get the parameter segment from the children of the current node:
                     * We are assuming a failed match because of different parameter namings
                     * between the relative url segment and the corresponding OpenApiUrlTreeNode segment name
                     * ex.: matching '/users/12345/messages' with '/users/{user-id}/messages'
                     */
                    tempTargetChild = targetChild?.Children?
                                      .FirstOrDefault(x => x.Value.IsParameter).Value;

                    /* If no parameter segment exists in the children of the
                     * current node or we've already skipped a parameter
                     * segment in the relative url from the last pass,
                     * then exit; there's no match.
                     */
                    if (tempTargetChild == null || parameterNameOffset > 0)
                    {
                        break;
                    }

                    /* To help us know we've skipped a
                     * corresponding segment in the relative url.
                     */
                    parameterNameOffset++;
                }
                else
                {
                    parameterNameOffset = 0;
                }

                // Move to the next segment
                targetChild = tempTargetChild;

                // We want the operations of the last segment of the path.
                if (i == urlSegments.Length - 1 && targetChild.HasOperations(label))
                {
                    operations = targetChild.PathItems[label].Operations;
                }
            }

            return(operations);
        }