use async_trait::async_trait;
use domainstack::{AsyncRule, AsyncValidate, RuleContext, ValidationContext, ValidationError};
use std::sync::Arc;
#[derive(Clone)]
struct Database {
registered_emails: Vec<String>,
}
impl Database {
fn new() -> Self {
Self {
registered_emails: vec![
"admin@example.com".to_string(),
"user@example.com".to_string(),
],
}
}
async fn email_exists(&self, email: &str) -> bool {
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
self.registered_emails.contains(&email.to_string())
}
async fn username_exists(&self, username: &str) -> bool {
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
username == "admin" || username == "root"
}
}
struct UserRegistration {
username: String,
email: String,
password: String,
}
#[async_trait]
impl AsyncValidate for UserRegistration {
async fn validate_async(&self, ctx: &ValidationContext) -> Result<(), ValidationError> {
let db = ctx
.get_resource::<Database>("db")
.expect("Database not in context");
let mut errors = ValidationError::default();
if db.email_exists(&self.email).await {
errors.push(
domainstack::Path::from("email"),
"email_taken",
format!("Email '{}' is already registered", self.email),
);
}
if db.username_exists(&self.username).await {
errors.push(
domainstack::Path::from("username"),
"username_taken",
format!("Username '{}' is already taken", self.username),
);
}
if self.password.len() < 8 {
errors.push(
domainstack::Path::from("password"),
"password_too_short",
"Password must be at least 8 characters",
);
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
}
fn email_unique() -> AsyncRule<str> {
AsyncRule::new(|email: &str, ctx: &RuleContext, vctx: &ValidationContext| {
let db = vctx
.get_resource::<Database>("db")
.expect("Database not in context");
let email = email.to_string();
let path = ctx.full_path();
async move {
if db.email_exists(&email).await {
ValidationError::single(path, "email_taken", "Email is already registered")
} else {
ValidationError::default()
}
}
})
}
#[tokio::main]
async fn main() {
println!("=== Async Validation Example ===\n");
let db = Arc::new(Database::new());
let ctx = ValidationContext::new().with_resource("db", db);
println!("Example 1: Successful registration");
let user1 = UserRegistration {
username: "newuser".to_string(),
email: "newuser@example.com".to_string(),
password: "securepassword123".to_string(),
};
match user1.validate_async(&ctx).await {
Ok(()) => println!("[ok] User registration validated successfully\n"),
Err(e) => println!("[error] Validation failed:\n{}\n", e),
}
println!("Example 2: Email already taken");
let user2 = UserRegistration {
username: "newuser2".to_string(),
email: "admin@example.com".to_string(), password: "password123".to_string(),
};
match user2.validate_async(&ctx).await {
Ok(()) => println!("[ok] User registration validated successfully\n"),
Err(e) => println!("[error] Validation failed:\n{}\n", e),
}
println!("Example 3: Username already taken");
let user3 = UserRegistration {
username: "admin".to_string(), email: "new@example.com".to_string(),
password: "password123".to_string(),
};
match user3.validate_async(&ctx).await {
Ok(()) => println!("[ok] User registration validated successfully\n"),
Err(e) => println!("[error] Validation failed:\n{}\n", e),
}
println!("Example 4: Multiple validation errors");
let user4 = UserRegistration {
username: "admin".to_string(), email: "user@example.com".to_string(), password: "short".to_string(), };
match user4.validate_async(&ctx).await {
Ok(()) => println!("[ok] User registration validated successfully\n"),
Err(e) => println!("[error] Validation failed:\n{}\n", e),
}
println!("Example 5: Using AsyncRule for email validation");
let rule = email_unique();
let rule_ctx = RuleContext::root("email");
let result = rule.apply("admin@example.com", &rule_ctx, &ctx).await;
if result.is_empty() {
println!("[ok] Email is available\n");
} else {
println!("[error] Email validation failed:\n{}\n", result);
}
let result = rule.apply("available@example.com", &rule_ctx, &ctx).await;
if result.is_empty() {
println!("[ok] Email is available\n");
} else {
println!("[error] Email validation failed:\n{}\n", result);
}
println!("=== All examples completed ===");
}