dator/
mail.rs

1// Validate the local part of the email (the part before the '@').
2fn email_local(s: &str) -> bool {
3    let s = s.as_bytes();
4
5    if s.len() == 0 || s.len() > 64 {
6        return false;
7    }
8
9    let mut last: u8 = 46;
10
11    for c in s.iter() {
12        match c {
13            // Special chars
14            b'#' | b'$' | b'%' | b'&' | b'\'' | b'*' | b'+' | b'-' | b'/' | b'=' | b'?' => {
15                last = *c;
16            }
17            b'^' | b'_' | b'`' | b'{' | b'|' | b'}' | b'~' => {
18                last = *c;
19            }
20            b'0'..=b'9' => {
21                last = *c;
22            }
23            b'A'..=b'Z' => {
24                last = *c;
25            }
26            b'a'..=b'z' => {
27                last = *c;
28            }
29            b'.' => {
30                // local part cant start with dot,
31                // or contains two in a row.
32                if last == b'.' {
33                    return false;
34                }
35                last = *c;
36            }
37            // Any invalid character
38            _ => {
39                return false;
40            }
41        }
42    }
43
44    // Last character cant be dot.
45    last != b'.'
46}
47
48/// Checks whether the given string is a valid email.
49///
50/// This function not allows IP addresses in the domain part (eg.: `bob@[127.0.0.1]`).
51///
52/// # Example
53///
54/// ```
55/// if dator::email("bob@example.com") {
56///     // bob@example.com is a valid email address
57/// }
58/// ```
59/// ```
60/// if !dator::email("bob@[0.0.0.0]") {
61///     // bob@[0.0.0.0] is a valid email address
62/// }
63/// ```
64pub fn email(s: &str) -> bool {
65    let parts: Vec<&str> = s.split('@').collect();
66
67    if parts.len() != 2 {
68        return false;
69    }
70
71    // Check local part
72    match parts.get(0) {
73        Some(part) => {
74            if !email_local(part) {
75                return false;
76            }
77        }
78        None => {
79            return false;
80        }
81    }
82
83    // Check domain part
84    match parts.get(1) {
85        Some(part) => {
86            if !crate::domain(part) {
87                return false;
88            }
89        }
90        None => {
91            return false;
92        }
93    }
94
95    true
96}