rustlavel-validation: Laravel-style validation, written from scratch.
Rules are declared the way they are in Laravel — as a string — or built with methods when the compiler should check them:
# use rustlavel_validation::{validate, Rule, Validator};
# use rustlavel_http::Request;
# async fn example(request: &mut Request) {
let data = validate(request, &[("email", "required|email"), ("age", "integer|min:18")])
.await
.unwrap();
// the same rules, checked at compile time
let same = Validator::from_request(request)
.rule("email", Rule::required().email())
.rule("age", Rule::integer().min(18))
.validate();
# let _ = (data, same);
# }
A failure is an [Errors] — field to messages — which turns into Laravel's
422 body, {"message": "...", "errors": {"email": ["..."]}}, for a client
that wants JSON, and a plain 422 for a browser.
Why the entry point is async
Nothing here awaits yet. It is async because the rules that come next —
Laravel's unique and exists — must ask the database, and this project
treats a stable API as a feature. Better one .await today than a breaking
signature change the week rustlavel-db lands.
Using ? in a handler
[Errors] implements [IntoResponse], so a handler can hand one straight
back. Rust's orphan rules stop this crate from also implementing that trait
for Result<_, Errors> — Result belongs to core and the trait belongs to
rustlavel-http — so [attempt] bridges the gap and lets the body of a
handler use ? as normal:
# use rustlavel_validation::{attempt, validate};
# use rustlavel_http::{IntoResponse, Request, Response};
async fn store(mut request: Request) -> impl IntoResponse {
attempt(async move {
let data = validate(&mut request, &[("email", "required|email")]).await?;
Ok(Response::json(data.into_json()))
})
.await
}