1use nom::{
2 branch::alt,
3 bytes::streaming::{escaped, tag, tag_no_case, take, take_while, take_while1},
4 character::streaming::{char, digit1, one_of},
5 combinator::{map, map_res, opt},
6 multi::{separated_list0, separated_list1},
7 sequence::{delimited, preceded, tuple},
8 IResult,
9};
10
11use std::str::{from_utf8, FromStr};
12
13pub fn number(i: &[u8]) -> IResult<&[u8], u32> {
19 let (i, bytes) = digit1(i)?;
20 match from_utf8(bytes).ok().and_then(|s| u32::from_str(s).ok()) {
21 Some(v) => Ok((i, v)),
22 None => Err(nom::Err::Error(nom::error::make_error(
23 i,
24 nom::error::ErrorKind::MapRes,
25 ))),
26 }
27}
28
29pub fn number_64(i: &[u8]) -> IResult<&[u8], u64> {
31 let (i, bytes) = digit1(i)?;
32 match from_utf8(bytes).ok().and_then(|s| u64::from_str(s).ok()) {
33 Some(v) => Ok((i, v)),
34 None => Err(nom::Err::Error(nom::error::make_error(
35 i,
36 nom::error::ErrorKind::MapRes,
37 ))),
38 }
39}
40
41pub fn sequence_range(i: &[u8]) -> IResult<&[u8], std::ops::RangeInclusive<u32>> {
46 map(tuple((number, tag(":"), number)), |(s, _, e)| s..=e)(i)
47}
48
49pub fn sequence_set(i: &[u8]) -> IResult<&[u8], Vec<std::ops::RangeInclusive<u32>>> {
54 separated_list1(tag(","), alt((sequence_range, map(number, |n| n..=n))))(i)
55}
56
57pub fn string(i: &[u8]) -> IResult<&[u8], &[u8]> {
61 alt((quoted, literal))(i)
62}
63
64pub fn string_utf8(i: &[u8]) -> IResult<&[u8], &str> {
66 map_res(string, from_utf8)(i)
67}
68
69pub fn quoted(i: &[u8]) -> IResult<&[u8], &[u8]> {
71 delimited(
72 char('"'),
73 escaped(
74 take_while1(|byte| is_text_char(byte) && !is_quoted_specials(byte)),
75 '\\',
76 one_of("\\\""),
77 ),
78 char('"'),
79 )(i)
80}
81
82pub fn quoted_utf8(i: &[u8]) -> IResult<&[u8], &str> {
84 map_res(quoted, from_utf8)(i)
85}
86
87pub fn is_quoted_specials(c: u8) -> bool {
89 c == b'"' || c == b'\\'
90}
91
92pub fn literal(input: &[u8]) -> IResult<&[u8], &[u8]> {
95 let mut parser = tuple((tag(b"{"), number, tag(b"}"), tag("\r\n")));
96
97 let (remaining, (_, count, _, _)) = parser(input)?;
98
99 let (remaining, data) = take(count)(remaining)?;
100
101 Ok((remaining, data))
102}
103
104pub fn astring(i: &[u8]) -> IResult<&[u8], &[u8]> {
108 alt((take_while1(is_astring_char), string))(i)
109}
110
111pub fn astring_utf8(i: &[u8]) -> IResult<&[u8], &str> {
113 map_res(astring, from_utf8)(i)
114}
115
116pub fn is_astring_char(c: u8) -> bool {
118 is_atom_char(c) || is_resp_specials(c)
119}
120
121pub fn is_atom_char(c: u8) -> bool {
123 is_char(c) && !is_atom_specials(c)
124}
125
126pub fn is_atom_specials(c: u8) -> bool {
128 c == b'('
129 || c == b')'
130 || c == b'{'
131 || c == b' '
132 || c < 32
133 || is_list_wildcards(c)
134 || is_quoted_specials(c)
135 || is_resp_specials(c)
136}
137
138pub fn is_resp_specials(c: u8) -> bool {
140 c == b']'
141}
142
143pub fn atom(i: &[u8]) -> IResult<&[u8], &str> {
145 map_res(take_while1(is_atom_char), from_utf8)(i)
146}
147
148pub fn nstring(i: &[u8]) -> IResult<&[u8], Option<&[u8]>> {
152 alt((map(nil, |_| None), map(string, Some)))(i)
153}
154
155pub fn nstring_utf8(i: &[u8]) -> IResult<&[u8], Option<&str>> {
157 alt((map(nil, |_| None), map(string_utf8, Some)))(i)
158}
159
160pub fn nil(i: &[u8]) -> IResult<&[u8], &[u8]> {
162 tag_no_case("NIL")(i)
163}
164
165pub fn text(i: &[u8]) -> IResult<&[u8], &str> {
169 map_res(take_while(is_text_char), from_utf8)(i)
170}
171
172pub fn is_text_char(c: u8) -> bool {
174 is_char(c) && c != b'\r' && c != b'\n'
175}
176
177pub fn is_char(c: u8) -> bool {
182 matches!(c, 0x01..=0x7F)
183}
184
185pub fn is_list_wildcards(c: u8) -> bool {
189 c == b'%' || c == b'*'
190}
191
192pub fn paren_delimited<'a, F, O, E>(f: F) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], O, E>
193where
194 F: FnMut(&'a [u8]) -> IResult<&'a [u8], O, E>,
195 E: nom::error::ParseError<&'a [u8]>,
196{
197 delimited(char('('), f, char(')'))
198}
199
200pub fn parenthesized_nonempty_list<'a, F, O, E>(
201 f: F,
202) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], Vec<O>, E>
203where
204 F: FnMut(&'a [u8]) -> IResult<&'a [u8], O, E>,
205 E: nom::error::ParseError<&'a [u8]>,
206{
207 delimited(char('('), separated_list1(char(' '), f), char(')'))
208}
209
210pub fn parenthesized_list<'a, F, O, E>(f: F) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], Vec<O>, E>
211where
212 F: FnMut(&'a [u8]) -> IResult<&'a [u8], O, E>,
213 E: nom::error::ParseError<&'a [u8]>,
214{
215 delimited(
216 char('('),
217 separated_list0(char(' '), f),
218 preceded(
219 opt(char(' ')), char(')'),
221 ),
222 )
223}
224
225pub fn opt_opt<'a, F, O, E>(mut f: F) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], Option<O>, E>
226where
227 F: FnMut(&'a [u8]) -> IResult<&'a [u8], Option<O>, E>,
228{
229 move |i: &[u8]| match f(i) {
230 Ok((i, o)) => Ok((i, o)),
231 Err(nom::Err::Error(_)) => Ok((i, None)),
232 Err(e) => Err(e),
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239 use assert_matches::assert_matches;
240
241 #[test]
242 fn test_quoted() {
243 let (rem, val) = quoted(br#""Hello"???"#).unwrap();
244 assert_eq!(rem, b"???");
245 assert_eq!(val, b"Hello");
246
247 assert_eq!(
249 quoted(br#""Hello \" "???"#),
250 Ok((&b"???"[..], &br#"Hello \" "#[..]))
251 );
252 assert_eq!(
253 quoted(br#""Hello \\ "???"#),
254 Ok((&b"???"[..], &br#"Hello \\ "#[..]))
255 );
256
257 assert!(quoted(br#""Hello \a "???"#).is_err());
259 assert!(quoted(br#""Hello \z "???"#).is_err());
260 assert!(quoted(br#""Hello \? "???"#).is_err());
261
262 let (rem, val) = quoted(br#""Hello \"World\""???"#).unwrap();
263 assert_eq!(rem, br#"???"#);
264 assert_eq!(val, br#"Hello \"World\""#);
266 assert_matches!(quoted(br#""#), Err(nom::Err::Incomplete(_)));
271 assert_matches!(quoted(br#""\"#), Err(nom::Err::Incomplete(_)));
272 assert_matches!(quoted(br#""Hello "#), Err(nom::Err::Incomplete(_)));
273
274 assert_matches!(quoted(br"\"), Err(nom::Err::Error(_)));
276 }
277
278 #[test]
279 fn test_string_literal() {
280 match string(b"{3}\r\nXYZ") {
281 Ok((_, value)) => {
282 assert_eq!(value, b"XYZ");
283 }
284 rsp => panic!("unexpected response {rsp:?}"),
285 }
286 }
287
288 #[test]
289 fn test_string_literal_containing_null() {
290 match string(b"{5}\r\nX\0Y\0Z") {
291 Ok((_, value)) => {
292 assert_eq!(value, b"X\0Y\0Z");
293 }
294 rsp => panic!("unexpected response {rsp:?}"),
295 }
296 }
297
298 #[test]
299 fn test_astring() {
300 match astring(b"text ") {
301 Ok((_, value)) => {
302 assert_eq!(value, b"text");
303 }
304 rsp => panic!("unexpected response {rsp:?}"),
305 }
306 }
307
308 #[test]
309 fn test_sequence_range() {
310 match sequence_range(b"23:28 ") {
311 Ok((_, value)) => {
312 assert_eq!(*value.start(), 23);
313 assert_eq!(*value.end(), 28);
314 assert_eq!(value.collect::<Vec<u32>>(), vec![23, 24, 25, 26, 27, 28]);
315 }
316 rsp => panic!("Unexpected response {rsp:?}"),
317 }
318 }
319
320 #[test]
321 fn test_sequence_set() {
322 match sequence_set(b"1,2:8,10,15:30 ") {
323 Ok((_, value)) => {
324 assert_eq!(value.len(), 4);
325 let v = &value[0];
326 assert_eq!(*v.start(), 1);
327 assert_eq!(*v.end(), 1);
328 let v = &value[1];
329 assert_eq!(*v.start(), 2);
330 assert_eq!(*v.end(), 8);
331 let v = &value[2];
332 assert_eq!(*v.start(), 10);
333 assert_eq!(*v.end(), 10);
334 let v = &value[3];
335 assert_eq!(*v.start(), 15);
336 assert_eq!(*v.end(), 30);
337 }
338 rsp => panic!("Unexpected response {rsp:?}"),
339 }
340 }
341}