///|
/// Combinator and conditional keywords:
/// allOf, anyOf, oneOf, not, if/then/else.
fn kw_logic(
ctx : Ctx,
obj : Map[String, Json],
inst : Json,
ip : Path,
sp : Path,
) -> Unit {
match obj.get("allOf") {
Some(Array(schemas)) =>
for i, sub in schemas {
// each branch runs in its own annotation scope so that cousin
// branches cannot see each other's evaluations; a branch's
// annotations merge into the parent once it has run
ctx.push_scope()
validate_node(ctx, sub, inst, ip, sp.key("allOf").idx(i))
ctx.merge_scope()
}
_ => ()
}
match obj.get("anyOf") {
Some(Array(schemas)) => {
let mut matched = false
for i, sub in schemas {
// evaluate every branch: annotations from all successful
// branches must be visible to unevaluated* keywords
if ctx.probe(sub, inst, ip, sp.key("anyOf").idx(i)) {
matched = true
}
}
if !matched {
ctx.add_error(
ip,
sp.key("anyOf"),
"anyOf",
"value does not match any schema in anyOf",
)
}
}
_ => ()
}
match obj.get("oneOf") {
Some(Array(schemas)) => {
let mut matched = -1
for i, sub in schemas {
if ctx.probe(sub, inst, ip, sp.key("oneOf").idx(i)) {
if matched >= 0 {
matched = -2 // more than one
break
}
matched = i
}
}
if matched == -1 {
ctx.add_error(
ip,
sp.key("oneOf"),
"oneOf",
"value does not match exactly one schema in oneOf (matched none)",
)
} else if matched == -2 {
ctx.add_error(
ip,
sp.key("oneOf"),
"oneOf",
"value matches more than one schema in oneOf",
)
}
}
_ => ()
}
match obj.get("not") {
Some(sub) =>
if ctx.probe(sub, inst, ip, sp.key("not")) {
ctx.add_error(
ip,
sp.key("not"),
"not",
"value matches the schema in `not` but must not",
)
}
None => ()
}
match obj.get("if") {
Some(if_schema) => {
let ok = ctx.probe(if_schema, inst, ip, sp.key("if"))
if ok {
match obj.get("then") {
Some(then_schema) =>
validate_node(ctx, then_schema, inst, ip, sp.key("then"))
None => ()
}
} else {
match obj.get("else") {
Some(else_schema) =>
validate_node(ctx, else_schema, inst, ip, sp.key("else"))
None => ()
}
}
}
None => ()
}
}