1#[macro_export]
40macro_rules! assert_email_address_as_result {
41 ($a:expr $(,)?) => {
42 match (&$a) {
43 a => {
44 if !a.contains("@") {
45 Err(
46 format!(
47 concat!(
48 "assertion failed: `assert_email_address!(a)`\n",
49 "https://docs.rs/assertables/", env!("CARGO_PKG_VERSION"), "/assertables/macro.assert_email_address.html\n",
50 " email address must contain an '@' at sign.\n",
51 " a label: `{}`,\n",
52 " a debug: `{:?}`,\n",
53 " a: `{}`",
54 ),
55 stringify!($a),
56 $a,
57 a,
58 )
59 )
60 } else {
61 let parts = a.split("@").collect::<Vec<&str>>();
62 match parts.len() {
63 2 => {
64 let (local_part, domain_part) = (parts[0], parts[1]);
65 let local_part_len = local_part.len();
66 let domain_part_len = domain_part.len();
67 if local_part_len < 1 {
68 Err(
69 format!(
70 concat!(
71 "assertion failed: `assert_email_address!(a)`\n",
72 "https://docs.rs/assertables/", env!("CARGO_PKG_VERSION"), "/assertables/macro.assert_email_address.html\n",
73 " email address local part must be 1 character or more.\n",
74 " a label: `{}`,\n",
75 " a debug: `{:?}`,\n",
76 " a: `{}`,\n",
77 " local part length: {}"
78 ),
79 stringify!($a),
80 $a,
81 a,
82 local_part_len,
83 )
84 )
85 }
86 else
87 if local_part_len > 64 {
88 Err(
89 format!(
90 concat!(
91 "assertion failed: `assert_email_address!(a)`\n",
92 "https://docs.rs/assertables/", env!("CARGO_PKG_VERSION"), "/assertables/macro.assert_email_address.html\n",
93 " email address local part must be maximum 64 characters.\n",
94 " a label: `{}`,\n",
95 " a debug: `{:?}`,\n",
96 " a: `{}`,\n",
97 " local part length: {}"
98 ),
99 stringify!($a),
100 $a,
101 a,
102 local_part_len,
103 )
104 )
105 }
106 else
107 if domain_part.len() < 1 {
108 Err(
109 format!(
110 concat!(
111 "assertion failed: `assert_email_address!(a)`\n",
112 "https://docs.rs/assertables/", env!("CARGO_PKG_VERSION"), "/assertables/macro.assert_email_address.html\n",
113 " email address domain part must be 1 character or more.\n",
114 " a label: `{}`,\n",
115 " a debug: `{:?}`,\n",
116 " a: `{}`,\n",
117 " domain part length: {}"
118 ),
119 stringify!($a),
120 $a,
121 a,
122 domain_part_len,
123 )
124 )
125 }
126 else
127 if domain_part.len() > 255 {
128 Err(
129 format!(
130 concat!(
131 "assertion failed: `assert_email_address!(a)`\n",
132 "https://docs.rs/assertables/", env!("CARGO_PKG_VERSION"), "/assertables/macro.assert_email_address.html\n",
133 " email address domain part must be maximum 255 characters.\n",
134 " a label: `{}`,\n",
135 " a debug: `{:?}`,\n",
136 " a: `{}`,\n",
137 " domain part length: {}"
138 ),
139 stringify!($a),
140 $a,
141 a,
142 domain_part_len,
143 )
144 )
145 }
146 else {
147 Ok(())
148 }
149 },
150 _ => {
151 Err(
152 format!(
153 concat!(
154 "assertion failed: `assert_email_address!(a)`\n",
155 "https://docs.rs/assertables/", env!("CARGO_PKG_VERSION"), "/assertables/macro.assert_email_address.html\n",
156 " email address must contain a local part, then an '@' at sign, then a domain part.\n",
157 " a label: `{}`,\n",
158 " a debug: `{:?}`,\n",
159 " a: `{}`",
160 ),
161 stringify!($a),
162 $a,
163 a,
164 )
165 )
166 }
167 }
168 }
169 }
170 }
171 };
172}
173
174#[cfg(test)]
175mod test_assert_email_address_as_result {
176 use std::sync::Once;
177
178 #[test]
179 fn success() {
180 let a = "hello@example.com";
181 for _ in 0..1 {
182 let actual = assert_email_address_as_result!(a);
183 assert_eq!(actual, Ok(()));
184 }
185 }
186
187 #[test]
188 fn success_once() {
189 static A: Once = Once::new();
190 fn a() -> &'static str {
191 if A.is_completed() {
192 panic!("A.is_completed()")
193 } else {
194 A.call_once(|| {})
195 }
196 "hello@example.com"
197 }
198
199 assert_eq!(A.is_completed(), false);
200 let result = assert_email_address_as_result!(a());
201 assert!(result.is_ok());
202 assert_eq!(A.is_completed(), true);
203 }
204
205 #[test]
206 fn failure_because_at_sign_is_absent() {
207 let a = "hello*example.com";
208 let actual = assert_email_address_as_result!(a);
209 let message = concat!(
210 "assertion failed: `assert_email_address!(a)`\n",
211 "https://docs.rs/assertables/",
212 env!("CARGO_PKG_VERSION"),
213 "/assertables/macro.assert_email_address.html\n",
214 " email address must contain an '@' at sign.\n",
215 " a label: `a`,\n",
216 " a debug: `\"hello*example.com\"`,\n",
217 " a: `hello*example.com`",
218 );
219 assert_eq!(actual.unwrap_err(), message);
220 }
221
222 #[test]
223 fn failure_because_local_part_is_blank() {
224 let a = "@example.com";
225 let actual = assert_email_address_as_result!(a);
226 let message = concat!(
227 "assertion failed: `assert_email_address!(a)`\n",
228 "https://docs.rs/assertables/",
229 env!("CARGO_PKG_VERSION"),
230 "/assertables/macro.assert_email_address.html\n",
231 " email address local part must be 1 character or more.\n",
232 " a label: `a`,\n",
233 " a debug: `\"@example.com\"`,\n",
234 " a: `@example.com`,\n",
235 " local part length: 0",
236 );
237 assert_eq!(actual.unwrap_err(), message);
238 }
239
240 #[test]
241 fn failure_because_local_part_is_too_long() {
242 let a = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@example.com";
243 let actual = assert_email_address_as_result!(a);
244 let message = concat!(
245 "assertion failed: `assert_email_address!(a)`\n",
246 "https://docs.rs/assertables/", env!("CARGO_PKG_VERSION"), "/assertables/macro.assert_email_address.html\n",
247 " email address local part must be maximum 64 characters.\n",
248 " a label: `a`,\n",
249 " a debug: `\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@example.com\"`,\n",
250 " a: `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@example.com`,\n",
251 " local part length: 65",
252 );
253 assert_eq!(actual.unwrap_err(), message);
254 }
255
256 #[test]
257 fn failure_because_domain_part_is_blank() {
258 let a = "hello@";
259 let actual = assert_email_address_as_result!(a);
260 let message = concat!(
261 "assertion failed: `assert_email_address!(a)`\n",
262 "https://docs.rs/assertables/",
263 env!("CARGO_PKG_VERSION"),
264 "/assertables/macro.assert_email_address.html\n",
265 " email address domain part must be 1 character or more.\n",
266 " a label: `a`,\n",
267 " a debug: `\"hello@\"`,\n",
268 " a: `hello@`,\n",
269 " domain part length: 0",
270 );
271 assert_eq!(actual.unwrap_err(), message);
272 }
273
274 #[test]
275 fn failure_because_domain_part_is_too_long() {
276 let a = "hello@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
277 let actual = assert_email_address_as_result!(a);
278 let message = concat!(
279 "assertion failed: `assert_email_address!(a)`\n",
280 "https://docs.rs/assertables/", env!("CARGO_PKG_VERSION"), "/assertables/macro.assert_email_address.html\n",
281 " email address domain part must be maximum 255 characters.\n",
282 " a label: `a`,\n",
283 " a debug: `\"hello@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"`,\n",
284 " a: `hello@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`,\n",
285 " domain part length: 256"
286 );
287 assert_eq!(actual.unwrap_err(), message);
288 }
289}
290
291#[macro_export]
339macro_rules! assert_email_address {
340 ($a:expr $(,)?) => {
341 match $crate::assert_email_address_as_result!($a) {
342 Ok(a) => a,
343 Err(err) => panic!("{}", err),
344 }
345 };
346 ($a:expr, $($message:tt)+) => {
347 match $crate::assert_email_address_as_result!($a) {
348 Ok(a) => a,
349 Err(err) => panic!("{}\n{}", format_args!($($message)+), err),
350 }
351 };
352}
353
354#[cfg(test)]
355mod test_assert_email_address {
356 use std::panic;
357
358 #[test]
359 fn success() {
360 let a = "hello@example.com";
361 for _ in 0..1 {
362 let actual = assert_email_address!(a);
363 assert_eq!(actual, ());
364 }
365 }
366
367 #[test]
368 fn failure_because_at_sign_is_absent() {
369 let a = "hello*example.com";
370 let result = panic::catch_unwind(|| {
371 let _actual = assert_email_address!(a);
372 });
373 let message = concat!(
374 "assertion failed: `assert_email_address!(a)`\n",
375 "https://docs.rs/assertables/",
376 env!("CARGO_PKG_VERSION"),
377 "/assertables/macro.assert_email_address.html\n",
378 " email address must contain an '@' at sign.\n",
379 " a label: `a`,\n",
380 " a debug: `\"hello*example.com\"`,\n",
381 " a: `hello*example.com`"
382 );
383 assert_eq!(
384 result
385 .unwrap_err()
386 .downcast::<String>()
387 .unwrap()
388 .to_string(),
389 message
390 );
391 }
392
393 #[test]
394 fn failure_because_local_part_is_blank() {
395 let a = "@example.com";
396 let result = panic::catch_unwind(|| {
397 let _actual = assert_email_address!(a);
398 });
399 let message = concat!(
400 "assertion failed: `assert_email_address!(a)`\n",
401 "https://docs.rs/assertables/",
402 env!("CARGO_PKG_VERSION"),
403 "/assertables/macro.assert_email_address.html\n",
404 " email address local part must be 1 character or more.\n",
405 " a label: `a`,\n",
406 " a debug: `\"@example.com\"`,\n",
407 " a: `@example.com`,\n",
408 " local part length: 0",
409 );
410 assert_eq!(
411 result
412 .unwrap_err()
413 .downcast::<String>()
414 .unwrap()
415 .to_string(),
416 message
417 );
418 }
419
420 #[test]
421 fn failure_because_local_part_is_too_long() {
422 let a = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@example.com";
423 let result = panic::catch_unwind(|| {
424 let _actual = assert_email_address!(a);
425 });
426 let message = concat!(
427 "assertion failed: `assert_email_address!(a)`\n",
428 "https://docs.rs/assertables/", env!("CARGO_PKG_VERSION"), "/assertables/macro.assert_email_address.html\n",
429 " email address local part must be maximum 64 characters.\n",
430 " a label: `a`,\n",
431 " a debug: `\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@example.com\"`,\n",
432 " a: `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@example.com`,\n",
433 " local part length: 65",
434 );
435 assert_eq!(
436 result
437 .unwrap_err()
438 .downcast::<String>()
439 .unwrap()
440 .to_string(),
441 message
442 );
443 }
444
445 #[test]
446 fn failure_because_domain_part_is_blank() {
447 let a = "hello@";
448 let result = panic::catch_unwind(|| {
449 let _actual = assert_email_address!(a);
450 });
451 let message = concat!(
452 "assertion failed: `assert_email_address!(a)`\n",
453 "https://docs.rs/assertables/",
454 env!("CARGO_PKG_VERSION"),
455 "/assertables/macro.assert_email_address.html\n",
456 " email address domain part must be 1 character or more.\n",
457 " a label: `a`,\n",
458 " a debug: `\"hello@\"`,\n",
459 " a: `hello@`,\n",
460 " domain part length: 0",
461 );
462 assert_eq!(
463 result
464 .unwrap_err()
465 .downcast::<String>()
466 .unwrap()
467 .to_string(),
468 message
469 );
470 }
471
472 #[test]
473 fn failure_because_domain_part_is_too_long() {
474 let a = "hello@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
475 let result = panic::catch_unwind(|| {
476 let _actual = assert_email_address!(a);
477 });
478 let message = concat!(
479 "assertion failed: `assert_email_address!(a)`\n",
480 "https://docs.rs/assertables/", env!("CARGO_PKG_VERSION"), "/assertables/macro.assert_email_address.html\n",
481 " email address domain part must be maximum 255 characters.\n",
482 " a label: `a`,\n",
483 " a debug: `\"hello@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"`,\n",
484 " a: `hello@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`,\n",
485 " domain part length: 256",
486 );
487 assert_eq!(
488 result
489 .unwrap_err()
490 .downcast::<String>()
491 .unwrap()
492 .to_string(),
493 message
494 );
495 }
496}
497
498#[macro_export]
527macro_rules! debug_assert_email_address {
528 ($($arg:tt)*) => {
529 if $crate::cfg!(debug_assertions) {
530 $crate::assert_email_address!($($arg)*);
531 }
532 };
533}