Skip to main content

ferro_rs/validation/
async_rule.rs

1//! Async validation rule trait.
2
3use async_trait::async_trait;
4use serde_json::Value;
5
6/// An async validation rule for DB-backed or I/O-bound checks.
7///
8/// The async counterpart to [`crate::validation::Rule`]. Implement this for
9/// rules that require async I/O (e.g. DB lookups). Stored as
10/// `Box<dyn AsyncRule>`, mirroring `Box<dyn Rule>`.
11///
12/// `#[async_trait]` is required because stable Rust async fn in traits is not
13/// dyn-compatible — `Box<dyn AsyncRule>` needs the `async_trait` transform.
14///
15/// # Error semantics (D-12)
16/// `validate` returns `Err(message)` ONLY for field-level validation failures.
17/// I/O / infrastructure errors (e.g. a DB connection error) MUST NOT be
18/// returned as a validation message. They are signalled to the validator with
19/// a message prefixed `__infra_error__:` so [`crate::validation::AsyncValidator`]
20/// can re-raise them as a framework error (HTTP 500) instead of a field error.
21///
22/// # Safety
23/// `Send + Sync` are required: boxed trait objects are held across `.await`
24/// points and shared across async request tasks.
25#[async_trait]
26pub trait AsyncRule: Send + Sync {
27    /// Validate the field value. `Ok(())` on pass; `Err(message)` on a
28    /// field-level validation failure. See trait docs for the
29    /// `__infra_error__:` sentinel used for infrastructure failures.
30    async fn validate(&self, field: &str, value: &Value, data: &Value) -> Result<(), String>;
31
32    /// Rule name, used for custom-message lookup (e.g. `"unique"`).
33    fn name(&self) -> &'static str;
34}