///|
priv enum ValueType {
NullType
BooleanType
NumberType
IntegerType
StringType
ArrayType
ObjectType
}
///|
priv enum Plan {
Accept
Reject(String)
Rules(Array[Rule])
}
///|
priv enum Rule {
Types(Array[ValueType], String)
Constant(Json, String)
Enumeration(Array[Json], String)
Lower(ExactNumber, Bool, String)
Upper(ExactNumber, Bool, String)
Multiple(ExactNumber, String)
StringLength(Int?, Int?, String)
Pattern(PatternSupport, String)
Objects(ObjectPlan)
Arrays(ArrayPlan)
All(Array[Plan], String)
Any(Array[Plan], String)
One(Array[Plan], String)
Negate(Plan, String)
Conditional(Plan, Plan?, Plan?)
Ref(Int)
}
///|
priv struct ObjectPlan {
path : String
properties : Map[String, Plan]
patterns : Array[(PatternSupport, Plan)]
additional : Plan?
required : Array[String]
names : Plan?
minimum : Int?
maximum : Int?
dependent_required : Array[(String, Array[String])]
dependent_schemas : Array[(String, Plan)]
}
///|
priv struct ArrayPlan {
path : String
prefix : Array[Plan]
items : Plan?
contains : Plan?
min_contains : Int
max_contains : Int?
minimum : Int?
maximum : Int?
unique : Bool
}
///|
/// An owned indexed node graph: `$ref` rules jump to arena slots instead of
/// inlining targets, so recursive schemas compile once and validation never
/// builds cyclic heap references or expands plans exponentially.
priv struct Program {
nodes : Array[Plan]
}
///|
/// An owned, precompiled 2020-12 validation plan. Compilation does not mutate
/// the source document; constants and enumerations are copied into the plan.
pub struct Schema {
priv plan : Program
}
///|
fn pointer_step(path : String, key : String) -> String {
path + "/" + key.replace_all(old="~", new="~0").replace_all(old="/", new="~1")
}
///|
fn owned_json(value : Json) -> Json {
match value {
Object(members) => {
let copy : Map[String, Json] = Map([])
for key, child in members {
copy.set(key, owned_json(child))
}
Json::object(copy)
}
Array(values) => Json::array(values.map(owned_json))
_ => value
}
}