use time::{Date, OffsetDateTime}; use crate::errors::AuthBusinessError; pub fn validate_email(raw: &str) -> Result { let email = raw.trim().to_lowercase(); if email.len() < 3 || email.len() > 254 { return Err(AuthBusinessError::BadRequest( "invalid email address".to_string(), )); } if email.chars().any(|c| c.is_whitespace()) { return Err(AuthBusinessError::BadRequest( "invalid email address".to_string(), )); } let Some(at) = email.find('@') else { return Err(AuthBusinessError::BadRequest( "invalid email address".to_string(), )); }; if at == 0 || at == email.len() - 1 || !email[at + 1..].contains('.') { return Err(AuthBusinessError::BadRequest( "invalid email address".to_string(), )); } Ok(email) } pub fn validate_username(name: &str) -> Result { if name.is_empty() || name.len() > 64 { return Err(AuthBusinessError::BadRequest( "username must be between 1 and 64 characters".to_string(), )); } if !name .chars() .all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '.' | '-')) { return Err(AuthBusinessError::BadRequest( "username may only contain letters, digits, underscores, dots and dashes".to_string(), )); } Ok(name.to_string()) } pub fn validate_age(date_of_birth: Option, min_age: u8) -> Result<(), AuthBusinessError> { let Some(dob) = date_of_birth else { return Err(AuthBusinessError::BadRequest( "date of birth is required".to_string(), )); }; let today = OffsetDateTime::now_utc().date(); let mut age = today.year() - dob.year(); if (today.month(), today.day()) < (dob.month(), dob.day()) { age -= 1; } if age < min_age as i32 { return Err(AuthBusinessError::BadRequest(format!( "you must be at least {min_age} years old" ))); } Ok(()) }