aster_forge_validation/
email.rs1use crate::{Result, ValidationError};
8
9pub fn validate_email(email: &str) -> Result<()> {
16 if email.len() > 254 {
17 return Err(ValidationError::new("email is too long"));
18 }
19 if email.matches('@').count() != 1 {
20 return Err(ValidationError::new("invalid email format"));
21 }
22 let Some((local, domain)) = email.split_once('@') else {
23 return Err(ValidationError::new("invalid email format"));
24 };
25 if local.is_empty() || domain.is_empty() {
26 return Err(ValidationError::new("invalid email format"));
27 }
28 if !domain.contains('.') {
29 return Err(ValidationError::new("invalid email format"));
30 }
31 Ok(())
32}
33
34pub fn normalize_email(email: &str) -> Result<String> {
40 let normalized = email.trim().to_ascii_lowercase();
41 validate_email(&normalized)?;
42 Ok(normalized)
43}
44
45pub fn email_domain(email: &str) -> Result<String> {
51 let normalized = normalize_email(email)?;
52 normalized
53 .rsplit_once('@')
54 .map(|(_, domain)| domain.to_ascii_lowercase())
55 .ok_or_else(|| ValidationError::new("invalid email format"))
56}
57
58#[cfg(test)]
59mod tests {
60 use super::{email_domain, normalize_email, validate_email};
61
62 #[test]
63 fn validate_email_requires_exactly_one_at_separator() {
64 assert!(validate_email("alice@example.com").is_ok());
65 assert!(validate_email("alice@@example.com").is_err());
66 assert!(validate_email("alice@example@com").is_err());
67 assert!(validate_email("alice.example.com").is_err());
68 assert!(validate_email("@example.com").is_err());
69 assert!(validate_email("alice@").is_err());
70 }
71
72 #[test]
73 fn email_helpers_keep_existing_normalization_contract() {
74 assert_eq!(
75 normalize_email(" Alice@Example.COM ").unwrap(),
76 "alice@example.com"
77 );
78 assert_eq!(email_domain("alice@Example.COM").unwrap(), "example.com");
79 }
80
81 #[test]
82 fn validate_email_rejects_missing_domain_dot_and_overlong_values() {
83 assert!(validate_email("alice@example").is_err());
84 assert!(validate_email("alice@.").is_ok());
85
86 let long_local = "a".repeat(245);
87 let too_long = format!("{long_local}@example.com");
88 assert!(too_long.len() > 254);
89 assert!(validate_email(&too_long).is_err());
90 }
91}