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    /// Finds the index of the character after the raw string parameter (comma or end of data).
173    fn find_end_of_raw_string_parameter(&self) -> usize {
174        self.buffer_index
175            + self
176                .buffer
177                .get(self.buffer_index..)
178                .map(|buffer| buffer.iter().take_while(|byte| **byte != b',').count())
179                .unwrap_or(self.buffer.len())
180    }
181
182    fn parse_int_parameter(&self) -> (usize, bool, Option<i32>) {
183        let mut new_buffer_index = self.buffer_index;
184        // Get the end index of the current parameter.
185        let parameter_end = self.find_end_of_int_parameter();
186        // Get the bytes in which the int should reside.
187        let int_slice = match self.buffer.get(self.buffer_index..parameter_end) {
188            None => {
189                return (new_buffer_index, false, None);
190            }
191            Some(int_slice) => int_slice,
192        };
193        if int_slice.is_empty() {
194            // We probably hit the end of the buffer.
195            // The parameter is empty but as it is optional not invalid
196            // Advance the index to the character after the parameter separator (comma) if it's there.
197            new_buffer_index =
198                parameter_end + (self.buffer.get(parameter_end) == Some(&b',')) as usize;
199            return (new_buffer_index, true, None);
200        }
201
202        // Skip the leading '+'
203        let int_slice = if int_slice[0] == b'+' {
204            &int_slice[1..]
205        } else {
206            int_slice
207        };
208
209        // Parse the int
210        let parsed_int = crate::formatter::parse_int(int_slice);
211
212        // Advance the index to the character after the parameter separator (comma) if it's there.
213        new_buffer_index = parameter_end + (self.buffer.get(parameter_end) == Some(&b',')) as usize;
214        // If we've found an int, then the data may be valid and we allow the closure to set the result ok data.
215        if let Some(parameter_value) = parsed_int {
216            (new_buffer_index, true, Some(parameter_value))
217        } else {
218            (new_buffer_index, false, None)
219        }
220    }
221
222    fn parse_string_parameter(&self) -> (usize, bool, Option<&'a str>) {
223        let mut new_buffer_index = self.buffer_index;
224        // Get the end index of the current parameter.
225        let parameter_end = self.find_end_of_string_parameter();
226        if parameter_end > self.buffer.len() {
227            // We hit the end of the buffer.
228            // The parameter is empty but as it is optional not invalid
229            return (new_buffer_index, true, None);
230        }
231        // Get the bytes in which the string should reside.
232        let string_slice = &self.buffer[(new_buffer_index + 1)..(parameter_end - 1)];
233
234        let has_comma_after_parameter = if let Some(next_char) = self.buffer.get(parameter_end) {
235            *next_char == b','
236        } else {
237            false
238        };
239
240        // Advance the index to the character after the parameter separator.
241        new_buffer_index = parameter_end + has_comma_after_parameter as usize;
242        // If we've found a valid string, then the data may be valid and we allow the closure to set the result ok data.
243        if let Ok(parameter_value) = core::str::from_utf8(string_slice) {
244            (new_buffer_index, true, Some(parameter_value))
245        } else {
246            (new_buffer_index, false, None)
247        }
248    }
249
250    fn parse_raw_string(&self) -> (usize, bool, Option<&'a str>) {
251        let mut new_buffer_index = self.buffer_index;
252        // Get the end index of the current string.
253        let end = self.find_end_of_raw_string();
254        // Get the bytes in which the string should reside.
255        let string_slice = &self.buffer[new_buffer_index..(end - 1)];
256
257        // Advance the index to the character after the string.
258        new_buffer_index = end - 1usize;
259
260        // If we've found a valid string, then the data may be valid and we allow the closure to set the result ok data.
261        if let Ok(parameter_value) = core::str::from_utf8(string_slice) {
262            (new_buffer_index, true, Some(parameter_value))
263        } else {
264            (new_buffer_index, false, None)
265        }
266    }
267
268    fn parse_raw_string_parameter(&self) -> (usize, bool, Option<&'a str>) {
269        let mut new_buffer_index = self.buffer_index;
270        // Get the end index of the current parameter.
271        let parameter_end = self.find_end_of_raw_string_parameter();
272        // Get the bytes in which the string should reside.
273        let string_slice = &self.buffer[new_buffer_index..parameter_end];
274
275        if string_slice.is_empty() {
276            // We probably hit the end of the buffer.
277            // The parameter is empty but as it is optional not invalid
278            // Advance the index to the character after the parameter separator (comma) if it's there.
279            new_buffer_index =
280                parameter_end + (self.buffer.get(parameter_end) == Some(&b',')) as usize;
281            return (new_buffer_index, true, None);
282        }
283
284        let has_comma_after_parameter = if let Some(next_char) = self.buffer.get(parameter_end) {
285            *next_char == b','
286        } else {
287            false
288        };
289
290        // Advance the index to the character after the parameter separator.
291        new_buffer_index = parameter_end + has_comma_after_parameter as usize;
292        // If we've found a valid string, then the data may be valid and we allow the closure to set the result ok data.
293        if let Ok(parameter_value) = core::str::from_utf8(string_slice) {
294            (new_buffer_index, true, Some(parameter_value))
295        } else {
296            (new_buffer_index, false, None)
297        }
298    }
299
300    /// Finish parsing the command and get the results
301    pub fn finish(self) -> Result<D, ParseError> {
302        if self.data_valid {
303            Ok(self.data)
304        } else {
305            Err(ParseError(self.buffer_index))
306        }
307    }
308}
309
310impl<'a, D: TupleConcat<i32>> CommandParser<'a, D> {
311    /// Tries reading an int parameter
312    pub fn expect_int_parameter(self) -> CommandParser<'a, D::Out> {
313        // If we're already not valid, then quit
314        if !self.data_valid {
315            return CommandParser {
316                buffer: self.buffer,
317                buffer_index: self.buffer_index,
318                data_valid: self.data_valid,
319                data: self.data.tup_cat(0),
320            };
321        }
322
323        let (buffer_index, data_valid, data) = self.parse_int_parameter();
324        if let Some(parameter_value) = data {
325            CommandParser {
326                buffer: self.buffer,
327                buffer_index,
328                data_valid,
329                data: self.data.tup_cat(parameter_value),
330            }
331            .trim_space()
332        } else {
333            CommandParser {
334                buffer: self.buffer,
335                buffer_index,
336                data_valid: false,
337                data: self.data.tup_cat(0),
338            }
339            .trim_space()
340        }
341    }
342}
343
344impl<'a, D: TupleConcat<&'a str>> CommandParser<'a, D> {
345    /// Tries reading a string parameter
346    pub fn expect_string_parameter(self) -> CommandParser<'a, D::Out> {
347        // If we're already not valid, then quit
348        if !self.data_valid {
349            return CommandParser {
350                buffer: self.buffer,
351                buffer_index: self.buffer_index,
352                data_valid: self.data_valid,
353                data: self.data.tup_cat(""),
354            };
355        }
356
357        let (buffer_index, data_valid, data) = self.parse_string_parameter();
358        if let Some(parameter_value) = data {
359            CommandParser {
360                buffer: self.buffer,
361                buffer_index,
362                data_valid,
363                data: self.data.tup_cat(parameter_value),
364            }
365            .trim_space()
366        } else {
367            CommandParser {
368                buffer: self.buffer,
369                buffer_index,
370                data_valid: false,
371                data: self.data.tup_cat(""),
372            }
373            .trim_space()
374        }
375    }
376
377    /// Tries reading a non-parameter, non-quoted string
378    pub fn expect_raw_string(self) -> CommandParser<'a, D::Out> {
379        // If we're already not valid, then quit
380        if !self.data_valid {
381            return CommandParser {
382                buffer: self.buffer,
383                buffer_index: self.buffer_index,
384                data_valid: self.data_valid,
385                data: self.data.tup_cat(""),
386            };
387        }
388
389        let (buffer_index, data_valid, data) = self.parse_raw_string();
390        if let Some(parameter_value) = data {
391            CommandParser {
392                buffer: self.buffer,
393                buffer_index,
394                data_valid,
395                data: self.data.tup_cat(parameter_value),
396            }
397            .trim_space()
398        } else {
399            CommandParser {
400                buffer: self.buffer,
401                buffer_index,
402                data_valid: false,
403                data: self.data.tup_cat(""),
404            }
405            .trim_space()
406        }
407    }
408
409    /// Tries reading a raw string parameter (non-quoted string separated by commas)
410    pub fn expect_raw_string_parameter(self) -> CommandParser<'a, D::Out> {
411        // If we're already not valid, then quit
412        if !self.data_valid {
413            return CommandParser {
414                buffer: self.buffer,
415                buffer_index: self.buffer_index,
416                data_valid: self.data_valid,
417                data: self.data.tup_cat(""),
418            };
419        }
420
421        let (buffer_index, data_valid, data) = self.parse_raw_string_parameter();
422        if let Some(parameter_value) = data {
423            CommandParser {
424                buffer: self.buffer,
425                buffer_index,
426                data_valid,
427                data: self.data.tup_cat(parameter_value),
428            }
429            .trim_space()
430        } else {
431            CommandParser {
432                buffer: self.buffer,
433                buffer_index,
434                data_valid: false,
435                data: self.data.tup_cat(""),
436            }
437            .trim_space()
438        }
439    }
440}
441
442//
443// Optional parameters
444//
445
446impl<'a, D: TupleConcat<Option<i32>>> CommandParser<'a, D> {
447    /// Tries reading an int parameter
448    pub fn expect_optional_int_parameter(self) -> CommandParser<'a, D::Out> {
449        // If we're already not valid, then quit
450        if !self.data_valid {
451            return CommandParser {
452                buffer: self.buffer,
453                buffer_index: self.buffer_index,
454                data_valid: self.data_valid,
455                data: self.data.tup_cat(None),
456            };
457        }
458
459        let (buffer_index, data_valid, data) = self.parse_int_parameter();
460        CommandParser {
461            buffer: self.buffer,
462            buffer_index,
463            data_valid,
464            data: self.data.tup_cat(data),
465        }
466        .trim_space()
467    }
468}
469
470impl<'a, D: TupleConcat<Option<&'a str>>> CommandParser<'a, D> {
471    /// Tries reading a string parameter
472    pub fn expect_optional_string_parameter(self) -> CommandParser<'a, D::Out> {
473        // If we're already not valid, then quit
474        if !self.data_valid {
475            return CommandParser {
476                buffer: self.buffer,
477                buffer_index: self.buffer_index,
478                data_valid: self.data_valid,
479                data: self.data.tup_cat(None),
480            };
481        }
482
483        let (buffer_index, data_valid, data) = self.parse_string_parameter();
484        CommandParser {
485            buffer: self.buffer,
486            buffer_index,
487            data_valid,
488            data: self.data.tup_cat(data),
489        }
490        .trim_space()
491    }
492
493    /// Tries reading a non-parameter, non-quoted string
494    pub fn expect_optional_raw_string(self) -> CommandParser<'a, D::Out> {
495        // If we're already not valid, then quit
496        if !self.data_valid {
497            return CommandParser {
498                buffer: self.buffer,
499                buffer_index: self.buffer_index,
500                data_valid: self.data_valid,
501                data: self.data.tup_cat(None),
502            };
503        }
504
505        let (buffer_index, data_valid, data) = self.parse_raw_string();
506        CommandParser {
507            buffer: self.buffer,
508            buffer_index,
509            data_valid,
510            data: self.data.tup_cat(data),
511        }
512        .trim_space()
513    }
514
515    /// Tries reading an optional raw string parameter (non-quoted string separated by commas)
516    pub fn expect_optional_raw_string_parameter(self) -> CommandParser<'a, D::Out> {
517        // If we're already not valid, then quit
518        if !self.data_valid {
519            return CommandParser {
520                buffer: self.buffer,
521                buffer_index: self.buffer_index,
522                data_valid: self.data_valid,
523                data: self.data.tup_cat(None),
524            };
525        }
526
527        let (buffer_index, data_valid, data) = self.parse_raw_string_parameter();
528        CommandParser {
529            buffer: self.buffer,
530            buffer_index,
531            data_valid,
532            data: self.data.tup_cat(data),
533        }
534        .trim_space()
535    }
536}
537
538/// Error type for parsing
539///
540/// The number is the index of up to where it was correctly parsed
541#[derive(Debug, Clone, PartialEq)]
542#[cfg_attr(feature = "defmt", derive(defmt::Format))]
543pub struct ParseError(usize);
544
545#[cfg(test)]
546mod tests {
547    use super::*;
548
549    #[test]
550    fn test_ok() {
551        let (x, y, z) = CommandParser::parse(b"+SYSGPIOREAD:654,\"true\",-65154\r\nOK\r\n")
552            .expect_identifier(b"+SYSGPIOREAD:")
553            .expect_int_parameter()
554            .expect_string_parameter()
555            .expect_int_parameter()
556            .expect_identifier(b"\r\nOK\r\n")
557            .finish()
558            .unwrap();
559
560        assert_eq!(x, 654);
561        assert_eq!(y, "true");
562        assert_eq!(z, -65154);
563    }
564
565    #[test]
566    fn test_positive_int_param() {
567        let (x,) = CommandParser::parse(b"OK+RP:+20dBm\r\n")
568            .expect_identifier(b"OK+RP:")
569            .expect_int_parameter()
570            .expect_identifier(b"dBm\r\n")
571            .finish()
572            .unwrap();
573
574        assert_eq!(x, 20);
575    }
576
577    #[test]
578    fn test_whitespace() {
579        let (x, y, z) = CommandParser::parse(b"+SYSGPIOREAD: 654, \"true\", -65154 \r\nOK\r\n")
580            .expect_identifier(b"+SYSGPIOREAD:")
581            .expect_int_parameter()
582            .expect_string_parameter()
583            .expect_int_parameter()
584            .expect_identifier(b"\r\nOK\r\n")
585            .finish()
586            .unwrap();
587
588        assert_eq!(x, 654);
589        assert_eq!(y, "true");
590        assert_eq!(z, -65154);
591    }
592
593    #[test]
594    fn string_param_at_end() {
595        let (x, y) = CommandParser::parse(br#"+SYSGPIOREAD: 42, "param at end""#)
596            .expect_identifier(b"+SYSGPIOREAD:")
597            .expect_int_parameter()
598            .expect_string_parameter()
599            .finish()
600            .unwrap();
601
602        assert_eq!(x, 42);
603        assert_eq!(y, "param at end");
604    }
605
606    #[test]
607    fn test_optional_int_parameter_all_present() {
608        let (x, y, z) = CommandParser::parse(b"+SYSGPIOREAD:654,\"true\",-65154\r\nOK\r\n")
609            .expect_identifier(b"+SYSGPIOREAD:")
610            .expect_optional_int_parameter()
611            .expect_optional_string_parameter()
612            .expect_optional_int_parameter()
613            .expect_identifier(b"\r\nOK\r\n")
614            .finish()
615            .unwrap();
616
617        assert_eq!(x, Some(654));
618        assert_eq!(y, Some("true"));
619        assert_eq!(z, Some(-65154));
620    }
621
622    #[test]
623    fn test_optional_int_parameter_middle_not_present() {
624        let (x, y, z) = CommandParser::parse(b"+SYSGPIOREAD:,\"true\"\r\nOK\r\n")
625            .expect_identifier(b"+SYSGPIOREAD:")
626            .expect_optional_int_parameter()
627            .expect_optional_string_parameter()
628            .expect_optional_int_parameter()
629            .expect_identifier(b"\r\nOK\r\n")
630            .finish()
631            .unwrap();
632
633        assert_eq!(x, None);
634        assert_eq!(y, Some("true"));
635        assert_eq!(z, None);
636    }
637
638    #[test]
639    fn test_optional_int_parameter_end_not_present() {
640        let (x, y, z) = CommandParser::parse(b"+SYSGPIOREAD:654,\"true\",\r\nOK\r\n")
641            .expect_identifier(b"+SYSGPIOREAD:")
642            .expect_optional_int_parameter()
643            .expect_optional_string_parameter()
644            .expect_optional_int_parameter()
645            .expect_optional_identifier(b"\r\nOK\r\n")
646            .finish()
647            .unwrap();
648
649        assert_eq!(x, Some(654));
650        assert_eq!(y, Some("true"));
651        assert_eq!(z, None);
652    }
653
654    #[test]
655    fn test_optional_identifier() {
656        let r = CommandParser::parse(b"+SYSGPIOREAD:,\"true\"\r\nK\r\n")
657            .expect_identifier(b"+SYSGPIOREAD:")
658            .expect_optional_int_parameter()
659            .expect_optional_string_parameter()
660            .expect_optional_int_parameter()
661            .expect_optional_identifier(b"\r\nOK\r\n")
662            .finish();
663
664        assert_eq!(r, Err(ParseError(20)));
665
666        let (x, y, z) = CommandParser::parse(b"+SYSGPIOREAD:,\"true\"\r\nOK\r\n")
667            .expect_identifier(b"+SYSGPIOREAD:")
668            .expect_optional_int_parameter()
669            .expect_optional_string_parameter()
670            .expect_optional_int_parameter()
671            .expect_optional_identifier(b"\r\nOK\r\n")
672            .finish()
673            .unwrap();
674
675        assert_eq!(x, None);
676        assert_eq!(y, Some("true"));
677        assert_eq!(z, None);
678
679        let (x, y, z) = CommandParser::parse(b"+SYSGPIOREAD:,\"true\"")
680            .expect_identifier(b"+SYSGPIOREAD:")
681            .expect_optional_int_parameter()
682            .expect_optional_string_parameter()
683            .expect_optional_int_parameter()
684            .expect_optional_identifier(b"\r\nOK\r\n")
685            .finish()
686            .unwrap();
687
688        assert_eq!(x, None);
689        assert_eq!(y, Some("true"));
690        assert_eq!(z, None);
691    }
692
693    #[test]
694    fn test_raw_string_parameter() {
695        let (x, y, raw, z) =
696            CommandParser::parse(b"+SYSGPIOREAD:654,\"true\",123ABC,-65154\r\nOK\r\n")
697                .expect_identifier(b"+SYSGPIOREAD:")
698                .expect_int_parameter()
699                .expect_string_parameter()
700                .expect_raw_string_parameter()
701                .expect_int_parameter()
702                .expect_identifier(b"\r\nOK\r\n")
703                .finish()
704                .unwrap();
705
706        assert_eq!(x, 654);
707        assert_eq!(y, "true");
708        assert_eq!(raw, "123ABC");
709        assert_eq!(z, -65154);
710    }
711
712    #[test]
713    fn raw_string_parameters_includes_non_ascii_characters() {
714        let (x, y, raw, z) =
715            CommandParser::parse("+SYSGPIOREAD:654,\"true\",123àABC,-65154\r\nOK\r\n".as_bytes())
716                .expect_identifier(b"+SYSGPIOREAD:")
717                .expect_int_parameter()
718                .expect_string_parameter()
719                .expect_raw_string_parameter()
720                .expect_int_parameter()
721                .expect_identifier(b"\r\nOK\r\n")
722                .finish()
723                .unwrap();
724
725        assert_eq!(x, 654);
726        assert_eq!(y, "true");
727        assert_eq!(raw, "123àABC");
728        assert_eq!(z, -65154);
729    }
730}