1use regex::Regex;
4use std::sync::OnceLock;
5
6static EMAIL_REGEX: OnceLock<Regex> = OnceLock::new();
7
8fn get_email_regex() -> &'static Regex {
9 EMAIL_REGEX.get_or_init(|| {
10 Regex::new(r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
11 .expect("Failed to compile email regex")
12 })
13}
14
15pub fn is_valid_email(email: &str) -> bool {
28 if email.is_empty() || email.len() > 254 {
29 return false;
30 }
31
32 get_email_regex().is_match(email)
33}
34
35pub fn is_valid_email_with_domain(email: &str, allowed_domains: &[&str]) -> bool {
37 if !is_valid_email(email) {
38 return false;
39 }
40
41 if let Some(domain) = email.split('@').nth(1) {
42 allowed_domains.iter().any(|&d| domain == d)
43 } else {
44 false
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 #[test]
53 fn test_valid_emails() {
54 assert!(is_valid_email("user@example.com"));
55 assert!(is_valid_email("test.email@domain.com"));
56 assert!(is_valid_email("user+tag@example.co.uk"));
57 assert!(is_valid_email("a@b.c"));
58 }
59
60 #[test]
61 fn test_invalid_emails() {
62 assert!(!is_valid_email(""));
63 assert!(!is_valid_email("invalid"));
64 assert!(!is_valid_email("@example.com"));
65 assert!(!is_valid_email("user@"));
66 assert!(!is_valid_email("user @example.com"));
67 assert!(!is_valid_email("user@.com"));
68 }
69
70 #[test]
71 fn test_email_with_domain() {
72 assert!(is_valid_email_with_domain(
73 "user@example.com",
74 &["example.com", "test.com"]
75 ));
76 assert!(!is_valid_email_with_domain(
77 "user@other.com",
78 &["example.com", "test.com"]
79 ));
80 }
81}