static JsonTransformRules()
        {
            Empty = (token, recurse) => Option <JToken> .None;

            Identity = (token, recurse) =>
            {
                if (token is JArray jArray)
                {
                    return(new JArray(jArray
                                      .SelectMany(item => recurse(item)
                                                  .AsEnumerable())));
                }

                if (token is JObject jObject)
                {
                    return(new JObject(jObject
                                       .Properties()
                                       .SelectMany(prop => recurse(prop)
                                                   .AsEnumerable())));
                }

                if (token is JProperty property)
                {
                    return(recurse(property.Value)
                           .Map(transformedPropertyValue => (JToken) new JProperty(property.Name, transformedPropertyValue)));
                }

                return(token);
            };
        }
 public static JsonTransformRule Eager(this JsonTransformRule firstRule, JsonTransformRule secondRule) => (token, recurse) =>
 {
     return(firstRule(token, recurse).Bind(first => secondRule(first, recurse)));
 };
 public static JsonTransformRule Lazy(this JsonTransformRule firstRule, JsonTransformRule secondRule) => (token, recurse) =>
 {
     return(firstRule(token, recurse).IfNone(() => secondRule(token, recurse)));
 };
        public static Option <JToken> Transform(this JToken token, JsonTransformRule rule)
        {
            Option <JToken> Recurse(JToken ast) => rule(ast, Recurse);

            return(Recurse(token));
        }
            public JsonSupportTypedGremlinQueryProvider(IModelGremlinQueryProvider <JToken> baseProvider)
            {
                this._baseProvider = baseProvider;

                this._transformRule = JsonTransformRules
                                      .Empty
                                      .Lazy((token, recurse) => token is JObject jObject && (jObject["@type"]?.ToString().Equals("g:Property", StringComparison.OrdinalIgnoreCase)).GetValueOrDefault()
                        ? jObject.TryGetValue("@value")
                                            .Map(value => value as JObject)
                                            .Bind(valueObject => valueObject.TryGetValue("value"))
                        : Option <JToken> .None)
                                      .Lazy((token, recurse) =>
                {
                    if (token is JObject jObject && (jObject["@type"]?.ToString().Equals("g:Map", StringComparison.OrdinalIgnoreCase)).GetValueOrDefault())
                    {
                        return(jObject.TryGetValue("@value")
                               .Map(value => value as JArray)
                               .Bind(array =>
                        {
                            var mapObject = new JObject();

                            for (var i = 0; i < array.Count - 1; i += 2)
                            {
                                if (array[i] is JValue value && value.Value is string key)
                                {
                                    mapObject[key] = array[i + 1];
                                }
                            }

                            return recurse(mapObject);
                        }));
                    }

                    return(Option <JToken> .None);
                })
                                      .Lazy((token, recurse) => token is JObject jObject && jObject.ContainsKey("@type") && jObject["@value"] is JToken valueToken
                        ? recurse(valueToken)
                        : Option <JToken> .None)
                                      .Lazy((token, recurse) =>
                {
                    if (token is JObject jObject && (jObject.Parent?.Parent is JObject typedVertexProperty && typedVertexProperty["@type"]?.ToString() == "g:VertexProperty" || jObject.Parent?.Parent?.Parent?.Parent is JProperty parentProperty && parentProperty.Name.Equals("properties", StringComparison.OrdinalIgnoreCase)))
                    {
                        return(jObject
                               .TryGetValue("value")
                               .Bind(recurse));
                    }

                    return(Option <JToken> .None);
                })
                                      .Lazy((token, recurse) =>
                {
                    if (token is JObject jObject && !jObject.ContainsKey("$type"))
                    {
                        return(jObject
                               .TryGetValue("label")
                               .Bind(labelToken => baseProvider.Model
                                     .TryGetElementTypeOfLabel(labelToken.ToString()))
                               .Bind(type =>
                        {
                            jObject.AddFirst(new JProperty("$type", type.AssemblyQualifiedName));

                            return recurse(jObject);
                        }));
                    }

                    return(Option <JToken> .None);
                })
                                      .Lazy((token, recurse) =>
                {
                    if (token is JObject jObject && jObject["properties"] is JObject propertiesObject)
                    {
                        foreach (var item in propertiesObject)
                        {
                            jObject[item.Key] = recurse(item.Value).IfNone(jObject[item.Key]);
                        }

                        propertiesObject.Parent.Remove();

                        return(recurse(jObject));
                    }

                    return(Option <JToken> .None);
                })
                                      .Lazy(JsonTransformRules.Identity);
            }