at_commands/
parser.rs

1//! Module that defines the at command parser
2
3use crate::tuple_concat::TupleConcat;
4
5/// ```
6/// use at_commands::parser::CommandParser;
7/// let (x, y, z) = CommandParser::parse(b"+SYSGPIOREAD:654,\"true\",-65154\r\nOK\r\n")
8///    .expect_identifier(b"+SYSGPIOREAD:")
9///    .expect_int_parameter()
10///    .expect_string_parameter()
11///    .expect_int_parameter()
12///    .expect_identifier(b"\r\nOK\r\n")
13///    .finish()
14///    .unwrap();
15///
16/// assert_eq!(x, 654);
17/// assert_eq!(y, "true");
18/// assert_eq!(z, -65154);
19///
20/// let (w,) = CommandParser::parse(b"+STATUS: READY\r\nOK\r\n")
21///    .expect_identifier(b"+STATUS: ")
22///    .expect_raw_string()
23///    .expect_identifier(b"\r\nOK\r\n")
24///    .finish()
25///    .unwrap();
26///
27/// assert_eq!(w, "READY");
28/// ```
29#[must_use]
30pub struct CommandParser<'a, D> {
31    buffer: &'a [u8],
32    buffer_index: usize,
33    data_valid: bool,
34    data: D,
35}
36
37impl<'a> CommandParser<'a, ()> {
38    /// Start parsing the command
39    pub fn parse(buffer: &'a [u8]) -> CommandParser<'a, ()> {
40        CommandParser {
41            buffer,
42            buffer_index: 0,
43            data_valid: true,
44            data: (),
45        }
46    }
47}
48impl<'a, D> CommandParser<'a, D> {
49    /// Tries reading an identifier
50    pub fn expect_identifier(mut self, identifier: &[u8]) -> Self {
51        // If we're already not valid, then quit
52        if !self.data_valid {
53            return self;
54        }
55
56        if self.buffer[self.buffer_index..].len() < identifier.len() {
57            self.data_valid = false;
58            return self;
59        }
60
61        // Zip together the identifier and the buffer data. If all bytes are the same, the data is valid.
62        self.data_valid = self.buffer[self.buffer_index..]
63            .iter()
64            .zip(identifier)
65            .all(|(buffer, id)| *buffer == *id);
66        // Advance the index
67        self.buffer_index += identifier.len();
68
69        self.trim_space()
70    }
71
72    /// Tries reading an optional identifier.
73    pub fn expect_optional_identifier(mut self, identifier: &[u8]) -> Self {
74        // If we're already not valid, then quit
75        if !self.data_valid {
76            return self;
77        }
78
79        // empty identifier is always valid
80        if self.buffer[self.buffer_index..].is_empty() {
81            return self;
82        }
83
84        if self.buffer[self.buffer_index..].len() < identifier.len() {
85            self.data_valid = false;
86            return self;
87        }
88
89        // Zip together the identifier and the buffer data. If all bytes are the same, the data is valid.
90        self.data_valid = self.buffer[self.buffer_index..]
91            .iter()
92            .zip(identifier)
93            .all(|(buffer, id)| *buffer == *id);
94        // Advance the index
95        self.buffer_index += identifier.len();
96
97        self.trim_space()
98    }
99
100    /// Moves the internal buffer index over the next bit of space characters, if any
101    fn trim_space(mut self) -> Self {
102        // If we're already not valid, then quit
103        if !self.data_valid {
104            return self;
105        }
106
107        while let Some(c) = self.buffer.get(self.buffer_index) {
108            if *c == b' ' {
109                self.buffer_index += 1;
110            } else {
111                break;
112            }
113        }
114
115        self
116    }
117
118    /// Finds the index of the character after the int parameter or the end of the data.
119    fn find_end_of_int_parameter(&self) -> usize {
120        self.buffer_index
121            + self
122                .buffer
123                .get(self.buffer_index..)
124                .map(|buffer| {
125                    buffer
126                        .iter()
127                        .take_while(|byte| {
128                            byte.is_ascii_digit() || **byte == b'-' || **byte == b'+'
129                        })
130                        .count()
131                })
132                .unwrap_or(self.buffer.len())
133    }
134
135    /// Finds the index of the character after the string parameter or the end of the data.
136    fn find_end_of_string_parameter(&self) -> usize {
137        let mut counted_quotes = 0;
138
139        self.buffer_index
140            + self
141                .buffer
142                .get(self.buffer_index..)
143                .map(|buffer| {
144                    buffer
145                        .iter()
146                        .take_while(|byte| {
147                            counted_quotes += (**byte == b'"') as u8;
148                            counted_quotes < 2
149                        })
150                        .count()
151                        + 1
152                })
153                .unwrap_or(self.buffer.len())
154    }
155
156    /// Finds the index of the control character after the non-quoted string or the end of the data.
157    fn find_end_of_raw_string(&self) -> usize {
158        self.buffer_index
159            + self
160                .buffer
161                .get(self.buffer_index..)
162                .map(|buffer| {
163                    buffer
164                        .iter()
165                        .take_while(|byte| !(**byte as char).is_ascii_control())
166                        .count()
167                        + 1
168                })
169                .unwrap_or(self.buffer.len())
170    }
171
172    fn parse_int_parameter(&self) -> (usize, bool, Option<i32>) {
173        let mut new_buffer_index = self.buffer_index;
174        // Get the end index of the current parameter.
175        let parameter_end = self.find_end_of_int_parameter();
176        // Get the bytes in which the int should reside.
177        let int_slice = match self.buffer.get(self.buffer_index..parameter_end) {
178            None => {
179                return (new_buffer_index, false, None);
180            }
181            Some(int_slice) => int_slice,
182        };
183        if int_slice.is_empty() {
184            // We probably hit the end of the buffer.
185            // The parameter is empty but as it is optional not invalid
186            // Advance the index to the character after the parameter separator (comma) if it's there.
187            new_buffer_index =
188                parameter_end + (self.buffer.get(parameter_end) == Some(&b',')) as usize;
189            return (new_buffer_index, true, None);
190        }
191
192        // Skip the leading '+'
193        let int_slice = if int_slice[0] == b'+' {
194            &int_slice[1..]
195        } else {
196            int_slice
197        };
198
199        // Parse the int
200        let parsed_int = crate::formatter::parse_int(int_slice);
201
202        // Advance the index to the character after the parameter separator (comma) if it's there.
203        new_buffer_index = parameter_end + (self.buffer.get(parameter_end) == Some(&b',')) as usize;
204        // If we've found an int, then the data may be valid and we allow the closure to set the result ok data.
205        if let Some(parameter_value) = parsed_int {
206            (new_buffer_index, true, Some(parameter_value))
207        } else {
208            (new_buffer_index, false, None)
209        }
210    }
211
212    fn parse_string_parameter(&self) -> (usize, bool, Option<&'a str>) {
213        let mut new_buffer_index = self.buffer_index;
214        // Get the end index of the current parameter.
215        let parameter_end = self.find_end_of_string_parameter();
216        if parameter_end > self.buffer.len() {
217            // We hit the end of the buffer.
218            // The parameter is empty but as it is optional not invalid
219            return (new_buffer_index, true, None);
220        }
221        // Get the bytes in which the string should reside.
222        let string_slice = &self.buffer[(new_buffer_index + 1)..(parameter_end - 1)];
223
224        let has_comma_after_parameter = if let Some(next_char) = self.buffer.get(parameter_end) {
225            *next_char == b','
226        } else {
227            false
228        };
229
230        // Advance the index to the character after the parameter separator.
231        new_buffer_index = parameter_end + has_comma_after_parameter as usize;
232        // If we've found a valid string, then the data may be valid and we allow the closure to set the result ok data.
233        if let Ok(parameter_value) = core::str::from_utf8(string_slice) {
234            (new_buffer_index, true, Some(parameter_value))
235        } else {
236            (new_buffer_index, false, None)
237        }
238    }
239
240    fn parse_raw_string_parameter(&self) -> (usize, bool, Option<&'a str>) {
241        let mut new_buffer_index = self.buffer_index;
242        // Get the end index of the current string.
243        let end = self.find_end_of_raw_string();
244        // Get the bytes in which the string should reside.
245        let string_slice = &self.buffer[new_buffer_index..(end - 1)];
246
247        // Advance the index to the character after the string.
248        new_buffer_index = end - 1usize;
249
250        // If we've found a valid string, then the data may be valid and we allow the closure to set the result ok data.
251        if let Ok(parameter_value) = core::str::from_utf8(string_slice) {
252            (new_buffer_index, true, Some(parameter_value))
253        } else {
254            (new_buffer_index, false, None)
255        }
256    }
257
258    /// Finish parsing the command and get the results
259    pub fn finish(self) -> Result<D, ParseError> {
260        if self.data_valid {
261            Ok(self.data)
262        } else {
263            Err(ParseError(self.buffer_index))
264        }
265    }
266}
267
268impl<'a, D: TupleConcat<i32>> CommandParser<'a, D> {
269    /// Tries reading an int parameter
270    pub fn expect_int_parameter(self) -> CommandParser<'a, D::Out> {
271        // If we're already not valid, then quit
272        if !self.data_valid {
273            return CommandParser {
274                buffer: self.buffer,
275                buffer_index: self.buffer_index,
276                data_valid: self.data_valid,
277                data: self.data.tup_cat(0),
278            };
279        }
280
281        let (buffer_index, data_valid, data) = self.parse_int_parameter();
282        if let Some(parameter_value) = data {
283            return CommandParser {
284                buffer: self.buffer,
285                buffer_index,
286                data_valid,
287                data: self.data.tup_cat(parameter_value),
288            }
289            .trim_space();
290        } else {
291            return CommandParser {
292                buffer: self.buffer,
293                buffer_index,
294                data_valid: false,
295                data: self.data.tup_cat(0),
296            }
297            .trim_space();
298        }
299    }
300}
301
302impl<'a, D: TupleConcat<&'a str>> CommandParser<'a, D> {
303    /// Tries reading a string parameter
304    pub fn expect_string_parameter(self) -> CommandParser<'a, D::Out> {
305        // If we're already not valid, then quit
306        if !self.data_valid {
307            return CommandParser {
308                buffer: self.buffer,
309                buffer_index: self.buffer_index,
310                data_valid: self.data_valid,
311                data: self.data.tup_cat(""),
312            };
313        }
314
315        let (buffer_index, data_valid, data) = self.parse_string_parameter();
316        if let Some(parameter_value) = data {
317            return CommandParser {
318                buffer: self.buffer,
319                buffer_index,
320                data_valid,
321                data: self.data.tup_cat(parameter_value),
322            }
323            .trim_space();
324        } else {
325            return CommandParser {
326                buffer: self.buffer,
327                buffer_index,
328                data_valid: false,
329                data: self.data.tup_cat(""),
330            }
331            .trim_space();
332        }
333    }
334
335    /// Tries reading a non-parameter, non-quoted string
336    pub fn expect_raw_string(self) -> CommandParser<'a, D::Out> {
337        // If we're already not valid, then quit
338        if !self.data_valid {
339            return CommandParser {
340                buffer: self.buffer,
341                buffer_index: self.buffer_index,
342                data_valid: self.data_valid,
343                data: self.data.tup_cat(""),
344            };
345        }
346
347        let (buffer_index, data_valid, data) = self.parse_raw_string_parameter();
348        if let Some(parameter_value) = data {
349            return CommandParser {
350                buffer: self.buffer,
351                buffer_index,
352                data_valid,
353                data: self.data.tup_cat(parameter_value),
354            }
355            .trim_space();
356        } else {
357            return CommandParser {
358                buffer: self.buffer,
359                buffer_index,
360                data_valid: false,
361                data: self.data.tup_cat(""),
362            }
363            .trim_space();
364        }
365    }
366}
367
368//
369// Optional parameters
370//
371
372impl<'a, D: TupleConcat<Option<i32>>> CommandParser<'a, D> {
373    /// Tries reading an int parameter
374    pub fn expect_optional_int_parameter(self) -> CommandParser<'a, D::Out> {
375        // If we're already not valid, then quit
376        if !self.data_valid {
377            return CommandParser {
378                buffer: self.buffer,
379                buffer_index: self.buffer_index,
380                data_valid: self.data_valid,
381                data: self.data.tup_cat(None),
382            };
383        }
384
385        let (buffer_index, data_valid, data) = self.parse_int_parameter();
386        return CommandParser {
387            buffer: self.buffer,
388            buffer_index,
389            data_valid,
390            data: self.data.tup_cat(data),
391        }
392        .trim_space();
393    }
394}
395
396impl<'a, D: TupleConcat<Option<&'a str>>> CommandParser<'a, D> {
397    /// Tries reading a string parameter
398    pub fn expect_optional_string_parameter(self) -> CommandParser<'a, D::Out> {
399        // If we're already not valid, then quit
400        if !self.data_valid {
401            return CommandParser {
402                buffer: self.buffer,
403                buffer_index: self.buffer_index,
404                data_valid: self.data_valid,
405                data: self.data.tup_cat(None),
406            };
407        }
408
409        let (buffer_index, data_valid, data) = self.parse_string_parameter();
410        return CommandParser {
411            buffer: self.buffer,
412            buffer_index,
413            data_valid,
414            data: self.data.tup_cat(data),
415        }
416        .trim_space();
417    }
418
419    /// Tries reading a non-parameter, non-quoted string
420    pub fn expect_optional_raw_string(self) -> CommandParser<'a, D::Out> {
421        // If we're already not valid, then quit
422        if !self.data_valid {
423            return CommandParser {
424                buffer: self.buffer,
425                buffer_index: self.buffer_index,
426                data_valid: self.data_valid,
427                data: self.data.tup_cat(None),
428            };
429        }
430
431        let (buffer_index, data_valid, data) = self.parse_raw_string_parameter();
432        return CommandParser {
433            buffer: self.buffer,
434            buffer_index,
435            data_valid,
436            data: self.data.tup_cat(data),
437        }
438        .trim_space();
439    }
440}
441
442/// Error type for parsing
443///
444/// The number is the index of up to where it was correctly parsed
445#[derive(Debug, Clone, PartialEq)]
446#[cfg_attr(feature = "defmt", derive(defmt::Format))]
447pub struct ParseError(usize);
448
449#[cfg(test)]
450mod tests {
451    use super::*;
452
453    #[test]
454    fn test_ok() {
455        let (x, y, z) = CommandParser::parse(b"+SYSGPIOREAD:654,\"true\",-65154\r\nOK\r\n")
456            .expect_identifier(b"+SYSGPIOREAD:")
457            .expect_int_parameter()
458            .expect_string_parameter()
459            .expect_int_parameter()
460            .expect_identifier(b"\r\nOK\r\n")
461            .finish()
462            .unwrap();
463
464        assert_eq!(x, 654);
465        assert_eq!(y, "true");
466        assert_eq!(z, -65154);
467    }
468
469    #[test]
470    fn test_positive_int_param() {
471        let (x,) = CommandParser::parse(b"OK+RP:+20dBm\r\n")
472            .expect_identifier(b"OK+RP:")
473            .expect_int_parameter()
474            .expect_identifier(b"dBm\r\n")
475            .finish()
476            .unwrap();
477
478        assert_eq!(x, 20);
479    }
480
481    #[test]
482    fn test_whitespace() {
483        let (x, y, z) = CommandParser::parse(b"+SYSGPIOREAD: 654, \"true\", -65154 \r\nOK\r\n")
484            .expect_identifier(b"+SYSGPIOREAD:")
485            .expect_int_parameter()
486            .expect_string_parameter()
487            .expect_int_parameter()
488            .expect_identifier(b"\r\nOK\r\n")
489            .finish()
490            .unwrap();
491
492        assert_eq!(x, 654);
493        assert_eq!(y, "true");
494        assert_eq!(z, -65154);
495    }
496
497    #[test]
498    fn string_param_at_end() {
499        let (x, y) = CommandParser::parse(br#"+SYSGPIOREAD: 42, "param at end""#)
500            .expect_identifier(b"+SYSGPIOREAD:")
501            .expect_int_parameter()
502            .expect_string_parameter()
503            .finish()
504            .unwrap();
505
506        assert_eq!(x, 42);
507        assert_eq!(y, "param at end");
508    }
509
510    #[test]
511    fn test_optional_int_parameter_all_present() {
512        let (x, y, z) = CommandParser::parse(b"+SYSGPIOREAD:654,\"true\",-65154\r\nOK\r\n")
513            .expect_identifier(b"+SYSGPIOREAD:")
514            .expect_optional_int_parameter()
515            .expect_optional_string_parameter()
516            .expect_optional_int_parameter()
517            .expect_identifier(b"\r\nOK\r\n")
518            .finish()
519            .unwrap();
520
521        assert_eq!(x, Some(654));
522        assert_eq!(y, Some("true"));
523        assert_eq!(z, Some(-65154));
524    }
525
526    #[test]
527    fn test_optional_int_parameter_middle_not_present() {
528        let (x, y, z) = CommandParser::parse(b"+SYSGPIOREAD:,\"true\"\r\nOK\r\n")
529            .expect_identifier(b"+SYSGPIOREAD:")
530            .expect_optional_int_parameter()
531            .expect_optional_string_parameter()
532            .expect_optional_int_parameter()
533            .expect_identifier(b"\r\nOK\r\n")
534            .finish()
535            .unwrap();
536
537        assert_eq!(x, None);
538        assert_eq!(y, Some("true"));
539        assert_eq!(z, None);
540    }
541
542    #[test]
543    fn test_optional_int_parameter_end_not_present() {
544        let (x, y, z) = CommandParser::parse(b"+SYSGPIOREAD:654,\"true\",\r\nOK\r\n")
545            .expect_identifier(b"+SYSGPIOREAD:")
546            .expect_optional_int_parameter()
547            .expect_optional_string_parameter()
548            .expect_optional_int_parameter()
549            .expect_optional_identifier(b"\r\nOK\r\n")
550            .finish()
551            .unwrap();
552
553        assert_eq!(x, Some(654));
554        assert_eq!(y, Some("true"));
555        assert_eq!(z, None);
556    }
557
558    #[test]
559    fn test_optional_identifier() {
560        let r = CommandParser::parse(b"+SYSGPIOREAD:,\"true\"\r\nK\r\n")
561            .expect_identifier(b"+SYSGPIOREAD:")
562            .expect_optional_int_parameter()
563            .expect_optional_string_parameter()
564            .expect_optional_int_parameter()
565            .expect_optional_identifier(b"\r\nOK\r\n")
566            .finish();
567
568        assert_eq!(r, Err(ParseError(20)));
569
570        let (x, y, z) = CommandParser::parse(b"+SYSGPIOREAD:,\"true\"\r\nOK\r\n")
571            .expect_identifier(b"+SYSGPIOREAD:")
572            .expect_optional_int_parameter()
573            .expect_optional_string_parameter()
574            .expect_optional_int_parameter()
575            .expect_optional_identifier(b"\r\nOK\r\n")
576            .finish()
577            .unwrap();
578
579        assert_eq!(x, None);
580        assert_eq!(y, Some("true"));
581        assert_eq!(z, None);
582
583        let (x, y, z) = CommandParser::parse(b"+SYSGPIOREAD:,\"true\"")
584            .expect_identifier(b"+SYSGPIOREAD:")
585            .expect_optional_int_parameter()
586            .expect_optional_string_parameter()
587            .expect_optional_int_parameter()
588            .expect_optional_identifier(b"\r\nOK\r\n")
589            .finish()
590            .unwrap();
591
592        assert_eq!(x, None);
593        assert_eq!(y, Some("true"));
594        assert_eq!(z, None);
595    }
596}