domainstack 1.1.1

Write validation once, use everywhere: Rust rules auto-generate JSON Schema + OpenAPI + TypeScript/Zod. WASM browser validation. Axum/Actix/Rocket adapters.
Documentation
//! Demonstrates serde integration with ValidateOnDeserialize
//!
//! This example shows how validation happens automatically during JSON deserialization.
//! With #[derive(ValidateOnDeserialize)], you get:
//! - Automatic validation during deserialization
//! - Better error messages (validation errors, not serde errors)
//! - Type-safe: if you have the type, it's guaranteed valid
//! - Single step: no separate .validate() call needed
//!
//! Run with:
//! ```sh
//! cargo run --example serde_validation --features serde,regex
//! ```

use domainstack_derive::ValidateOnDeserialize;

/// User registration with automatic validation during deserialization
#[derive(ValidateOnDeserialize, Debug)]
struct UserRegistration {
    #[validate(email)]
    #[validate(max_len = 255)]
    email: String,

    #[validate(range(min = 18, max = 120))]
    age: u8,

    #[validate(alphanumeric)]
    #[validate(min_len = 3)]
    #[validate(max_len = 20)]
    username: String,

    /// Optional fields work seamlessly
    display_name: Option<String>,
}

/// Configuration with custom serde attributes
#[derive(ValidateOnDeserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct ServerConfig {
    #[validate(range(min = 1024, max = 65535))]
    server_port: u16,

    #[validate(min_len = 1)]
    #[validate(max_len = 200)]
    server_host: String,

    #[serde(default = "default_workers")]
    #[validate(range(min = 1, max = 128))]
    worker_threads: u8,
}

fn default_workers() -> u8 {
    4
}

fn main() {
    println!("=== Serde Integration with ValidateOnDeserialize ===\n");

    // ============================================
    // 1. VALID DESERIALIZATION
    // ============================================
    println!("1. Valid Deserialization:\n");

    let valid_json = r#"{
        "email": "alice@example.com",
        "age": 25,
        "username": "alice123",
        "display_name": "Alice Smith"
    }"#;

    match serde_json::from_str::<UserRegistration>(valid_json) {
        Ok(user) => {
            println!("   [ok] Successfully deserialized and validated:");
            println!("     - Email: {}", user.email);
            println!("     - Age: {}", user.age);
            println!("     - Username: {}", user.username);
            println!(
                "     - Display name: {}",
                user.display_name.as_deref().unwrap_or("None")
            );
        }
        Err(e) => println!("   [error] Unexpected error: {}", e),
    }

    // ============================================
    // 2. INVALID EMAIL (Validation Error)
    // ============================================
    println!("\n2. Invalid Email (Validation Rejected):\n");

    let invalid_email_json = r#"{
        "email": "not-an-email",
        "age": 25,
        "username": "alice123"
    }"#;

    match serde_json::from_str::<UserRegistration>(invalid_email_json) {
        Ok(_) => println!("   [error] Should have failed validation!"),
        Err(e) => {
            println!("   [ok] Validation failed during deserialization:");
            println!("     Error: {}", e);
        }
    }

    // ============================================
    // 3. INVALID AGE (Out of Range)
    // ============================================
    println!("\n3. Invalid Age (Out of Range):\n");

    let invalid_age_json = r#"{
        "email": "bob@example.com",
        "age": 15,
        "username": "bob456"
    }"#;

    match serde_json::from_str::<UserRegistration>(invalid_age_json) {
        Ok(_) => println!("   [error] Should have failed validation!"),
        Err(e) => {
            println!("   [ok] Age validation failed:");
            println!("     Error: {}", e);
        }
    }

    // ============================================
    // 4. INVALID USERNAME (Too Short)
    // ============================================
    println!("\n4. Invalid Username (Too Short):\n");

    let invalid_username_json = r#"{
        "email": "carol@example.com",
        "age": 30,
        "username": "ab"
    }"#;

    match serde_json::from_str::<UserRegistration>(invalid_username_json) {
        Ok(_) => println!("   [error] Should have failed validation!"),
        Err(e) => {
            println!("   [ok] Username validation failed:");
            println!("     Error: {}", e);
        }
    }

    // ============================================
    // 5. SERDE ATTRIBUTES (rename_all, default)
    // ============================================
    println!("\n5. Serde Attributes Work:\n");

    // Using camelCase (from #[serde(rename_all = "camelCase")])
    let config_json = r#"{
        "serverPort": 8080,
        "serverHost": "localhost"
    }"#;

    match serde_json::from_str::<ServerConfig>(config_json) {
        Ok(config) => {
            println!("   [ok] Config deserialized with serde attributes:");
            println!("     - Server port: {}", config.server_port);
            println!("     - Server host: {}", config.server_host);
            println!("     - Worker threads: {} (default)", config.worker_threads);
        }
        Err(e) => println!("   [error] Unexpected error: {}", e),
    }

    // ============================================
    // 6. INVALID CONFIG (Port Out of Range)
    // ============================================
    println!("\n6. Invalid Config (Port Out of Range):\n");

    let invalid_config_json = r#"{
        "serverPort": 80,
        "serverHost": "localhost"
    }"#;

    match serde_json::from_str::<ServerConfig>(invalid_config_json) {
        Ok(_) => println!("   [error] Should have failed validation!"),
        Err(e) => {
            println!("   [ok] Port validation failed:");
            println!("     Error: {}", e);
        }
    }

    // ============================================
    // 7. KEY BENEFITS
    // ============================================
    println!("\n7. Key Benefits:\n");
    println!("   [ok] Single step: deserialize + validate in one call");
    println!("   [ok] Better errors: 'age must be between 18 and 120'");
    println!("   [ok] Type safety: if you have User, it's guaranteed valid");
    println!("   [ok] Serde attributes work: rename, default, etc.");
    println!("   [ok] Same validation rules as #[derive(Validate)]");
    println!("   [ok] <2% overhead vs two-step approach (benchmarked)");

    println!("\n=============================================");
    println!("Example complete! ValidateOnDeserialize provides single-step validation during deserialization.");
}