public static Validator CreateSchemaValidator(JsonValue schema, CustomTypeDefinitionProvider customTypesProvider, Mode mode) { IReadOnlyDictionary <string, PropertyValidator> propValidators = schema.Properties() .Map(x => (name: x.Item1, def: x.Item2)) .ToDictionary(prop => prop.name, prop => prop.def.GetPropertyOption("type").Match( type => CreateSinglePropertyValidator(type, customTypesProvider), () => InvalidPropValidator(prop.name))); return(new Validator((string propName, JsonValue propValue) => propValidators.TryGetValue(propName) .Match(validator => validator(propValue).Match(x => x, e => Error($"error validating prop {propName}: {string.Join(",", e) }")), () => mode == Mode.AllowUndefinedProperties ? Valid : Error($"property \"{propName}\" not found")))); }
private static PropertyValidator CreateSinglePropertyValidator(JsonValue schema, CustomTypeDefinitionProvider provider) { switch (schema) { case JsonValue.String typeName: var type = typeName.Item; var validator = fun((Func <JsonValue, bool> fn) => new PropertyValidator(x => fn(x) ? Valid : Error($"value is not a {type}"))); switch (type) { case "number": return(validator(x => x.IsNumber)); case "date": return(validator(x => DateTime.TryParse(x.AsString(), out var _))); case "string": return(validator(x => x.IsString)); case "boolean": return(validator(x => x.IsBoolean)); case "array": return(validator(x => x.IsArray)); case "object": return(validator(x => x.IsRecord)); default: return(provider(type) .Map(customType => CreateCustomTypeValidator(customType)) .IfNone(() => (JsonValue value) => Error($"custom type \"{type}\" not exists"))); } case JsonValue.Record customTypeRaw: return(customTypeRaw.GetPropertyOption("name").Bind(x => customTypeRaw.GetPropertyOption("ofType")).Match(arrayType => { var arrayValidator = CreateSinglePropertyValidator(arrayType, provider); return (x => x.AsArray().Map(i => arrayValidator(i)).Reduce((a, b) => a | b)); }, () => CreateCustomTypeValidator(CustomTypeDefinition.FromJsonValue(customTypeRaw)))); default: return((JsonValue _) => Error("unknown type definition")); } }