serde_email/
email.rs

1use ::std::convert::AsRef;
2use ::std::convert::From;
3use ::std::fmt::Debug;
4use ::std::fmt::Display;
5use ::std::fmt::Formatter;
6use ::std::fmt::Result as FmtResult;
7use ::std::str::FromStr;
8
9use crate::is_valid_email;
10use crate::EmailError;
11
12#[cfg(feature = "serde")]
13mod email_visitor;
14#[cfg(feature = "serde")]
15pub(crate) use self::email_visitor::*;
16
17#[cfg(feature = "serde")]
18mod serde_support;
19
20#[cfg(feature = "sea-orm")]
21mod sea_orm_support;
22
23/// A validated Email object.
24///
25/// These can be created from a string using `Email::from_string`,
26/// `Email::from_str`, or `String::parse`.
27///
28/// Once built you can turn this like a `String`, use with Serde, or Sea Orm.
29///
30/// Note that Email objects _are_ case sensetive.
31/// The email addresses `Email::from_str("bob@example.com")` and `Email::from_str("BoB@example.com")`,
32/// will not be equal to each other.
33#[derive(Clone, Debug, PartialEq, Eq, Hash)]
34pub struct Email {
35    raw_email: String,
36}
37
38impl Email {
39    /// Creates a new Email, from the `String` given.
40    ///
41    /// If the given string doesn't look like a valid email,
42    /// then this will return an EmailError.
43    pub fn from_string(raw_email: String) -> Result<Self, EmailError> {
44        if !is_valid_email(&raw_email) {
45            let err = EmailError::Invalid { raw_email };
46            return Err(err);
47        }
48
49        Ok(Self { raw_email })
50    }
51
52    /// Creates a new Email, from the `str` given.
53    ///
54    /// If the given string doesn't look like a valid email,
55    /// then this will return an EmailError.
56    pub fn from_str<S>(raw_email: S) -> Result<Self, EmailError>
57    where
58        S: AsRef<str>,
59    {
60        Self::from_string(raw_email.as_ref().to_string())
61    }
62
63    /// Returns a new Email, where the email has been uppercased.
64    pub fn to_lowercase(&self) -> Self {
65        Self {
66            raw_email: self.raw_email.to_lowercase(),
67        }
68    }
69
70    /// Returns a new Email, where the internal email has been uppercased.
71    pub fn to_uppercase(&self) -> Self {
72        Self {
73            raw_email: self.raw_email.to_uppercase(),
74        }
75    }
76
77    pub fn as_str<'a>(&'a self) -> &'a str {
78        &self.raw_email
79    }
80}
81
82/// This is a common default, provided in use for stuff like tests.
83///
84/// The default email is `default@example.com`.
85impl Default for Email {
86    fn default() -> Self {
87        Self::from_str("default@example.com").expect("Default Email should always be valid")
88    }
89}
90
91impl Display for Email {
92    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
93        write!(f, "{}", self.raw_email)
94    }
95}
96
97impl From<Email> for String {
98    fn from(email: Email) -> Self {
99        email.raw_email
100    }
101}
102
103impl AsRef<str> for Email {
104    fn as_ref(&self) -> &str {
105        &self.raw_email
106    }
107}
108
109impl AsRef<String> for Email {
110    fn as_ref(&self) -> &String {
111        &self.raw_email
112    }
113}
114
115impl FromStr for Email {
116    type Err = EmailError;
117
118    fn from_str(s: &str) -> Result<Self, Self::Err> {
119        Email::from_str(s)
120    }
121}
122
123impl TryFrom<String> for Email {
124    type Error = EmailError;
125
126    fn try_from(raw: String) -> Result<Self, Self::Error> {
127        Email::from_string(raw)
128    }
129}
130
131impl<'a> TryFrom<&'a str> for Email {
132    type Error = EmailError;
133
134    fn try_from(raw: &'a str) -> Result<Self, Self::Error> {
135        Email::from_str(raw)
136    }
137}
138
139impl<'a> PartialEq<&'a str> for Email {
140    fn eq(&self, other: &&'a str) -> bool {
141        self.raw_email == *other
142    }
143}
144
145impl PartialEq<String> for Email {
146    fn eq(&self, other: &String) -> bool {
147        self.raw_email == *other
148    }
149}
150
151#[cfg(test)]
152mod test_from_string {
153    use super::*;
154
155    #[test]
156    fn it_should_accept_a_valid_email() {
157        let maybe_email = Email::from_string("john@example.com".to_string());
158
159        assert!(maybe_email.is_ok());
160    }
161
162    #[test]
163    fn it_should_not_accept_a_non_valid_email() {
164        let maybe_email = Email::from_string("foxes".to_string());
165
166        assert!(maybe_email.is_err());
167    }
168
169    #[test]
170    fn it_should_not_accept_a_domain_on_its_own() {
171        let maybe_email = Email::from_string("@example.com".to_string());
172
173        assert!(maybe_email.is_err());
174    }
175
176    #[test]
177    fn it_should_not_accept_a_user_part_on_its_own() {
178        let maybe_email = Email::from_string("john@".to_string());
179
180        assert!(maybe_email.is_err());
181    }
182
183    #[test]
184    fn it_should_not_accept_an_empty_string() {
185        let maybe_email = Email::from_string("".to_string());
186
187        assert!(maybe_email.is_err());
188    }
189}
190
191#[cfg(test)]
192mod test_from_str {
193    use super::*;
194
195    #[test]
196    fn it_should_accept_a_valid_email() {
197        let maybe_email = Email::from_str("john@example.com");
198
199        assert!(maybe_email.is_ok());
200    }
201
202    #[test]
203    fn it_should_not_accept_a_non_valid_email() {
204        let maybe_email = Email::from_str("foxes");
205
206        assert!(maybe_email.is_err());
207    }
208}
209
210#[cfg(test)]
211mod test_try_from {
212    use super::*;
213
214    #[test]
215    fn it_should_parse_valid_email_from_str() {
216        let email: Email = "fox@example.com".try_into().unwrap();
217
218        assert_eq!(email, "fox@example.com");
219    }
220
221    #[test]
222    fn it_should_not_parse_invalid_email_from_str() {
223        let maybe_email: Result<Email, EmailError> = "🦊🦊🦊".try_into();
224
225        assert!(maybe_email.is_err());
226    }
227
228    #[test]
229    fn it_should_parse_valid_email_from_string() {
230        let email: Email = "fox@example.com".to_string().try_into().unwrap();
231
232        assert_eq!(email, "fox@example.com");
233    }
234
235    #[test]
236    fn it_should_not_parse_invalid_email_from_string() {
237        let maybe_email: Result<Email, EmailError> = "🦊🦊🦊".to_string().try_into();
238
239        assert!(maybe_email.is_err());
240    }
241}
242
243#[cfg(test)]
244mod test_parse {
245    use super::*;
246
247    #[test]
248    fn it_should_parse_valid_email_from_string() {
249        let email: Email = "fox@example.com".parse().unwrap();
250
251        assert_eq!(email, "fox@example.com");
252    }
253
254    #[test]
255    fn it_should_not_parse_invalid_email_from_string() {
256        let maybe_email: Result<Email, EmailError> = "🦊🦊🦊".parse();
257
258        assert!(maybe_email.is_err());
259    }
260}
261
262#[cfg(test)]
263mod test_display {
264    use super::*;
265
266    #[test]
267    fn it_should_write_same_email_as_given() {
268        let email: Email = "fox@example.com".parse().unwrap();
269        let output: String = format!("{}", email);
270
271        assert!(email == output);
272        assert_eq!(output, "fox@example.com");
273    }
274}
275
276#[cfg(test)]
277mod test_default {
278    use super::*;
279
280    #[test]
281    fn it_should_create_a_valid_default() {
282        let email = Email::default();
283
284        assert!(is_valid_email(&email));
285    }
286}
287
288#[cfg(test)]
289mod test_to_lowercase {
290    use super::*;
291
292    #[test]
293    fn it_should_make_it_lowercase() {
294        let email: Email = "JoE@eXaMpLe.com".parse().unwrap();
295
296        assert_eq!(
297            email.to_lowercase(),
298            Email::from_str("joe@example.com").unwrap()
299        );
300    }
301
302    #[test]
303    fn it_should_not_change_already_lowercase() {
304        let email: Email = "joe@example.com".parse().unwrap();
305
306        assert_eq!(
307            email.to_lowercase(),
308            Email::from_str("joe@example.com").unwrap()
309        );
310    }
311}
312
313#[cfg(test)]
314mod test_to_uppercase {
315    use super::*;
316
317    #[test]
318    fn it_should_make_it_uppercase() {
319        let email: Email = "JoE@eXaMpLe.com".parse().unwrap();
320
321        assert_eq!(
322            email.to_uppercase(),
323            Email::from_str("JOE@EXAMPLE.COM").unwrap()
324        );
325    }
326
327    #[test]
328    fn it_should_not_change_already_uppercase() {
329        let email: Email = "joe@example.com".parse().unwrap();
330
331        assert_eq!(
332            email.to_uppercase(),
333            Email::from_str("JOE@EXAMPLE.COM").unwrap()
334        );
335    }
336}
337
338#[cfg(test)]
339mod test_partial_eq {
340    use super::*;
341
342    #[test]
343    fn it_should_be_equal_to_strs() {
344        let email: Email = "joe@example.com".parse().unwrap();
345        let is_equal = email == "joe@example.com";
346
347        assert!(is_equal);
348    }
349
350    #[test]
351    fn it_should_not_be_equal_to_different_strs() {
352        let email: Email = "joe@example.com".parse().unwrap();
353        let is_not_equal = email != "🦊@example.com";
354
355        assert!(is_not_equal);
356    }
357}