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