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#[derive(Clone, Debug, PartialEq, Eq, Hash)]
34pub struct Email {
35 raw_email: String,
36}
37
38impl Email {
39 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 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 pub fn to_lowercase(&self) -> Self {
65 Self {
66 raw_email: self.raw_email.to_lowercase(),
67 }
68 }
69
70 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
82impl 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}