Databases
vld-sea turns the Set / Unchanged values of a SeaORM ActiveModel into a JSON object and validates it against a vld schema, so invalid data never reaches insert() / update().
Install
Cargo.toml
[dependencies]
vld = "0.5"
vld-sea = "0.5" # SeaORM 1 by default
sea-orm = { version = "1", features = ["macros", "sqlx-sqlite", "runtime-tokio-rustls"] }
Features
| Feature | Default | Effect |
|---|---|---|
sea-orm-1 | yes | Build against SeaORM 1 |
sea-orm-2 | Build against SeaORM 2 (Rust 1.94+) | |
with-json | Map JSON columns | |
with-chrono | Map chrono date / time / datetime columns | |
with-time | Map time date / time / datetime columns | |
with-uuid | Map UUID columns | |
with-rust_decimal | Map rust_decimal columns | |
with-bigdecimal | Map bigdecimal columns | |
postgres-array | Map Postgres array columns |
Exactly one of sea-orm-1 / sea-orm-2 must be enabled. Each with-* feature also enables the SeaORM feature of the same name. Without it, columns of that type are passed to the schema as null.
vld-sea = { version = "0.5", default-features = false, features = ["sea-orm-2", "with-chrono", "with-uuid"] }
MSRV: 1.85 with sea-orm-1, 1.94 with sea-orm-2 (see MSRV).
Example
use sea_orm::Set;
use vld_sea::prelude::*;
vld::schema! {
#[derive(Debug, Clone)]
pub struct UserInput {
pub name: String => vld::string().min(1).max(100),
pub email: String => vld::string().email(),
}
}
mod user {
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "users")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub name: String,
pub email: String,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
// Validate on every insert / update:
vld_sea::impl_vld_before_save!(ActiveModel, super::UserInput);
}
fn check() {
let am = user::ActiveModel {
id: Set(1),
name: Set("".to_owned()),
email: Set("not-an-email".to_owned()),
};
assert!(validate_active::<UserInput, _>(&am).is_err());
}
API
validate_active::<S, A>(&active_model)— validate theSet/Unchangedfields; returns the parsedS.validate_model::<S, T>(&value)— validate anySerializevalue (aModel, an input DTO); returnsS.validate_json::<S>(&json)— validate a rawserde_json::Value.before_save::<S, A>(&active_model)— likevalidate_active, but returnsResult<(), DbErr>for use insideActiveModelBehavior::before_save.impl_vld_before_save!(ActiveModel, Schema)/impl_vld_before_save!(ActiveModel, insert: A, update: B)— implementActiveModelBehaviorwith validation inbefore_save.active_model_to_json(&active_model)— the JSON object the schema sees.Validated<S, T>— validated wrapper (new,inner,into_inner,Deref,Serialize).VldSeaError—Validation(VldError)orSerialization(String); converts intoDbErr::Custom.vld_sea::sea_orm— the selected SeaORM crate, re-exported.
Gotchas
NotSetfields are omitted. For partial updates, use a schema whose fields are.optional(), or theinsert:/update:form ofimpl_vld_before_save!with a separate update schema.- Column names are the entity's column names (
Column::as_str()), not Rust field names, if the two differ. - Values map like
serde_json::to_valueof the Rust type:Bytesas an array ofu8, chrono / time dates asYYYY-MM-DD, zoned datetimes as RFC 3339, UUIDs and decimals as strings,f32through its decimal form. On SeaORM 2,ActiveEnumvalues map to their string value. - A column type whose
with-*feature is off, and aNonevalue, becomenull.