Skip to content

Instantly share code, notes, and snippets.

@xosnrdev
Last active December 22, 2025 10:14
Show Gist options
  • Select an option

  • Save xosnrdev/fa3a3b4c708da997fa3cddbbd533576f to your computer and use it in GitHub Desktop.

Select an option

Save xosnrdev/fa3a3b4c708da997fa3cddbbd533576f to your computer and use it in GitHub Desktop.
Simple RFC IETF 5322, 3414 email-password validation
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