Valida is a robust and extensible validation system for Rust, designed for flexibility, clarity, and internationalization. It supports both synchronous and asynchronous validators, custom rule injection, nested structures, and ergonomic error handling.
✅ Sync & Async Support Mix traditional and async validation with ease — ideal for scenarios involving databases, APIs, or file systems.
🌐 Built-in i18n (Internationalization) Comes with out-of-the-box support for 10 major languages (en, es, de, fr, uk, etc.) and can be easily extended or overridden using rust-i18n
.
🧱 Nested Validation Support Validate deeply nested fields inside Option, Vec, Arc, HashMap and custom structs.
🧪 DSL or Macro-Based Configuration Choose your style: configure validators declaratively using a fluent DSL or use Rust macros for brevity — or combine both approaches.
🚧 Precise Error Separation Validation errors are clearly separated from system-level failures (e.g. DB connection issues), simplifying custom error management and debugging.
📄 Flexible Output Formats Validation errors can be rendered as structured messages in formats like JSON, tree views, or localized text.
profile:
age: age.too_young
device:
name: Minimum length is 2 characters
email: Minimum length is 5 characters
optional:
age: age.too_young
device:
name: Minimum length is 2 characters
devices:
0:
device:
name: Minimum length is 2 characters
age: age.too_young
use valida::prelude::*;
#[Validatable(std::io::Error)]
pub struct Device {
#[validate(trimmed, min_length(2))]
pub name: String,
}
#[Validatable(std::io::Error)]
pub struct User {
#[validate(email, min_length(5))]
pub email: String,
#[validate(min(0))]
pub age: i32,
#[validate(nested(DeviceValidator))]
pub device: Device,
}
#[tokio::main]
async fn main() {
let user = User {
email: "bad".into(),
age: -5,
device: Device { name: "".into() },
};
let result = UserValidator.validate(&user).await;
if let Err(ValidatorFailure::Invalid(e)) = result {
println!("{}", e.to_json("en"));
println!("{}", e.to_json_form("en"));
}
}
use valida::prelude::*;
#[derive(Debug)]
pub struct Device {
pub name: String,
}
#[derive(Debug)]
pub struct User {
pub email: String,
pub age: i32,
pub device: Device,
}
pub struct DeviceValidator;
#[async_trait::async_trait]
impl IValidate<Device, std::io::Error> for DeviceValidator {
fn rules(&self, mut builder: RulesBuilder<Device, std::io::Error>) -> RulesBuilder<Device, std::io::Error> {
builder.field("name", |d| &d.name).trimmed().min_length(2).build();
builder
}
}
pub struct UserValidator;
#[async_trait::async_trait]
impl IValidate<User, std::io::Error> for UserValidator {
fn rules(&self, mut builder: RulesBuilder<User, std::io::Error>) -> RulesBuilder<User, std::io::Error> {
builder.field("email", |u| &u.email).email().min_length(5).build();
builder.field("age", |u| &u.age).min(0).build();
builder.field("device", |u| &u.device).nested(DeviceValidator).build();
builder
}
}
#[tokio::main]
async fn main() {
let user = User {
email: "bad".into(),
age: -5,
device: Device { name: "".into() },
};
let result = UserValidator.validate(&user).await;
if let Err(ValidatorFailure::Invalid(e)) = result {
println!("{}", e.to_json("en"));
println!("{}", e.to_json_form("en"));
}
}
{
"age":"Value must be at least 0",
"device":{
"name":"Minimum length is 2 characters"
},
"email":"Minimum length is 5 characters"
}
{
"age":"Value must be at least 0",
"device[name]":"Minimum length is 2 characters",
"email":"Minimum length is 5 characters"
}
This project aims to show support for Ukraine and its people amidst a war that has been ongoing since 2014. This war has a genocidal nature and has led to the deaths of thousands, injuries to millions, and significant property damage. We believe that the international community should focus on supporting Ukraine and ensuring security and freedom for its people.