nom_rfc8288/
streaming.rs

1use nom::{
2    AsChar, IResult, Input, Offset, Parser,
3    branch::alt,
4    character::streaming::char,
5    character::streaming::satisfy,
6    combinator::recognize,
7    error::ParseError,
8    multi::{fold_many0, many1_count},
9    sequence::{delimited, preceded},
10};
11
12use crate::{is_qdtext, is_quoted_pair, is_tchar};
13
14/// ```text
15/// TCHAR = "!" / "#" / "$" / "%" / "&" / "'" / "*"
16///       / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
17///       / DIGIT / ALPHA
18/// ```
19pub fn tchar<I, E>(input: I) -> IResult<I, char, E>
20where
21    I: Input,
22    <I as Input>::Item: AsChar,
23    E: ParseError<I>,
24{
25    satisfy(is_tchar).parse(input)
26}
27
28/// `TOKEN = 1*TCHAR`
29pub fn token<I, E>(input: I) -> IResult<I, I, E>
30where
31    I: Input + Copy + Offset,
32    <I as Input>::Item: AsChar,
33    E: ParseError<I>,
34{
35    recognize(many1_count(tchar)).parse(input)
36}
37
38/// `QDTEXT = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text`
39pub fn qdtext<I, E>(input: I) -> IResult<I, char, E>
40where
41    I: Input + Copy,
42    <I as Input>::Item: AsChar,
43    E: ParseError<I>,
44{
45    satisfy(is_qdtext)(input)
46}
47
48/// `QUOTED-PAIR = "\" ( HTAB / SP / VCHAR / obs-text )`
49fn quoted_pair<I, E>(input: I) -> IResult<I, char, E>
50where
51    I: Input + Copy,
52    <I as Input>::Item: AsChar,
53    E: ParseError<I>,
54{
55    preceded(char('\\'), satisfy(is_quoted_pair)).parse(input)
56}
57
58/// `QUOTED-STRING = DQUOTE *( qdtext / quoted-pair ) DQUOTE`
59pub fn quoted_string<I, E>(input: I) -> IResult<I, String, E>
60where
61    I: Input + Copy + Offset,
62    <I as Input>::Item: AsChar,
63    E: ParseError<I>,
64{
65    delimited(
66        char('"'),
67        fold_many0(
68            alt((qdtext, quoted_pair)),
69            String::default,
70            |mut collection, input| {
71                collection.push(input);
72                collection
73            },
74        ),
75        char('"'),
76    )
77    .parse(input)
78}
79
80#[cfg(test)]
81mod tests {
82    use nom::{Err as OutCome, Needed};
83    use nom_language::error::VerboseError;
84
85    use crate::streaming::{quoted_string, tchar, token};
86
87    #[test]
88    fn test_tchar() {
89        assert_eq!(
90            tchar::<_, VerboseError<&str>>(""),
91            Err(OutCome::Incomplete(Needed::Unknown))
92        );
93        assert_eq!(tchar::<_, VerboseError<&str>>("mbbb"), Ok(("bbb", 'm')));
94        assert_eq!(tchar::<_, VerboseError<&str>>("!aa"), Ok(("aa", '!')));
95        assert!(matches!(
96            tchar::<_, VerboseError<&str>>(","),
97            Err(OutCome::Error(_))
98        ));
99    }
100
101    #[test]
102    fn test_token() {
103        assert!(matches!(
104            token::<_, VerboseError<&str>>(""),
105            Err(OutCome::Incomplete(Needed::Unknown))
106        ));
107        assert_eq!(
108            token::<_, VerboseError<&str>>("mbbb"),
109            Err(OutCome::Incomplete(Needed::Unknown))
110        );
111        assert_eq!(token::<_, VerboseError<&str>>("a,"), Ok((",", "a")));
112        assert!(matches!(
113            token::<_, VerboseError<&str>>(","),
114            Err(OutCome::Error(_))
115        ));
116    }
117
118    #[test]
119    fn test_quoted_string() {
120        assert_eq!(
121            quoted_string::<_, VerboseError<&str>>(r#""""#),
122            Ok(("", String::from(r#""#)))
123        );
124
125        assert_eq!(
126            quoted_string::<_, VerboseError<&str>>(r#""hello""#),
127            Ok(("", String::from(r#"hello"#)))
128        );
129
130        assert_eq!(
131            quoted_string::<_, VerboseError<&str>>(r#""\"hello""#),
132            Ok(("", String::from(r#""hello"#)))
133        );
134
135        assert!(matches!(
136            quoted_string::<_, VerboseError<&str>>(r#""awd"#),
137            Err(OutCome::Incomplete(Needed::Unknown))
138        ));
139
140        assert!(matches!(
141            quoted_string::<_, VerboseError<&str>>(r#" "text""#),
142            Err(OutCome::Error(_))
143        ));
144
145        assert_eq!(
146            quoted_string::<_, VerboseError<&str>>(r#""awd"trailing"#),
147            Ok(("trailing", String::from("awd")))
148        );
149    }
150}