1use nom::{
2 branch::alt,
3 bytes::complete::{tag, take_while1},
4 character::complete::space0,
5 combinator::{into, recognize},
6 multi::many0,
7 sequence::{pair, terminated, tuple},
8 IResult,
9};
10use std::fmt;
11
12use crate::text::misc_token::unstructured;
13use crate::text::whitespace::{foldable_line, obs_crlf};
14
15#[derive(PartialEq, Clone)]
16pub struct Kv2<'a>(pub &'a [u8], pub &'a [u8]);
17impl<'a> From<(&'a [u8], &'a [u8])> for Kv2<'a> {
18 fn from(pair: (&'a [u8], &'a [u8])) -> Self {
19 Self(pair.0, pair.1)
20 }
21}
22impl<'a> fmt::Debug for Kv2<'a> {
23 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
24 fmt.debug_tuple("header::Kv2")
25 .field(&String::from_utf8_lossy(self.0))
26 .field(&String::from_utf8_lossy(self.1))
27 .finish()
28 }
29}
30
31#[derive(Debug, PartialEq, Clone)]
32pub enum Field<'a> {
33 Good(Kv2<'a>),
34 Bad(&'a [u8]),
35}
36impl<'a> From<Kv2<'a>> for Field<'a> {
37 fn from(kv: Kv2<'a>) -> Self {
38 Self::Good(kv)
39 }
40}
41impl<'a> From<&'a [u8]> for Field<'a> {
42 fn from(bad: &'a [u8]) -> Self {
43 Self::Bad(bad)
44 }
45}
46
47pub fn header_kv(input: &[u8]) -> IResult<&[u8], Vec<Field>> {
49 terminated(
50 many0(alt((into(correct_field), into(foldable_line)))),
51 obs_crlf,
52 )(input)
53}
54
55pub fn field_any(input: &[u8]) -> IResult<&[u8], &[u8]> {
56 terminated(
57 take_while1(|c| (0x21..=0x7E).contains(&c) && c != 0x3A),
58 tuple((space0, tag(b":"), space0)),
59 )(input)
60}
61
62pub fn correct_field(input: &[u8]) -> IResult<&[u8], Kv2> {
72 terminated(into(pair(field_any, recognize(unstructured))), obs_crlf)(input)
73}