Last active
December 22, 2025 10:14
-
-
Save xosnrdev/fa3a3b4c708da997fa3cddbbd533576f to your computer and use it in GitHub Desktop.
Simple RFC IETF 5322, 3414 email-password validation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use std::fmt; | |
| #[derive(Debug)] | |
| pub enum ValidationError { | |
| InvalidEmail, | |
| InvalidPassword, | |
| } | |
| impl fmt::Display for ValidationError { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| match self { | |
| ValidationError::InvalidEmail => write!(f, "Invalid email format"), | |
| ValidationError::InvalidPassword => { | |
| write!(f, "Password must be at least 8 characters long") | |
| } | |
| } | |
| } | |
| } | |
| #[derive(Default)] | |
| pub struct Validator<'a> { | |
| email: Option<&'a str>, | |
| password: Option<&'a str>, | |
| } | |
| impl<'a> Validator<'a> { | |
| pub fn email(mut self, email: &'a str) -> Self { | |
| self.email = Some(email); | |
| self | |
| } | |
| pub fn password(mut self, password: &'a str) -> Self { | |
| self.password = Some(password); | |
| self | |
| } | |
| pub fn validate(self) -> Result<(), ValidationError> { | |
| if self.email.is_some_and(|e| !is_valid_email(e)) { | |
| return Err(ValidationError::InvalidEmail); | |
| } | |
| if self.password.is_some_and(|p| !is_valid_password(p)) { | |
| return Err(ValidationError::InvalidPassword); | |
| } | |
| Ok(()) | |
| } | |
| } | |
| // RFC 5322 cited | |
| fn is_valid_email(s: &str) -> bool { | |
| let b = s.as_bytes(); | |
| let n = b.len(); | |
| if n == 0 || b[0] == b'@' || b[n - 1] == b'.' { | |
| return false; | |
| } | |
| let (mut at, mut dot) = (None, None); | |
| for (i, &c) in b.iter().enumerate() { | |
| if c.is_ascii_whitespace() || (c == b'@' && at.is_some()) || (c == b'.' && dot.is_some()) { | |
| return false; | |
| } | |
| if c == b'@' { | |
| at = Some(i); | |
| } | |
| if c == b'.' { | |
| dot = Some(i); | |
| } | |
| } | |
| matches!((at, dot), (Some(a), Some(d)) if a < d) | |
| } | |
| // RFC 3414 cited | |
| fn is_valid_password(password: &str) -> bool { | |
| !password.trim().is_empty() && password.len() >= 8 | |
| } | |
| #[test] | |
| fn test_is_valid_email() { | |
| // Non-empty | |
| // No leading @ | |
| // No trailing . | |
| // Contains @ | |
| // No appearance of . and @ more than once | |
| // Contains . after @ | |
| // No ASCII whitespace anywhere | |
| assert!(!is_valid_email("")); | |
| assert!(!is_valid_email(" ")); | |
| assert!(!is_valid_email("@domain.com")); | |
| assert!(!is_valid_email("user@domain.")); | |
| assert!(!is_valid_email("userdomain.com")); | |
| assert!(!is_valid_email("user@@domain.com")); | |
| assert!(!is_valid_email("user@domain..com")); | |
| assert!(!is_valid_email("userdomaincom")); | |
| assert!(!is_valid_email("user@domaincom")); | |
| assert!(!is_valid_email("user.domain@com")); | |
| assert!(!is_valid_email("user @domain.com")); | |
| assert!(is_valid_email("user@domain.com")); | |
| } | |
| #[test] | |
| fn test_is_valid_password() { | |
| // Non-empty | |
| // At least 8 characters long | |
| assert!(!is_valid_password("")); | |
| assert!(!is_valid_password(" ")); // 8 spaces | |
| assert!(!is_valid_password("short")); | |
| assert!(is_valid_password("validpassword")); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment