use regex::Regex;
use std::sync::OnceLock;
pub struct Valid;
impl Valid {
fn email_re() -> &'static Regex {
static RE: OnceLock<Regex> = OnceLock::new();
RE.get_or_init(|| Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$").unwrap())
}
fn mobile_re() -> &'static Regex {
static RE: OnceLock<Regex> = OnceLock::new();
RE.get_or_init(|| Regex::new(r"^1[3-9]\d{9}$").unwrap())
}
fn url_re() -> &'static Regex {
static RE: OnceLock<Regex> = OnceLock::new();
RE.get_or_init(|| Regex::new(r"^https?://[^\s/$.?#].[^\s]*$").unwrap())
}
pub fn is_email(s: &str) -> bool { Self::email_re().is_match(s) }
pub fn is_mobile(s: &str) -> bool { Self::mobile_re().is_match(s) }
pub fn is_url(s: &str) -> bool { Self::url_re().is_match(s) }
pub fn is_digits(s: &str) -> bool { !s.is_empty() && s.chars().all(|c| c.is_ascii_digit()) }
pub fn is_alphanumeric(s: &str) -> bool { !s.is_empty() && s.chars().all(|c| c.is_ascii_alphanumeric()) }
pub fn len_between(s: &str, min: usize, max: usize) -> bool {
let len = s.chars().count();
len >= min && len <= max
}
pub fn is_strong_password(s: &str) -> bool {
if s.len() < 8 { return false; }
let has_lower = s.chars().any(|c| c.is_ascii_lowercase());
let has_upper = s.chars().any(|c| c.is_ascii_uppercase());
let has_digit = s.chars().any(|c| c.is_ascii_digit());
has_lower && has_upper && has_digit
}
pub fn is_ipv4(s: &str) -> bool {
s.parse::<std::net::Ipv4Addr>().is_ok()
}
pub fn is_base64(s: &str) -> bool {
base64::Engine::decode(&base64::engine::general_purpose::STANDARD, s).is_ok() && s.len() % 4 == 0
}
}
#[cfg(feature = "validator")]
impl Valid {
pub fn format_validation_errors(errors: &validator::ValidationErrors) -> String {
let mut messages = Vec::new();
for (field, field_errors) in errors.field_errors() {
for error in field_errors {
if let Some(msg) = &error.message {
messages.push(format!("{}: {}", field, msg));
} else {
messages.push(format!("{}: {}", field, error.code));
}
}
}
messages.join("; ")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_email() { assert!(Valid::is_email("a@b.com")); assert!(!Valid::is_email("not-email")); }
#[test]
fn test_mobile() { assert!(Valid::is_mobile("13812345678")); assert!(!Valid::is_mobile("1234")); }
#[test]
fn test_password() { assert!(Valid::is_strong_password("Abcdefg1")); assert!(!Valid::is_strong_password("123456")); }
}