reinhardt_core/validators/
email.rs1use super::lazy_patterns::EMAIL_REGEX;
4use super::{ValidationError, ValidationResult, Validator};
5
6pub struct EmailValidator {
8 message: Option<String>,
9}
10
11impl EmailValidator {
12 pub fn new() -> Self {
37 Self { message: None }
38 }
39
40 pub fn with_message(mut self, message: impl Into<String>) -> Self {
52 self.message = Some(message.into());
53 self
54 }
55
56 fn validate_with_length_check(&self, email: &str) -> bool {
58 if email.len() > 320 {
60 return false;
61 }
62
63 let parts: Vec<&str> = email.split('@').collect();
65 if parts.len() != 2 {
66 return false;
67 }
68
69 let local_part = parts[0];
70 let domain_part = parts[1];
71
72 if local_part.is_empty() || local_part.len() > 64 {
74 return false;
75 }
76
77 if domain_part.is_empty() || domain_part.len() > 255 {
79 return false;
80 }
81
82 if local_part.contains("..") {
84 return false;
85 }
86
87 for label in domain_part.split('.') {
89 if label.is_empty() || label.len() > 63 {
90 return false;
91 }
92 }
93
94 EMAIL_REGEX.is_match(email)
96 }
97}
98
99impl Default for EmailValidator {
100 fn default() -> Self {
101 Self::new()
102 }
103}
104
105impl Validator<String> for EmailValidator {
106 fn validate(&self, value: &String) -> ValidationResult<()> {
107 if self.validate_with_length_check(value) {
108 Ok(())
109 } else if let Some(ref msg) = self.message {
110 Err(ValidationError::Custom(msg.clone()))
111 } else {
112 Err(ValidationError::InvalidEmail(value.clone()))
113 }
114 }
115}
116
117impl Validator<str> for EmailValidator {
118 fn validate(&self, value: &str) -> ValidationResult<()> {
119 if self.validate_with_length_check(value) {
120 Ok(())
121 } else if let Some(ref msg) = self.message {
122 Err(ValidationError::Custom(msg.clone()))
123 } else {
124 Err(ValidationError::InvalidEmail(value.to_string()))
125 }
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn test_valid_emails() {
135 let validator = EmailValidator::new();
136 let valid_emails = vec![
137 "test@example.com",
138 "user.name@example.com",
139 "user+tag@example.co.uk",
140 "user_name@example.com",
141 "user%test@example.com",
142 "user-name@sub.example.com",
143 "a@example.com",
144 "test@sub.sub.example.com",
145 "123@example.com",
146 ];
147
148 for email in valid_emails {
149 assert!(
150 validator.validate(email).is_ok(),
151 "Expected {} to be valid",
152 email
153 );
154 }
155 }
156
157 #[test]
158 fn test_invalid_emails() {
159 let validator = EmailValidator::new();
160 let invalid_emails = vec![
161 "invalid-email", "@example.com", "user@", "user..name@example.com", ".user@example.com", "user.@example.com", "user@-example.com", "user@example-.com", "user@example", "user@example.c", "user name@example.com", "user@exam ple.com", "user@@example.com", "user@.example.com", "user@example.com.", ];
177
178 for email in invalid_emails {
179 assert!(
180 validator.validate(email).is_err(),
181 "Expected {} to be invalid",
182 email
183 );
184 }
185 }
186
187 #[test]
188 fn test_length_constraints() {
189 let validator = EmailValidator::new();
190
191 let long_local = format!("{}@example.com", "a".repeat(65));
193 assert!(validator.validate(&long_local).is_err());
194
195 let long_domain = format!("user@{}.com", "a".repeat(252));
197 assert!(validator.validate(&long_domain).is_err());
198
199 let very_long_email = format!("{}@{}.com", "a".repeat(64), "b".repeat(252));
201 assert!(validator.validate(&very_long_email).is_err());
202
203 let long_label = format!("user@{}.example.com", "a".repeat(64));
205 assert!(validator.validate(&long_label).is_err());
206
207 let max_local = format!("{}@example.com", "a".repeat(64));
209 assert!(validator.validate(&max_local).is_ok());
210 }
211
212 #[test]
213 fn test_case_insensitivity() {
214 let validator = EmailValidator::new();
215 assert!(validator.validate("Test@Example.COM").is_ok());
216 assert!(validator.validate("USER@EXAMPLE.COM").is_ok());
217 }
218
219 #[test]
221 fn test_email_validator_with_numbers() {
222 let validator = EmailValidator::new();
223 assert!(validator.validate("123@example.com").is_ok());
224 assert!(validator.validate("user123@example.com").is_ok());
225 assert!(validator.validate("123user@example123.com").is_ok());
226 }
227
228 #[test]
229 fn test_email_validator_with_special_characters() {
230 let validator = EmailValidator::new();
231 assert!(validator.validate("user+tag@example.com").is_ok());
233 assert!(validator.validate("user_name@example.com").is_ok());
234 assert!(validator.validate("user-name@example.com").is_ok());
235 assert!(validator.validate("user.name@example.com").is_ok());
236 assert!(validator.validate("user%test@example.com").is_ok());
237 }
238
239 #[test]
240 fn test_email_validator_subdomains() {
241 let validator = EmailValidator::new();
242 assert!(validator.validate("user@mail.example.com").is_ok());
243 assert!(validator.validate("user@sub.mail.example.com").is_ok());
244 assert!(validator.validate("user@a.b.c.d.example.com").is_ok());
245 }
246
247 #[test]
248 fn test_email_validator_tld_variations() {
249 let validator = EmailValidator::new();
250 assert!(validator.validate("user@example.co").is_ok());
251 assert!(validator.validate("user@example.com").is_ok());
252 assert!(validator.validate("user@example.org").is_ok());
253 assert!(validator.validate("user@example.net").is_ok());
254 assert!(validator.validate("user@example.info").is_ok());
255 assert!(validator.validate("user@example.museum").is_ok());
256 }
257
258 #[test]
259 fn test_email_validator_edge_cases() {
260 let validator = EmailValidator::new();
261 assert!(validator.validate("a@b.co").is_ok());
263
264 assert!(validator.validate("user@123.com").is_ok());
266 assert!(validator.validate("user@example123.com").is_ok());
267 }
268
269 #[test]
270 fn test_email_validator_invalid_formats() {
271 let validator = EmailValidator::new();
272 assert!(validator.validate("user@domain@example.com").is_err());
274
275 assert!(validator.validate("@").is_err());
277 assert!(validator.validate("user@").is_err());
278 assert!(validator.validate("@domain.com").is_err());
279
280 assert!(validator.validate("user name@example.com").is_err());
282 assert!(validator.validate("user@exam ple.com").is_err());
283 assert!(validator.validate("user@example,com").is_err());
284 }
285
286 #[test]
287 fn test_email_validator_dot_rules() {
288 let validator = EmailValidator::new();
289 assert!(validator.validate("user..name@example.com").is_err());
291
292 assert!(validator.validate(".user@example.com").is_err());
294
295 assert!(validator.validate("user.@example.com").is_err());
297
298 assert!(validator.validate("user.name.test@example.com").is_ok());
300 }
301
302 #[test]
303 fn test_email_validator_hyphen_rules() {
304 let validator = EmailValidator::new();
305 assert!(validator.validate("user@my-domain.com").is_ok());
307 assert!(validator.validate("user@my-long-domain-name.com").is_ok());
308
309 assert!(validator.validate("user@-invalid.com").is_err());
311 assert!(validator.validate("user@invalid-.com").is_err());
312 assert!(validator.validate("user@invalid.-com").is_err());
313 assert!(validator.validate("user@invalid.com-").is_err());
314 }
315
316 #[test]
317 fn test_email_validator_returns_correct_error() {
318 let validator = EmailValidator::new();
319 let invalid_email = "invalid";
320 match validator.validate(invalid_email) {
321 Err(ValidationError::InvalidEmail(email)) => {
322 assert_eq!(email, invalid_email);
323 }
324 _ => panic!("Expected InvalidEmail error"),
325 }
326 }
327
328 #[test]
329 fn test_email_validator_with_string_type() {
330 let validator = EmailValidator::new();
331 let email = String::from("test@example.com");
332 assert!(validator.validate(&email).is_ok());
333
334 let invalid = String::from("invalid");
335 assert!(validator.validate(&invalid).is_err());
336 }
337}