Expand description
Composable validation traits and error types for Rust structs and values.
reliakit-validate provides a small, focused toolkit for expressing
validation rules as types. The core pieces are:
Validate— a trait that types implement to describe their validity rules.Valid<T>— a zero-cost wrapper that carries proof of successful validation in the type system.ValidationError— an error type that collects one or moreViolations, useful for validating multiple fields at once and returning all failures together.
§Examples
§Single-field validation
use reliakit_validate::{Validate, Valid, ValidationError};
struct Username(String);
impl Validate for Username {
type Error = ValidationError;
fn validate(&self) -> Result<(), Self::Error> {
if self.0.is_empty() {
return Err(ValidationError::new("username must not be empty"));
}
if self.0.len() > 32 {
return Err(ValidationError::new("username must not exceed 32 characters"));
}
Ok(())
}
}
let user = Valid::new(Username("alice".into())).unwrap();
assert_eq!(user.0, "alice");§Multi-field struct validation
use reliakit_validate::{Validate, ValidationError, Violation};
struct CreateUser {
name: String,
age: u8,
}
impl Validate for CreateUser {
type Error = ValidationError;
fn validate(&self) -> Result<(), Self::Error> {
let mut errors = ValidationError::empty();
if self.name.is_empty() {
errors.push(Violation::with_field("name", "must not be empty"));
}
if self.age < 18 {
errors.push(Violation::with_field("age", "must be at least 18"));
}
if errors.is_empty() { Ok(()) } else { Err(errors) }
}
}
let result = CreateUser { name: String::new(), age: 15 }.validate();
assert!(result.is_err());
assert_eq!(result.unwrap_err().len(), 2);§One error list for an API response
Collecting every Violation lets a request handler report all field
problems at once (e.g. as an HTTP 422 body) instead of making the client fix
one error per round-trip:
use reliakit_validate::{Validate, ValidationError, Violation};
struct Signup {
email: String,
password: String,
}
impl Validate for Signup {
type Error = ValidationError;
fn validate(&self) -> Result<(), Self::Error> {
let mut errors = ValidationError::empty();
if !self.email.contains('@') {
errors.push(Violation::with_field("email", "must contain @"));
}
if self.password.len() < 8 {
errors.push(Violation::with_field("password", "must be at least 8 characters"));
}
if errors.is_empty() { Ok(()) } else { Err(errors) }
}
}
let bad = Signup { email: "nope".into(), password: "x".into() };
let errors = bad.validate().unwrap_err();
// Render to the (field, message) pairs a JSON error body would carry.
let body: Vec<(&str, &str)> = errors
.violations()
.iter()
.map(|v| (v.field.unwrap_or("(root)"), v.message))
.collect();
assert_eq!(
body,
vec![("email", "must contain @"), ("password", "must be at least 8 characters")]
);For ready-made typed fields to validate — email, port, percentages, bounded
strings, and more — pair this crate with
reliakit-primitives. The
config_check example in the reliakit umbrella crate shows primitives,
validate, and secret working together on one config.
§Feature flags
std(default) enablesstd::error::ErrorforValidationErrorand impliesalloc.allocenablesValidationErrorandValidateResult, which collect multipleViolations in aVec.
§no_std
The crate supports no_std. The Validate trait, Valid<T>, and
Violation are available without alloc; implement Validate with your
own error type in allocation-free contexts. ValidationError and
ValidateResult require the alloc feature (enabled by default via std).
Structs§
- Valid
- A value that has been successfully validated.
- Validation
Error - One or more validation failures collected during validation.
- Violation
- A single failed constraint, optionally associated with a named field.
Traits§
- Validate
- A type that can validate itself.
Type Aliases§
- Validate
Result - Result alias for validation operations.