Error model
Every failed parse returns a VldError. It is a list of issues; parsing continues after the first problem, so you get all of them at once.
pub struct VldError {
pub issues: Vec<ValidationIssue>,
}
pub struct ValidationIssue {
pub code: IssueCode, // what went wrong, with parameters
pub message: String, // human-readable text
pub path: Vec<PathSegment>, // where: [Field("items"), Index(0), Field("sku")]
pub received: Option<serde_json::Value>, // the offending value, when known
}
pub enum PathSegment {
Field(String), // displayed as ".name"
Index(usize), // displayed as "[0]"
}
All of them implement Serialize and Deserialize, so you can return them from an API or store them. VldError implements Display and std::error::Error.
Reading issues
use vld::prelude::*;
let schema = vld::array(vld::object().field("sku", vld::string().len(8))).min_len(1);
let err = schema.parse(r#"[{"sku": "short"}]"#).unwrap_err();
let issue = &err.issues[0];
assert_eq!(issue.code.key(), "invalid_length");
assert_eq!(issue.path_string(), "[0].sku"); // `issue.path` holds the segments
match &issue.code {
IssueCode::InvalidLength { expected, .. } => assert_eq!(*expected, 8),
_ => unreachable!(),
}
issue.path_string()joins the path (.user.tags[0], empty for the root).issue.code.key()is a stable snake_case string (too_small,invalid_string, …). Prefer it over matching messages.IssueCodeis#[non_exhaustive]: add a_ =>arm when you match on it.issue.code.params()returns the template parameters (minimum,expected, …) as strings.
The full list of codes is on Issue codes.
Display
Display prints one line per issue: path: message, plus , received <value> when the value is known (except for invalid_type, whose message already names the received type).
.name: String must be at least 2 characters, received "A"
.email: Invalid email address, received "nope"
.address.zip: Required field is missing
For other layouts see Formatting.
Missing vs null
An absent required key reports missing_field ("Required field is missing"). A key that is present with null reports invalid_type (expected string, received null). Optional, nullable and defaulted fields accept both.
Building errors yourself
Use VldError in custom checks and when combining validations:
let mut errors = VldError::new();
errors
.issue(IssueCode::Custom { code: "passwords_differ".into() })
.message("Passwords do not match")
.path_field("confirm")
.finish();
errors.push(IssueCode::Custom { code: "weak".into() }, "Password is too weak");
assert_eq!(errors.issues.len(), 2);
assert_eq!(errors.issues[0].code.key(), "passwords_differ");
| Method | Purpose |
|---|---|
VldError::new() / single(code, msg) / from_code(code) | create |
push(code, msg) / push_code(code) | add an issue (push_code uses the default message) |
issue(code)…finish() | builder with message, path_field, path_index, received |
merge(other) | append another error's issues |
with_prefix(segment) | prepend a path segment to every issue |
is_empty() | no issues |
IssueCode::Custom { code } is reserved for your own codes. Built-in checks always use the typed variants.