use domainstack_derive::ValidateOnDeserialize;
#[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,
display_name: Option<String>,
}
#[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");
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),
}
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);
}
}
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);
}
}
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);
}
}
println!("\n5. Serde Attributes Work:\n");
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),
}
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);
}
}
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.");
}