email_type_rs/
email.rs

1use std::fmt::{Display, Formatter};
2use std::ops::Deref;
3use std::str::FromStr;
4
5use log::error;
6use serde::{Deserialize, Serialize, Serializer};
7use validate::rules::email;
8
9use crate::error::InvalidEmailError;
10
11/// Email type for type-driven design, also based on
12/// approach [Parse, don't validate](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/)
13///
14/// ## Example:
15///
16/// ```rust
17/// use std::str::FromStr;
18/// use email_type_rs::email::Email;
19///
20/// match Email::from_str("lexi.lambda@gmail.com") {
21///     Ok(email) => println!("email: {}", email.as_ref()),
22///     Err(e) => eprintln!("{}", e)
23/// }
24/// ```
25///
26#[derive(Deserialize, PartialEq, Hash, Eq, Clone, Debug)]
27#[serde(try_from = "String", into = "String")]
28pub struct Email(String);
29
30impl Serialize for Email {
31    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
32        where
33            S: Serializer,
34    {
35        serializer.serialize_str(&self.0)
36    }
37}
38
39impl FromStr for Email {
40    type Err = InvalidEmailError;
41
42    fn from_str(s: &str) -> Result<Self, Self::Err> {
43        match email().validate(&s) {
44            Ok(_) => Ok(Self(s.to_string())),
45
46            Err(e) => {
47                error!("invalid e-mail '{}': {}", s, e.get_message());
48                Err(InvalidEmailError::ParseError)
49            }
50        }
51    }
52}
53
54impl AsRef<str> for Email {
55    fn as_ref(&self) -> &str {
56        &self.0
57    }
58}
59
60impl TryFrom<String> for Email {
61    type Error = InvalidEmailError;
62
63    fn try_from(value: String) -> Result<Self, Self::Error> {
64        Email::from_str(&value)
65    }
66}
67
68impl From<Email> for String {
69    fn from(value: Email) -> Self {
70        value.0
71    }
72}
73
74impl Deref for Email {
75    type Target = str;
76
77    fn deref(&self) -> &Self::Target {
78        &self.0
79    }
80}
81
82impl Display for Email {
83    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
84        write!(f, "{}", self.0)
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use std::str::FromStr;
91
92    use fake::{Fake, Faker};
93    use fake::faker::internet::en::FreeEmail;
94    use serde::{Deserialize, Serialize};
95
96    use crate::email::Email;
97
98    #[derive(Serialize, Deserialize, PartialEq, Debug)]
99    struct User {
100        pub email: Email
101    }
102
103    #[test]
104    fn return_ok_for_valid_email() {
105        for _ in 1..30 {
106            let email = FreeEmail().fake::<String>();
107            assert!(Email::from_str(&email).is_ok());
108        }
109    }
110
111    #[test]
112    fn return_error_for_invalid_email() {
113        assert!(Email::from_str("").is_err());
114
115        for _ in 1..30 {
116            let value = get_random_string();
117            assert!(Email::from_str(&value).is_err());
118        }
119    }
120
121    #[test]
122    fn serialization_deserialization_test_for_invalid_value() {
123        let json = "{\"email\":\"invalid-email\"}".to_string();
124        match serde_json::from_str::<User>(&json) {
125            Ok(_) => panic!("error expected"),
126            Err(e) => println!("{}", e)
127        }
128    }
129
130    #[test]
131    fn use_as_str() {
132        let email = Email::from_str("a@b.com").unwrap();
133        assert_str_func(&email);
134    }
135
136    fn get_random_string() -> String {
137        Faker.fake::<String>()
138    }
139
140    fn assert_str_func(_: &str) {
141        assert!(true)
142    }
143}