1pub mod quoted_characters {
7 use abnf_core::streaming::{is_VCHAR, WSP};
8 use nom::{
9 branch::alt,
10 bytes::streaming::{tag, take_while_m_n},
11 combinator::recognize,
12 sequence::tuple,
13 IResult,
14 };
15
16 use super::obsolete::obs_qp;
17
18 pub fn quoted_pair(input: &[u8]) -> IResult<&[u8], &[u8]> {
20 let parser = alt((
21 recognize(tuple((
22 tag(b"\\"),
23 alt((take_while_m_n(1, 1, is_VCHAR), WSP)),
24 ))),
25 obs_qp,
26 ));
27
28 let (remaining, parsed) = recognize(parser)(input)?;
29
30 Ok((remaining, parsed))
31 }
32}
33
34pub mod folding_ws_and_comment {
36 use nom::IResult;
37
38 pub fn FWS(_input: &[u8]) -> IResult<&[u8], &[u8]> {
42 unimplemented!()
43 }
44
45 pub fn CFWS(_input: &[u8]) -> IResult<&[u8], &[u8]> {
55 unimplemented!()
56 }
57}
58
59pub mod atom {
61 use abnf_core::streaming::{is_ALPHA, is_DIGIT};
62 use nom::{
63 bytes::streaming::{tag, take_while1},
64 combinator::{opt, recognize},
65 multi::many0,
66 sequence::tuple,
67 IResult,
68 };
69
70 use super::folding_ws_and_comment::CFWS;
71
72 pub fn is_atext(byte: u8) -> bool {
87 let allowed = b"!#$%&'*+-/=?^_`{|}~";
88
89 is_ALPHA(byte) || is_DIGIT(byte) || allowed.contains(&byte)
90 }
91
92 pub fn atom(input: &[u8]) -> IResult<&[u8], &[u8]> {
94 let parser = tuple((opt(CFWS), take_while1(is_atext), opt(CFWS)));
95
96 let (remaining, parsed) = recognize(parser)(input)?;
97
98 Ok((remaining, parsed))
99 }
100
101 pub fn dot_atom_text(input: &[u8]) -> IResult<&[u8], &[u8]> {
103 let parser = tuple((
104 take_while1(is_atext),
105 many0(tuple((tag(b"."), take_while1(is_atext)))),
106 ));
107
108 let (remaining, parsed) = recognize(parser)(input)?;
109
110 Ok((remaining, parsed))
111 }
112
113 pub fn dot_atom(input: &[u8]) -> IResult<&[u8], &[u8]> {
115 let parser = tuple((opt(CFWS), dot_atom_text, opt(CFWS)));
116
117 let (remaining, parsed) = recognize(parser)(input)?;
118
119 Ok((remaining, parsed))
120 }
121
122 }
133
134pub mod quoted_strings {
136 use abnf_core::streaming::DQUOTE;
137 use nom::{
138 branch::alt,
139 bytes::streaming::take_while_m_n,
140 combinator::{opt, recognize},
141 multi::many0,
142 sequence::tuple,
143 IResult,
144 };
145
146 use super::{
147 folding_ws_and_comment::{CFWS, FWS},
148 obsolete::is_obs_qtext,
149 quoted_characters::quoted_pair,
150 };
151
152 pub fn is_qtext(byte: u8) -> bool {
156 match byte {
157 33 | 35..=91 | 93..=126 => true,
158 _ if is_obs_qtext(byte) => true,
159 _ => false,
160 }
161 }
162
163 pub fn qcontent(input: &[u8]) -> IResult<&[u8], &[u8]> {
165 let parser = alt((take_while_m_n(1, 1, is_qtext), quoted_pair));
166
167 let (remaining, parsed) = recognize(parser)(input)?;
168
169 Ok((remaining, parsed))
170 }
171
172 pub fn quoted_string(input: &[u8]) -> IResult<&[u8], &[u8]> {
174 let parser = tuple((
175 opt(CFWS),
176 DQUOTE,
177 many0(tuple((opt(FWS), qcontent))),
178 opt(FWS),
179 DQUOTE,
180 opt(CFWS),
181 ));
182
183 let (remaining, parsed) = recognize(parser)(input)?;
184
185 Ok((remaining, parsed))
186 }
187}
188
189pub mod miscellaneous {
191 use nom::{branch::alt, IResult};
192
193 use super::{atom::atom, quoted_strings::quoted_string};
194
195 pub fn word(input: &[u8]) -> IResult<&[u8], &[u8]> {
197 alt((atom, quoted_string))(input)
198 }
199
200 }
206
207pub mod datetime {
209 use abnf_core::streaming::is_DIGIT;
210 use nom::{
211 branch::alt,
212 bytes::streaming::{tag, tag_no_case, take_while_m_n},
213 combinator::{opt, recognize},
214 sequence::tuple,
215 IResult,
216 };
217
218 use super::folding_ws_and_comment::{CFWS, FWS};
219
220 pub fn date_time(input: &[u8]) -> IResult<&[u8], &[u8]> {
222 let parser = tuple((opt(tuple((day_of_week, tag(b",")))), date, time, opt(CFWS)));
223
224 let (remaining, parsed) = recognize(parser)(input)?;
225
226 Ok((remaining, parsed))
227 }
228
229 pub fn day_of_week(input: &[u8]) -> IResult<&[u8], &[u8]> {
231 let parser = tuple((opt(FWS), day_name));
232
233 let (remaining, parsed) = recognize(parser)(input)?;
234
235 Ok((remaining, parsed))
236 }
237
238 pub fn day_name(input: &[u8]) -> IResult<&[u8], &[u8]> {
240 let parser = alt((
241 tag_no_case(b"Mon"),
242 tag_no_case(b"Tue"),
243 tag_no_case(b"Wed"),
244 tag_no_case(b"Thu"),
245 tag_no_case(b"Fri"),
246 tag_no_case(b"Sat"),
247 tag_no_case(b"Sun"),
248 ));
249
250 let (remaining, parsed) = recognize(parser)(input)?;
251
252 Ok((remaining, parsed))
253 }
254
255 pub fn date(input: &[u8]) -> IResult<&[u8], &[u8]> {
257 let parser = tuple((day, month, year));
258
259 let (remaining, parsed) = recognize(parser)(input)?;
260
261 Ok((remaining, parsed))
262 }
263
264 pub fn day(input: &[u8]) -> IResult<&[u8], &[u8]> {
266 let parser = tuple((opt(FWS), take_while_m_n(1, 2, is_DIGIT), FWS));
267
268 let (remaining, parsed) = recognize(parser)(input)?;
269
270 Ok((remaining, parsed))
271 }
272
273 pub fn month(input: &[u8]) -> IResult<&[u8], &[u8]> {
275 let parser = alt((
276 tag_no_case(b"Jan"),
277 tag_no_case(b"Feb"),
278 tag_no_case(b"Mar"),
279 tag_no_case(b"Apr"),
280 tag_no_case(b"May"),
281 tag_no_case(b"Jun"),
282 tag_no_case(b"Jul"),
283 tag_no_case(b"Aug"),
284 tag_no_case(b"Sep"),
285 tag_no_case(b"Oct"),
286 tag_no_case(b"Nov"),
287 tag_no_case(b"Dec"),
288 ));
289
290 let (remaining, parsed) = recognize(parser)(input)?;
291
292 Ok((remaining, parsed))
293 }
294
295 pub fn year(input: &[u8]) -> IResult<&[u8], &[u8]> {
297 let parser = tuple((FWS, take_while_m_n(4, 8, is_DIGIT), FWS)); let (remaining, parsed) = recognize(parser)(input)?;
300
301 Ok((remaining, parsed))
302 }
303
304 pub fn time(input: &[u8]) -> IResult<&[u8], &[u8]> {
306 let parser = tuple((time_of_day, zone));
307
308 let (remaining, parsed) = recognize(parser)(input)?;
309
310 Ok((remaining, parsed))
311 }
312
313 pub fn time_of_day(input: &[u8]) -> IResult<&[u8], &[u8]> {
315 let parser = tuple((hour, tag(b":"), minute, opt(tuple((tag(b":"), second)))));
316
317 let (remaining, parsed) = recognize(parser)(input)?;
318
319 Ok((remaining, parsed))
320 }
321
322 pub fn hour(input: &[u8]) -> IResult<&[u8], &[u8]> {
324 let parser = take_while_m_n(2, 2, is_DIGIT);
327
328 let (remaining, parsed) = recognize(parser)(input)?;
329
330 Ok((remaining, parsed))
331 }
332
333 pub fn minute(input: &[u8]) -> IResult<&[u8], &[u8]> {
335 let parser = take_while_m_n(2, 2, is_DIGIT);
338
339 let (remaining, parsed) = recognize(parser)(input)?;
340
341 Ok((remaining, parsed))
342 }
343
344 pub fn second(input: &[u8]) -> IResult<&[u8], &[u8]> {
346 let parser = take_while_m_n(2, 2, is_DIGIT);
349
350 let (remaining, parsed) = recognize(parser)(input)?;
351
352 Ok((remaining, parsed))
353 }
354
355 pub fn zone(input: &[u8]) -> IResult<&[u8], &[u8]> {
357 let parser = tuple((
360 FWS,
361 alt((tag(b"+"), tag(b"-"))),
362 take_while_m_n(4, 4, is_DIGIT),
363 ));
364
365 let (remaining, parsed) = recognize(parser)(input)?;
366
367 Ok((remaining, parsed))
368 }
369}
370
371pub mod addr_spec {
373 use nom::{
374 branch::alt,
375 bytes::streaming::{tag, take_while_m_n},
376 combinator::{opt, recognize},
377 multi::many0,
378 sequence::tuple,
379 IResult,
380 };
381
382 use super::{
383 atom::dot_atom,
384 folding_ws_and_comment::{CFWS, FWS},
385 obsolete::{obs_domain, obs_dtext, obs_local_part},
386 quoted_strings::quoted_string,
387 };
388
389 pub fn local_part(input: &[u8]) -> IResult<&[u8], &[u8]> {
393 let parser = alt((dot_atom, quoted_string, obs_local_part));
394
395 let (remaining, parsed) = recognize(parser)(input)?;
396
397 Ok((remaining, parsed))
398 }
399
400 pub fn domain(input: &[u8]) -> IResult<&[u8], &[u8]> {
402 let parser = alt((dot_atom, domain_literal, obs_domain));
403
404 let (remaining, parsed) = recognize(parser)(input)?;
405
406 Ok((remaining, parsed))
407 }
408
409 pub fn domain_literal(input: &[u8]) -> IResult<&[u8], &[u8]> {
411 let parser = tuple((
412 opt(CFWS),
413 tag(b"["),
414 many0(tuple((opt(FWS), dtext))),
415 opt(FWS),
416 tag(b"]"),
417 opt(CFWS),
418 ));
419
420 let (remaining, parsed) = recognize(parser)(input)?;
421
422 Ok((remaining, parsed))
423 }
424
425 pub fn dtext(input: &[u8]) -> IResult<&[u8], &[u8]> {
429 fn is_a(byte: u8) -> bool {
430 matches!(byte, 33..=90)
431 }
432
433 fn is_b(byte: u8) -> bool {
434 matches!(byte, 94..=126)
435 }
436
437 let parser = alt((
438 take_while_m_n(1, 1, is_a),
439 take_while_m_n(1, 1, is_b),
440 obs_dtext,
441 ));
442
443 let (remaining, parsed) = recognize(parser)(input)?;
444
445 Ok((remaining, parsed))
446 }
447}
448
449pub mod identification {
451 use nom::{
452 branch::alt,
453 bytes::streaming::tag,
454 combinator::{opt, recognize},
455 multi::many0,
456 sequence::{delimited, tuple},
457 IResult,
458 };
459
460 use super::{
461 addr_spec::dtext,
462 atom::dot_atom_text,
463 folding_ws_and_comment::CFWS,
464 obsolete::{obs_id_left, obs_id_right},
465 };
466
467 pub fn msg_id(input: &[u8]) -> IResult<&[u8], &[u8]> {
478 let parser = tuple((
479 opt(CFWS),
480 tag(b"<"),
481 id_left,
482 tag(b"@"),
483 id_right,
484 tag(b">"),
485 opt(CFWS),
486 ));
487
488 let (remaining, parsed) = recognize(parser)(input)?;
489
490 Ok((remaining, parsed))
491 }
492
493 pub fn id_left(input: &[u8]) -> IResult<&[u8], &[u8]> {
495 let parser = alt((dot_atom_text, obs_id_left));
496
497 let (remaining, parsed) = recognize(parser)(input)?;
498
499 Ok((remaining, parsed))
500 }
501
502 pub fn id_right(input: &[u8]) -> IResult<&[u8], &[u8]> {
504 let parser = alt((dot_atom_text, no_fold_literal, obs_id_right));
505
506 let (remaining, parsed) = recognize(parser)(input)?;
507
508 Ok((remaining, parsed))
509 }
510
511 pub fn no_fold_literal(input: &[u8]) -> IResult<&[u8], &[u8]> {
513 let parser = delimited(tag(b"["), many0(dtext), tag(b"]"));
514
515 let (remaining, parsed) = recognize(parser)(input)?;
516
517 Ok((remaining, parsed))
518 }
519}
520
521pub mod obsolete {
523 use abnf_core::streaming::{CR, LF};
524 use nom::{
525 branch::alt,
526 bytes::streaming::{tag, take_while_m_n},
527 combinator::recognize,
528 multi::many0,
529 sequence::tuple,
530 IResult,
531 };
532
533 use super::{
534 addr_spec::{domain, local_part},
535 atom::atom,
536 miscellaneous::word,
537 quoted_characters::quoted_pair,
538 };
539
540 pub fn is_obs_NO_WS_CTL(byte: u8) -> bool {
545 matches!(byte, 1..=8 | 11 | 12 | 14..=31 | 127)
546 }
547
548 pub fn is_obs_qtext(byte: u8) -> bool {
550 is_obs_NO_WS_CTL(byte)
551 }
552
553 pub fn obs_qp(input: &[u8]) -> IResult<&[u8], &[u8]> {
555 let parser = tuple((
556 tag(b"\\"),
557 alt((
558 take_while_m_n(1, 1, |x| x == 0x00),
559 take_while_m_n(1, 1, is_obs_NO_WS_CTL),
560 LF,
561 CR,
562 )),
563 ));
564
565 let (remaining, parsed) = recognize(parser)(input)?;
566
567 Ok((remaining, parsed))
568 }
569
570 pub fn obs_local_part(input: &[u8]) -> IResult<&[u8], &[u8]> {
574 let parser = tuple((word, many0(tuple((tag(b"."), word)))));
575
576 let (remaining, parsed) = recognize(parser)(input)?;
577
578 Ok((remaining, parsed))
579 }
580
581 pub fn obs_domain(input: &[u8]) -> IResult<&[u8], &[u8]> {
583 let parser = tuple((atom, many0(tuple((tag(b"."), atom)))));
584
585 let (remaining, parsed) = recognize(parser)(input)?;
586
587 Ok((remaining, parsed))
588 }
589
590 pub fn obs_dtext(input: &[u8]) -> IResult<&[u8], &[u8]> {
592 let parser = alt((take_while_m_n(1, 1, is_obs_NO_WS_CTL), quoted_pair));
593
594 let (remaining, parsed) = recognize(parser)(input)?;
595
596 Ok((remaining, parsed))
597 }
598
599 pub fn obs_id_left(input: &[u8]) -> IResult<&[u8], &[u8]> {
603 local_part(input)
604 }
605
606 pub fn obs_id_right(input: &[u8]) -> IResult<&[u8], &[u8]> {
608 domain(input)
609 }
610}