1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
use crate::error::TypeError;
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::fmt;
/// This crate provides a simple and efficient way to parse and validate email addresses.
///
/// # Example
///
/// ```
/// use custom_type::Email;
///
/// let email = Email::parse("example@example.com").unwrap();
/// println!("{}", email);
/// ```
///
/// # Features
///
/// - Parse and validate email addresses using a regular expression.
/// - Normalize email addresses to lowercase.
/// - Custom error type for handling invalid email addresses.
/// ### Parse String To Valid Email
/// Call the `parse()` method to parse `impl ToString` into a valid email.
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Email(String);
impl fmt::Display for Email {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl Email {
/// Parses a given string into a valid email address.
///
/// # Arguments
///
/// * `email` - A string slice that holds the email address to be parsed.
///
/// # Returns
///
/// * `Ok(Self)` if the email is valid.
/// * `Err(TypeError::ParseError)` if the email is invalid.
///
/// # Examples
///
/// ```
/// use custom_type::Email;
///
/// let email = Email::parse("example@example.com");
/// assert!(email.is_ok());
///
/// let invalid_email = Email::parse("invalid-email");
/// assert!(invalid_email.is_err());
/// ```
pub fn parse(email: impl ToString) -> Result<Self, TypeError> {
// Normalize email
let email = email.to_string().to_lowercase();
// Email pattern on regular expression
let email_regex =
Regex::new(r"(?i)^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$")
.unwrap();
// Validate email using regular expression
if email_regex.is_match(&email) {
Ok(Self(email))
} else {
Err(TypeError::ParseError(
"unable to parse email, invalid email.".to_string(),
))
}
}
}
/// ======================================================================
/// ========================= Unit Test
/// ======================================================================
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_emails() {
assert_eq!(
Email::parse("example@example.com"),
Ok(Email("example@example.com".to_string()))
);
assert_eq!(
Email::parse("user.name+tag@example.co.id"),
Ok(Email("user.name+tag@example.co.id".to_string()))
);
assert_eq!(
Email::parse("user_name@example.co.uk"),
Ok(Email("user_name@example.co.uk".to_string()))
);
}
#[test]
fn test_invalid_emails() {
assert_eq!(
Email::parse("plainaddress"),
Err(TypeError::ParseError(
"unable to parse email, invalid email.".to_string()
))
);
assert_eq!(
Email::parse("@missingusername.com"),
Err(TypeError::ParseError(
"unable to parse email, invalid email.".to_string()
))
);
assert_eq!(
Email::parse("username@.com"),
Err(TypeError::ParseError(
"unable to parse email, invalid email.".to_string()
))
);
assert_eq!(
Email::parse("username@.com."),
Err(TypeError::ParseError(
"unable to parse email, invalid email.".to_string()
))
);
assert_eq!(
Email::parse("username@-ex@ample.com"),
Err(TypeError::ParseError(
"unable to parse email, invalid email.".to_string()
))
);
assert_eq!(
Email::parse("username@example..com"),
Err(TypeError::ParseError(
"unable to parse email, invalid email.".to_string()
))
);
}
}