deser-validate
Validation for deser. Values are validated while they are deserialized, so errors point at the value in the input (with line, column and path) like the errors of the format.
Validators are types. The Check adapter validates a field with them,
the field keeps its type:
use Deserialize;
use ;
// a validator from a condition and a message
validator!;
let json = r#"{"port": 0, "name": "web", "admins": []}"#;
let err = .unwrap_err;
assert_eq!;
Writing Validators
The validator! macro turns a condition or a function into a validator
type. There are three forms:
use ;
// a condition and the message if it's false
validator!;
// a function
validator!;
// a block
validator!;
assert!;
assert!;
assert_eq!;
Things to know about the validators the macro creates:
- A validator of a type validates everything that borrows as it: a
validator of
strvalidatesString,Box<str>andCow<str>, a validator of[T]validatesVec<T>. - Functions and blocks return a
Result<(), E>whereEis a message (&'static str,String) or aViolation, or abool. Messages become violations with the name of the validator in snake case as code (Slughas the codeslug,NonZerothe codenon_zero). Aboolfunction that returnsfalsefails with the messageis not valid. - Codes are what clients see, renaming a validator changes its code. A
code can be given as last argument instead:
validator!(Port(port: &u16) => *port != 0, "must not be zero", code = "port"). - The macro does not support types with generics or lifetimes. For those
implement
Validatoryourself, which is all the macro does too:
use ;
;
Violations have a code and parameters (for programs, for instance to
translate the messages or to point at a field in a user interface) and a
message for humans. Errors of invalid values have the Violation
attached.
Checks Across Fields
On a type Check wraps its derived implementation (written as _), the
validator sees the whole value. Plain functions work there as well:
use Deserialize;
use ;
validator!;
let json = r#"{"min": 90, "max": 80}"#;
let err = .unwrap_err;
assert_eq!;
This also works for values that are updated in place (layered configuration): the value is checked once the update is complete.
Naming Validators
Validators are named for the property that valid values have, as a noun or
an adjective: Email, Slug, NonEmpty, NonZero, MaxLen<64>. They
read as Check<NonZero> and do not need affixes like Valid, Is or
Rules. Negations start with Non (like std::num::NonZero). Validators
of a whole type name the rule they check (OrderedPorts, not
PortRangeRules), combinators name their structure (Each). The code of a
violation is the name of the validator in snake case (max_len,
non_zero), for the validators of this crate too.
Invalid Values
What happens with an invalid value depends on how the validator is used:
| invalid values | |
|---|---|
#[deser(as = Check<V>)] (fields) |
fail the deserialization, the field keeps its type |
#[deser(deserialize_as = Check<V, _>)] (types) |
fail the deserialization |
Validated<T, V> (the type of the field) |
are kept with all their errors, deserialization continues |
Types that are always valid are newtypes that check themselves. They are often named like the validator, the validator is named by its path then:
use Deserialize;
use Check;
] String);
Validated keeps all errors of its value, also errors like a string where
a number is expected and errors deep inside the value. This is what a form
that is shown again with its errors needs:
use Deserialize;
use ;
let json = r#"{"email": "jane@", "age": "old"}"#;
let signup: Signup = from_str.unwrap;
assert_eq!;
assert!;
assert!;
Reporting All Problems
A Validation reports all problems of an input at once: the errors that
Validated values keep and all errors that fail the deserialization, with
their paths. This is what an API that answers with a list of problems or a
configuration loader wants:
use Deserialize;
use ;
let validation = new;
let json = r#"{"email": "jane@", "age": 7}"#;
let rv = from_str
.;
let report = validation.finish.into_result.err.unwrap;
assert_eq!;
for issue in &report
Provided Validators
NonEmpty, Len (MinLen, MaxLen), Range (Min, Max), Email and
Each for the items of collections. Tuples of validators require all of
them: Check<(NonEmpty, MaxLen<64>)>.
This is built on the error handling of deser: containers can recover from
the errors of their items (Sink::recover) and collect them
(State::set_collect_errors), and errors can hold multiple errors.