17 · Schemas
Cross-field schema invariants
Attach where predicates after a schema declaration to validate the assembled value. Field conversion and defaults happen first. Invariants return bool and run in order; failure becomes a structured DecodeError at the object path. Field constraints keep their own field scope.
On this page
Attach where predicates after a schema declaration to validate the assembled value. Field conversion and defaults happen first. Invariants return bool and run in order; failure becomes a structured DecodeError at the object path. Field constraints keep their own field scope.
Complete example
Cross-field schema invariants.
06-cross-field-invariants.zolo
// Item 13: field checks -> defaults -> assembled-object invariants, once each.
var trace = ""
var cleaned = 0
fn last_default() -> int { trace += "D"; 10 }
fn nonnegative(value: int) -> bool { trace += "F"; value >= 0 }
schema Booking {
start: int where nonnegative,
end: int = last_default(),
}
where |booking| {
defer { cleaned += 1 }
trace += "I"
booking.start < booking.end
}
where |booking| { trace += "J"; booking.end <= 20 }
fn show(result: Result<Booking, DecodeError>) {
match result {
.Ok(booking) => print("ok:{booking.start}:{booking.end}"),
.Err(error) => {
print("{error.path}:{error.kind}:{error.expected}:{error.actual}:{error.message}")
print(error.causes.len())
},
}
}
fn main() {
let valid = Booking.parse(#{start: 2})
assert(match valid { .Ok(value) => value.start == 2 && value.end == 10, .Err(_) => false })
assert(trace == "FDIJ")
show(valid)
print(trace)
trace = ""
let invariant_failure = Booking.parse(#{start: 10, end: 1})
assert(match invariant_failure {
.Err(error) => error.path == "$" && error.expected == "Booking" && error.actual == "Booking"
&& error.message == "Booking: invariant 1 failed" && error.causes.len() == 0,
.Ok(_) => false,
})
assert(trace == "FI")
show(invariant_failure)
print(trace)
trace = ""
let field_failure = Booking.parse(#{start: -1})
assert(match field_failure {
.Err(error) => error.path == "$.start" && error.message == "start: constraint failed",
.Ok(_) => false,
})
assert(trace == "F")
show(field_failure)
print(trace)
trace = ""
let wrong_type = Booking.parse(#{start: 2, end: "bad"})
assert(match wrong_type {
.Err(error) => error.path == "$.end" && error.expected == "int" && error.actual == "string",
.Ok(_) => false,
})
assert(trace == "F")
show(wrong_type)
print(trace)
print(cleaned)
assert(cleaned == 2)
}
Rules and behavior
Validation, schemas and units · Documentation
From a checkout of the repository:
zolo examples/features/23-schemas/06-cross-field-invariants.zolo
See also