Migration
0.5 → 0.6
Regular expressions
regex-lite is no longer part of the public API: string().regex(Regex) / regex_msg and the vld::regex_lite re-export are gone. Use pattern (same regex feature, same issue code invalid_string / Regex, same JSON Schema pattern):
// before
vld::string().regex(vld::regex_lite::Regex::new(r"^[A-Z]{3}$").unwrap())
// after
vld::string().pattern(r"^[A-Z]{3}$")
// an invalid pattern you want to handle instead of panicking
vld::string().pattern(vld::rules::Pattern::new(src)?)
For checks that are easier in Rust than in a regex, see custom rules.
optional, nullable, nullish
nullable()fields are now required: an absent key fails withmissing_field(a presentnullis stillNone). Usenullish()where the key may be left out.optional()no longer addsnullto its JSON Schema (TypeScriptx?: T); parsing still acceptsnull.nullable()is inrequiredin JSON Schema (TypeScriptx: T | null).
See the table.
Unions serialize as plain values
Either / Either3 are serde untagged now: {"Left": "x"} becomes "x". Code that stored or sent the tagged form must use the bare value.
Removed
| Removed | Use |
|---|---|
features serialize, deserialize | nothing (serde support is always on): drop them from features |
ZFloat::finite() | nothing: JSON has no NaN / infinity |
ZObject::field_schema() | field() |
ZObject::deep_partial() | partial() on each nested object |
vld-actix::VldJsonError | VldActixError |
vld-diesel::validate_update, vld-sqlx::validate_update | validate_insert |
vld-surrealdb::validate_record | validate_content |
vld-http-common::ValidationIssueWithCode / format_issues_with_code | ValidationIssue / format_issues |
vld-http-common::format_json_parse_error / format_utf8_error | VldHttpError::parse (the adapters already answer with it) |
vld-tower::try_validated, vld-rama::try_validated | validated() |
vld-tauri::validate_event / validate_state / validate_plugin_config / validate_channel_message | validate |
impl_to_lapin!, impl_to_redis!, impl_to_redis_async! | LapinChannel::new(ch), RedisConn::new(conn), AsyncRedisConn::new(conn) |
vld-utoipa::impl_into_params!, impl_to_schema_query!, impl_to_schema_path! | #[into_params(parameter_in = ...)] + impl_to_schema!(T), or impl_to_schema!(T, query) |
Custom DynSchema impls
DynSchema has a new required method dyn_parse_missing (implemented for every VldSchema automatically). Only a hand-written DynSchema impl needs it: return what an absent object key should give.
0.4 → 0.5
vld 0.5 makes features additive, gives every built-in check a typed issue code and aligns all web adapters on one set of status codes. Most applications only need the first three steps. The full list of changes is in the changelog.
1. Cargo features
The std feature is gone (everything it gated is always on). serialize and deserialize are deprecated no-ops and will be removed in 0.6. diff now enables openapi.
# before
vld = { version = "0.4", features = ["std", "serialize", "derive"] }
# after
vld = { version = "0.5", features = ["derive"] }
2. Date backends
chrono, jiff and time are additive; each has its own module. Before, enabling several picked one silently (chrono > jiff > time).
- With one backend nothing changes:
vld::date(),vld::datetime()and the preludeZDate/ZDateTimestill work. - With several, call the module:
// before (0.4): whichever backend won
let d = vld::date();
// after (0.5)
let d = vld::chrono::date();
let t = vld::jiff::datetime();
let tod = vld::time::time(); // new: time of day
let ty: vld::chrono::ZDate = vld::chrono::date();
Libraries should always use the module form, because an application can enable a second backend through feature unification.
3. Issue codes
Every built-in check has a typed IssueCode variant; Custom is only for your own codes. TooSmall / TooBig carry origin: SizeOrigin and a JSON bound. IssueCode, StringValidation and DateKind are #[non_exhaustive].
// before
match &issue.code {
IssueCode::TooSmall { minimum, .. } => println!("min {minimum}"), // f64
IssueCode::Custom { code } if code == "invalid_length" => {}
_ => {}
}
// after
match &issue.code {
IssueCode::TooSmall { origin: SizeOrigin::String, minimum, .. } => {
println!("min {}", minimum.as_f64().unwrap_or_default()); // serde_json::Value
}
IssueCode::InvalidLength { expected, .. } => println!("exactly {expected}"),
_ => {} // required: the enum is non-exhaustive
}
// simplest and stable:
if issue.code.key() == "too_small" { /* … */ }
New typed variants include InvalidLength, InvalidFormat, InvalidDate, NotMultipleOf, InvalidEnumValue, InvalidUnion, NotUnique, MissingField, FileTypeNotAllowed, SerializeError. See Issue codes.
4. Messages
- Default messages come from one English template table.
- An absent required key reports
missing_field("Required field is missing"); a presentnullis stillinvalid_type. Displayno longer appends ", received X" toinvalid_type(the message already says it).
Tests that compare full English messages may need updating. Custom translation tables can add the new keys (missing_field, type.<name>, too_small.<origin>, …); the specific key is tried before the generic one. See Translations.
5. JSON Schema
Optional, nullable and nullish fields and unions emit anyOf instead of oneOf, and nullable() fields are no longer required. Update consumers or snapshots that expect oneOf.
// before
{"oneOf": [{"type": "string"}, {"type": "null"}]}
// after
{"anyOf": [{"type": "string"}, {"type": "null"}]}
The inherent to_json_schema() methods on individual schema types are gone; use the JsonSchema trait (json_schema()). ZObject::to_json_schema() stays.
6. Module paths
The core was split into schema/, error/, object/, macros/ and openapi/. The old paths vld::json_schema, vld::diff, vld::format, vld::i18n and vld::input remain as aliases, so imports keep compiling.
7. Schema caching
schema!, impl_validate_fields!, impl_rules! and #[derive(Validate)] build field schemas once per process. Field schemas must be Send + Sync + 'static, and values captured from runtime configuration are frozen at first use. See Performance.
VldInput::to_json_value now returns Cow<Value> (only relevant if you implemented VldInput).
8. derive
validate_fields() / parse_lenient() are generated only with #[vld(lenient)]. #[serde(flatten)] and #[serde(alias)] are compile errors instead of being ignored.
// after
#[derive(Debug, Default, serde::Serialize, vld::Validate)]
#[vld(lenient)]
struct Config { /* … */ }
9. Web adapters
VldJsonanswers 415 unless the content type isapplication/jsonorapplication/*+json. Clients must sendContent-Type: application/json.- Malformed JSON is 422 with the validation body (was 400).
- Each issue in the 422 body has a
codefield. - Query, path, form, header and cookie values fall back to their original text when the schema wants a string (
?q=123intovld::string()is"123").
See the web overview.
10. Crate-specific changes
| Crate | Change | What to do |
|---|---|---|
vld-tower, vld-rama | validated() returns Option<T>; try_validated is deprecated; the tower inner service receives Request<ValidatedBody<B>> and non-JSON bodies are no longer buffered | handle None |
vld-warp | handle_rejection maps rejections to their real status (unknown → 500, was 404); bodies are streamed with a limit (413) | check status-code tests |
vld-ntex | ntex 4 is the default; crate MSRV 1.97 | ntex 3: vld-ntex = { version = "0.5", default-features = false, features = ["ntex-3"] } |
vld-ts | with_default emits x.nullish().transform((v) => v ?? d) (Zod) / v.nullish(x, d) (Valibot) | regenerate client schemas; defaults accept null |
vld-clap | validate_or_exit requires clap::CommandFactory and reports clap's argument names; clap 4 | derive clap::Parser, or call validate_cli |
vld-tauri, vld-leptos, vld-dioxus, vld-surrealdb | issue paths have no leading dot (user.email, items[0]) | update path comparisons |
// vld-tower / vld-rama, before: returned T and panicked when missing
let body: CreateUser = vld_tower::validated(&req);
// after: None when the layer did not run or the request was not JSON
let Some(body) = vld_tower::validated::<CreateUser>(&req) else {
return bad_request();
};
11. MSRV
The core and most crates need Rust 1.85. Some integration features need more; see MSRV.