scrutiny 0.1.2

A powerful, standards-compliant validation library for Rust with 50+ rules, conditional logic, and first-class axum integration
Documentation
//! Core traits for the validation system.
//!
//! - [`Validate`] is the main trait, generated by `#[derive(Validate)]`.
//! - [`FieldAccess`] enables cross-field rules like `same`, `required_if`, `gt`, etc.
//! - [`Rule`] lets you create reusable custom validation rules.

use crate::error::ValidationErrors;
use crate::value::FieldValue;

/// The main validation trait. Derived via `#[derive(Validate)]`.
///
/// Call `.validate()` on any struct that derives `Validate` to run all rules.
/// Returns `Ok(())` if valid, or `Err(ValidationErrors)` with all failures.
///
/// # Example
///
/// ```rust
/// use scrutiny::Validate;
/// use scrutiny::traits::Validate as _;
///
/// #[derive(Validate)]
/// struct Login {
///     #[validate(required, email)]
///     email: Option<String>,
///     #[validate(required, min = 8)]
///     password: Option<String>,
/// }
///
/// let login = Login {
///     email: Some("user@example.com".into()),
///     password: Some("hunter42!".into()),
/// };
/// assert!(login.validate().is_ok());
/// ```
pub trait Validate {
    fn validate(&self) -> Result<(), ValidationErrors>;
}

/// Allows rules to look up sibling field values by name at runtime.
///
/// Automatically generated by the derive macro for each struct. This trait
/// bridges Rust's static type system with cross-field rules that need to
/// compare arbitrary fields by name (like `required_if`, `same`, `gt`, etc.)
pub trait FieldAccess {
    fn get_field_value(&self, field_name: &str) -> FieldValue;
}

/// A custom validation rule. Implement this for reusable validation logic.
///
/// # Example
///
/// ```rust
/// use scrutiny::traits::{Rule, FieldAccess};
///
/// struct NoSpam;
///
/// impl Rule<Option<String>> for NoSpam {
///     fn validate(&self, field: &str, value: &Option<String>, _fields: &dyn FieldAccess) -> Result<(), String> {
///         if let Some(v) = value {
///             if v.contains("buy now") {
///                 return Err(format!("The {} field looks like spam.", field));
///             }
///         }
///         Ok(())
///     }
/// }
/// ```
pub trait Rule<T: ?Sized> {
    fn validate(&self, field: &str, value: &T, fields: &dyn FieldAccess) -> Result<(), String>;
}

/// Function signature for custom validators used with `#[validate(custom = my_fn)]`.
///
/// The function receives a reference to the field value and the struct (as `FieldAccess`
/// for cross-field access). Return `Ok(())` if valid, or `Err(message)`.
pub type CustomValidator<T> = fn(&T, &dyn FieldAccess) -> Result<(), String>;