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/9.8.5/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/9.8.5/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/9.8.5/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/9.8.5/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/9.8.5/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/9.8.5/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/9.8.5/assertables/macro.assert_email_address.html\n",
212 " email address must contain an '@' at sign.\n",
213 " a label: `a`,\n",
214 " a debug: `\"hello*example.com\"`,\n",
215 " a: `hello*example.com`",
216 );
217 assert_eq!(actual.unwrap_err(), message);
218 }
219
220 #[test]
221 fn failure_because_local_part_is_blank() {
222 let a = "@example.com";
223 let actual = assert_email_address_as_result!(a);
224 let message = concat!(
225 "assertion failed: `assert_email_address!(a)`\n",
226 "https://docs.rs/assertables/9.8.5/assertables/macro.assert_email_address.html\n",
227 " email address local part must be 1 character or more.\n",
228 " a label: `a`,\n",
229 " a debug: `\"@example.com\"`,\n",
230 " a: `@example.com`,\n",
231 " local part length: 0",
232 );
233 assert_eq!(actual.unwrap_err(), message);
234 }
235
236 #[test]
237 fn failure_because_local_part_is_too_long() {
238 let a = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@example.com";
239 let actual = assert_email_address_as_result!(a);
240 let message = concat!(
241 "assertion failed: `assert_email_address!(a)`\n",
242 "https://docs.rs/assertables/9.8.5/assertables/macro.assert_email_address.html\n",
243 " email address local part must be maximum 64 characters.\n",
244 " a label: `a`,\n",
245 " a debug: `\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@example.com\"`,\n",
246 " a: `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@example.com`,\n",
247 " local part length: 65",
248 );
249 assert_eq!(actual.unwrap_err(), message);
250 }
251
252 #[test]
253 fn failure_because_domain_part_is_blank() {
254 let a = "hello@";
255 let actual = assert_email_address_as_result!(a);
256 let message = concat!(
257 "assertion failed: `assert_email_address!(a)`\n",
258 "https://docs.rs/assertables/9.8.5/assertables/macro.assert_email_address.html\n",
259 " email address domain part must be 1 character or more.\n",
260 " a label: `a`,\n",
261 " a debug: `\"hello@\"`,\n",
262 " a: `hello@`,\n",
263 " domain part length: 0",
264 );
265 assert_eq!(actual.unwrap_err(), message);
266 }
267
268 #[test]
269 fn failure_because_domain_part_is_too_long() {
270 let a = "hello@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
271 let actual = assert_email_address_as_result!(a);
272 let message = concat!(
273 "assertion failed: `assert_email_address!(a)`\n",
274 "https://docs.rs/assertables/9.8.5/assertables/macro.assert_email_address.html\n",
275 " email address domain part must be maximum 255 characters.\n",
276 " a label: `a`,\n",
277 " a debug: `\"hello@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"`,\n",
278 " a: `hello@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`,\n",
279 " domain part length: 256"
280 );
281 assert_eq!(actual.unwrap_err(), message);
282 }
283}
284
285#[macro_export]
333macro_rules! assert_email_address {
334 ($a:expr $(,)?) => {
335 match $crate::assert_email_address_as_result!($a) {
336 Ok(a) => a,
337 Err(err) => panic!("{}", err),
338 }
339 };
340 ($a:expr, $($message:tt)+) => {
341 match $crate::assert_email_address_as_result!($a) {
342 Ok(a) => a,
343 Err(err) => panic!("{}\n{}", format_args!($($message)+), err),
344 }
345 };
346}
347
348#[cfg(test)]
349mod test_assert_email_address {
350 use std::panic;
351
352 #[test]
353 fn success() {
354 let a = "hello@example.com";
355 for _ in 0..1 {
356 let actual = assert_email_address!(a);
357 assert_eq!(actual, ());
358 }
359 }
360
361 #[test]
362 fn failure_because_at_sign_is_absent() {
363 let a = "hello*example.com";
364 let result = panic::catch_unwind(|| {
365 let _actual = assert_email_address!(a);
366 });
367 let message = concat!(
368 "assertion failed: `assert_email_address!(a)`\n",
369 "https://docs.rs/assertables/9.8.5/assertables/macro.assert_email_address.html\n",
370 " email address must contain an '@' at sign.\n",
371 " a label: `a`,\n",
372 " a debug: `\"hello*example.com\"`,\n",
373 " a: `hello*example.com`"
374 );
375 assert_eq!(
376 result
377 .unwrap_err()
378 .downcast::<String>()
379 .unwrap()
380 .to_string(),
381 message
382 );
383 }
384
385 #[test]
386 fn failure_because_local_part_is_blank() {
387 let a = "@example.com";
388 let result = panic::catch_unwind(|| {
389 let _actual = assert_email_address!(a);
390 });
391 let message = concat!(
392 "assertion failed: `assert_email_address!(a)`\n",
393 "https://docs.rs/assertables/9.8.5/assertables/macro.assert_email_address.html\n",
394 " email address local part must be 1 character or more.\n",
395 " a label: `a`,\n",
396 " a debug: `\"@example.com\"`,\n",
397 " a: `@example.com`,\n",
398 " local part length: 0",
399 );
400 assert_eq!(
401 result
402 .unwrap_err()
403 .downcast::<String>()
404 .unwrap()
405 .to_string(),
406 message
407 );
408 }
409
410 #[test]
411 fn failure_because_local_part_is_too_long() {
412 let a = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@example.com";
413 let result = panic::catch_unwind(|| {
414 let _actual = assert_email_address!(a);
415 });
416 let message = concat!(
417 "assertion failed: `assert_email_address!(a)`\n",
418 "https://docs.rs/assertables/9.8.5/assertables/macro.assert_email_address.html\n",
419 " email address local part must be maximum 64 characters.\n",
420 " a label: `a`,\n",
421 " a debug: `\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@example.com\"`,\n",
422 " a: `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@example.com`,\n",
423 " local part length: 65",
424 );
425 assert_eq!(
426 result
427 .unwrap_err()
428 .downcast::<String>()
429 .unwrap()
430 .to_string(),
431 message
432 );
433 }
434
435 #[test]
436 fn failure_because_domain_part_is_blank() {
437 let a = "hello@";
438 let result = panic::catch_unwind(|| {
439 let _actual = assert_email_address!(a);
440 });
441 let message = concat!(
442 "assertion failed: `assert_email_address!(a)`\n",
443 "https://docs.rs/assertables/9.8.5/assertables/macro.assert_email_address.html\n",
444 " email address domain part must be 1 character or more.\n",
445 " a label: `a`,\n",
446 " a debug: `\"hello@\"`,\n",
447 " a: `hello@`,\n",
448 " domain part length: 0",
449 );
450 assert_eq!(
451 result
452 .unwrap_err()
453 .downcast::<String>()
454 .unwrap()
455 .to_string(),
456 message
457 );
458 }
459
460 #[test]
461 fn failure_because_domain_part_is_too_long() {
462 let a = "hello@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
463 let result = panic::catch_unwind(|| {
464 let _actual = assert_email_address!(a);
465 });
466 let message = concat!(
467 "assertion failed: `assert_email_address!(a)`\n",
468 "https://docs.rs/assertables/9.8.5/assertables/macro.assert_email_address.html\n",
469 " email address domain part must be maximum 255 characters.\n",
470 " a label: `a`,\n",
471 " a debug: `\"hello@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"`,\n",
472 " a: `hello@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`,\n",
473 " domain part length: 256",
474 );
475 assert_eq!(
476 result
477 .unwrap_err()
478 .downcast::<String>()
479 .unwrap()
480 .to_string(),
481 message
482 );
483 }
484}
485
486#[macro_export]
515macro_rules! debug_assert_email_address {
516 ($($arg:tt)*) => {
517 if cfg!(debug_assertions) {
518 $crate::assert_email_address!($($arg)*);
519 }
520 };
521}