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        let found_optional = self.buffer[self.buffer_index..]
91            .iter()
92            .zip(identifier)
93            .all(|(buffer, id)| *buffer == *id);
94
95        if found_optional {
96            // If we found the optional advance the index
97            // Advance the index
98            self.buffer_index += identifier.len();
99
100            self.trim_space()
101        } else {
102            // If we did not find the optional just keep the index
103            self
104        }
105    }
106
107    /// Moves the internal buffer index over the next bit of space characters, if any
108    fn trim_space(mut self) -> Self {
109        // If we're already not valid, then quit
110        if !self.data_valid {
111            return self;
112        }
113
114        while let Some(c) = self.buffer.get(self.buffer_index) {
115            if *c == b' ' {
116                self.buffer_index += 1;
117            } else {
118                break;
119            }
120        }
121
122        self
123    }
124
125    /// Moves the internal buffer index over the next byte which is not a whitespace. The white
126    /// space is defined in [core::primitive::u8::is_ascii_whitespace]
127    pub fn trim_whitespace(mut self) -> Self {
128        // If we're already not valid, then quit
129        if !self.data_valid {
130            return self;
131        }
132
133        let white_spaces = self.buffer[self.buffer_index..]
134            .iter()
135            .take_while(|c| c.is_ascii_whitespace())
136            .count();
137
138        self.buffer_index += white_spaces;
139
140        self
141    }
142
143    /// Finds the index of the character after the int parameter or the end of the data.
144    fn find_end_of_int_parameter(&self) -> usize {
145        self.buffer_index
146            + self
147                .buffer
148                .get(self.buffer_index..)
149                .map(|buffer| {
150                    buffer
151                        .iter()
152                        .take_while(|byte| {
153                            byte.is_ascii_digit() || **byte == b'-' || **byte == b'+'
154                        })
155                        .count()
156                })
157                .unwrap_or(self.buffer.len())
158    }
159
160    /// Finds the index of the character after the string parameter or the end of the data.
161    fn find_end_of_string_parameter(&self) -> usize {
162        let mut counted_quotes = 0;
163
164        self.buffer_index
165            + self
166                .buffer
167                .get(self.buffer_index..)
168                .map(|buffer| {
169                    buffer
170                        .iter()
171                        .take_while(|byte| {
172                            counted_quotes += (**byte == b'"') as u8;
173                            counted_quotes < 2
174                        })
175                        .count()
176                        + 1
177                })
178                .unwrap_or(self.buffer.len())
179    }
180
181    /// Finds the index of the control character after the non-quoted string or the end of the data.
182    fn find_end_of_raw_string(&self) -> usize {
183        self.buffer_index
184            + self
185                .buffer
186                .get(self.buffer_index..)
187                .map(|buffer| {
188                    buffer
189                        .iter()
190                        .take_while(|byte| !(**byte as char).is_ascii_control())
191                        .count()
192                        + 1
193                })
194                .unwrap_or(self.buffer.len())
195    }
196
197    /// Finds the index of the character after the raw string parameter (comma or end of data).
198    fn find_end_of_raw_string_parameter(&self) -> usize {
199        self.buffer_index
200            + self
201                .buffer
202                .get(self.buffer_index..)
203                .map(|buffer| buffer.iter().take_while(|byte| **byte != b',').count())
204                .unwrap_or(self.buffer.len())
205    }
206
207    fn parse_int_parameter(&self) -> (usize, bool, Option<i32>) {
208        let mut new_buffer_index = self.buffer_index;
209        // Get the end index of the current parameter.
210        let parameter_end = self.find_end_of_int_parameter();
211        // Get the bytes in which the int should reside.
212        let int_slice = match self.buffer.get(self.buffer_index..parameter_end) {
213            None => {
214                return (new_buffer_index, false, None);
215            }
216            Some(int_slice) => int_slice,
217        };
218        if int_slice.is_empty() {
219            // We probably hit the end of the buffer.
220            // The parameter is empty but as it is optional not invalid
221            // Advance the index to the character after the parameter separator (comma) if it's there.
222            new_buffer_index =
223                parameter_end + (self.buffer.get(parameter_end) == Some(&b',')) as usize;
224            return (new_buffer_index, true, None);
225        }
226
227        // Skip the leading '+'
228        let int_slice = if int_slice[0] == b'+' {
229            &int_slice[1..]
230        } else {
231            int_slice
232        };
233
234        // Parse the int
235        let parsed_int = crate::formatter::parse_int(int_slice);
236
237        // Advance the index to the character after the parameter separator (comma) if it's there.
238        new_buffer_index = parameter_end + (self.buffer.get(parameter_end) == Some(&b',')) as usize;
239        // If we've found an int, then the data may be valid and we allow the closure to set the result ok data.
240        if let Some(parameter_value) = parsed_int {
241            (new_buffer_index, true, Some(parameter_value))
242        } else {
243            (new_buffer_index, false, None)
244        }
245    }
246
247    fn parse_string_parameter(&self) -> (usize, bool, Option<&'a str>) {
248        let mut new_buffer_index = self.buffer_index;
249        // Get the end index of the current parameter.
250        let parameter_end = self.find_end_of_string_parameter();
251        if parameter_end > self.buffer.len() {
252            // We hit the end of the buffer.
253            // The parameter is empty but as it is optional not invalid
254            return (new_buffer_index, true, None);
255        }
256        // Get the bytes in which the string should reside.
257        let string_slice = &self.buffer[(new_buffer_index + 1)..(parameter_end - 1)];
258
259        let has_comma_after_parameter = if let Some(next_char) = self.buffer.get(parameter_end) {
260            *next_char == b','
261        } else {
262            false
263        };
264
265        // Advance the index to the character after the parameter separator.
266        new_buffer_index = parameter_end + has_comma_after_parameter as usize;
267        // If we've found a valid string, then the data may be valid and we allow the closure to set the result ok data.
268        if let Ok(parameter_value) = core::str::from_utf8(string_slice) {
269            (new_buffer_index, true, Some(parameter_value))
270        } else {
271            (new_buffer_index, false, None)
272        }
273    }
274
275    fn parse_raw_string(&self) -> (usize, bool, Option<&'a str>) {
276        let mut new_buffer_index = self.buffer_index;
277        // Get the end index of the current string.
278        let end = self.find_end_of_raw_string();
279        // Get the bytes in which the string should reside.
280        let string_slice = &self.buffer[new_buffer_index..(end - 1)];
281
282        // Advance the index to the character after the string.
283        new_buffer_index = end - 1usize;
284
285        // If we've found a valid string, then the data may be valid and we allow the closure to set the result ok data.
286        if let Ok(parameter_value) = core::str::from_utf8(string_slice) {
287            (new_buffer_index, true, Some(parameter_value))
288        } else {
289            (new_buffer_index, false, None)
290        }
291    }
292
293    fn parse_raw_string_parameter(&self) -> (usize, bool, Option<&'a str>) {
294        let mut new_buffer_index = self.buffer_index;
295        // Get the end index of the current parameter.
296        let parameter_end = self.find_end_of_raw_string_parameter();
297        // Get the bytes in which the string should reside.
298        let string_slice = &self.buffer[new_buffer_index..parameter_end];
299
300        if string_slice.is_empty() {
301            // We probably hit the end of the buffer.
302            // The parameter is empty but as it is optional not invalid
303            // Advance the index to the character after the parameter separator (comma) if it's there.
304            new_buffer_index =
305                parameter_end + (self.buffer.get(parameter_end) == Some(&b',')) as usize;
306            return (new_buffer_index, true, None);
307        }
308
309        let has_comma_after_parameter = if let Some(next_char) = self.buffer.get(parameter_end) {
310            *next_char == b','
311        } else {
312            false
313        };
314
315        // Advance the index to the character after the parameter separator.
316        new_buffer_index = parameter_end + has_comma_after_parameter as usize;
317        // If we've found a valid string, then the data may be valid and we allow the closure to set the result ok data.
318        if let Ok(parameter_value) = core::str::from_utf8(string_slice) {
319            (new_buffer_index, true, Some(parameter_value))
320        } else {
321            (new_buffer_index, false, None)
322        }
323    }
324
325    /// Finish parsing the command and get the results
326    pub fn finish(self) -> Result<D, ParseError> {
327        if self.data_valid {
328            Ok(self.data)
329        } else {
330            Err(ParseError(self.buffer_index))
331        }
332    }
333}
334
335impl<'a, D: TupleConcat<i32>> CommandParser<'a, D> {
336    /// Tries reading an int parameter
337    pub fn expect_int_parameter(self) -> CommandParser<'a, D::Out> {
338        // If we're already not valid, then quit
339        if !self.data_valid {
340            return CommandParser {
341                buffer: self.buffer,
342                buffer_index: self.buffer_index,
343                data_valid: self.data_valid,
344                data: self.data.tup_cat(0),
345            };
346        }
347
348        let (buffer_index, data_valid, data) = self.parse_int_parameter();
349        if let Some(parameter_value) = data {
350            CommandParser {
351                buffer: self.buffer,
352                buffer_index,
353                data_valid,
354                data: self.data.tup_cat(parameter_value),
355            }
356            .trim_space()
357        } else {
358            CommandParser {
359                buffer: self.buffer,
360                buffer_index,
361                data_valid: false,
362                data: self.data.tup_cat(0),
363            }
364            .trim_space()
365        }
366    }
367}
368
369impl<'a, D: TupleConcat<&'a str>> CommandParser<'a, D> {
370    /// Tries reading a string parameter
371    pub fn expect_string_parameter(self) -> CommandParser<'a, D::Out> {
372        // If we're already not valid, then quit
373        if !self.data_valid {
374            return CommandParser {
375                buffer: self.buffer,
376                buffer_index: self.buffer_index,
377                data_valid: self.data_valid,
378                data: self.data.tup_cat(""),
379            };
380        }
381
382        let (buffer_index, data_valid, data) = self.parse_string_parameter();
383        if let Some(parameter_value) = data {
384            CommandParser {
385                buffer: self.buffer,
386                buffer_index,
387                data_valid,
388                data: self.data.tup_cat(parameter_value),
389            }
390            .trim_space()
391        } else {
392            CommandParser {
393                buffer: self.buffer,
394                buffer_index,
395                data_valid: false,
396                data: self.data.tup_cat(""),
397            }
398            .trim_space()
399        }
400    }
401
402    /// Tries reading a non-parameter, non-quoted string
403    pub fn expect_raw_string(self) -> CommandParser<'a, D::Out> {
404        // If we're already not valid, then quit
405        if !self.data_valid {
406            return CommandParser {
407                buffer: self.buffer,
408                buffer_index: self.buffer_index,
409                data_valid: self.data_valid,
410                data: self.data.tup_cat(""),
411            };
412        }
413
414        let (buffer_index, data_valid, data) = self.parse_raw_string();
415        if let Some(parameter_value) = data {
416            CommandParser {
417                buffer: self.buffer,
418                buffer_index,
419                data_valid,
420                data: self.data.tup_cat(parameter_value),
421            }
422            .trim_space()
423        } else {
424            CommandParser {
425                buffer: self.buffer,
426                buffer_index,
427                data_valid: false,
428                data: self.data.tup_cat(""),
429            }
430            .trim_space()
431        }
432    }
433
434    /// Tries reading a raw string parameter (non-quoted string separated by commas)
435    pub fn expect_raw_string_parameter(self) -> CommandParser<'a, D::Out> {
436        // If we're already not valid, then quit
437        if !self.data_valid {
438            return CommandParser {
439                buffer: self.buffer,
440                buffer_index: self.buffer_index,
441                data_valid: self.data_valid,
442                data: self.data.tup_cat(""),
443            };
444        }
445
446        let (buffer_index, data_valid, data) = self.parse_raw_string_parameter();
447        if let Some(parameter_value) = data {
448            CommandParser {
449                buffer: self.buffer,
450                buffer_index,
451                data_valid,
452                data: self.data.tup_cat(parameter_value),
453            }
454            .trim_space()
455        } else {
456            CommandParser {
457                buffer: self.buffer,
458                buffer_index,
459                data_valid: false,
460                data: self.data.tup_cat(""),
461            }
462            .trim_space()
463        }
464    }
465}
466
467//
468// Optional parameters
469//
470
471impl<'a, D: TupleConcat<Option<i32>>> CommandParser<'a, D> {
472    /// Tries reading an int parameter
473    pub fn expect_optional_int_parameter(self) -> CommandParser<'a, D::Out> {
474        // If we're already not valid, then quit
475        if !self.data_valid {
476            return CommandParser {
477                buffer: self.buffer,
478                buffer_index: self.buffer_index,
479                data_valid: self.data_valid,
480                data: self.data.tup_cat(None),
481            };
482        }
483
484        let (buffer_index, data_valid, data) = self.parse_int_parameter();
485        CommandParser {
486            buffer: self.buffer,
487            buffer_index,
488            data_valid,
489            data: self.data.tup_cat(data),
490        }
491        .trim_space()
492    }
493}
494
495impl<'a, D: TupleConcat<Option<&'a str>>> CommandParser<'a, D> {
496    /// Tries reading a string parameter
497    pub fn expect_optional_string_parameter(self) -> CommandParser<'a, D::Out> {
498        // If we're already not valid, then quit
499        if !self.data_valid {
500            return CommandParser {
501                buffer: self.buffer,
502                buffer_index: self.buffer_index,
503                data_valid: self.data_valid,
504                data: self.data.tup_cat(None),
505            };
506        }
507
508        let (buffer_index, data_valid, data) = self.parse_string_parameter();
509        CommandParser {
510            buffer: self.buffer,
511            buffer_index,
512            data_valid,
513            data: self.data.tup_cat(data),
514        }
515        .trim_space()
516    }
517
518    /// Tries reading a non-parameter, non-quoted string
519    pub fn expect_optional_raw_string(self) -> CommandParser<'a, D::Out> {
520        // If we're already not valid, then quit
521        if !self.data_valid {
522            return CommandParser {
523                buffer: self.buffer,
524                buffer_index: self.buffer_index,
525                data_valid: self.data_valid,
526                data: self.data.tup_cat(None),
527            };
528        }
529
530        let (buffer_index, data_valid, data) = self.parse_raw_string();
531        CommandParser {
532            buffer: self.buffer,
533            buffer_index,
534            data_valid,
535            data: self.data.tup_cat(data),
536        }
537        .trim_space()
538    }
539
540    /// Tries reading an optional raw string parameter (non-quoted string separated by commas)
541    pub fn expect_optional_raw_string_parameter(self) -> CommandParser<'a, D::Out> {
542        // If we're already not valid, then quit
543        if !self.data_valid {
544            return CommandParser {
545                buffer: self.buffer,
546                buffer_index: self.buffer_index,
547                data_valid: self.data_valid,
548                data: self.data.tup_cat(None),
549            };
550        }
551
552        let (buffer_index, data_valid, data) = self.parse_raw_string_parameter();
553        CommandParser {
554            buffer: self.buffer,
555            buffer_index,
556            data_valid,
557            data: self.data.tup_cat(data),
558        }
559        .trim_space()
560    }
561}
562
563/// Error type for parsing
564///
565/// The number is the index of up to where it was correctly parsed
566#[derive(Debug, Clone, PartialEq)]
567#[cfg_attr(feature = "defmt", derive(defmt::Format))]
568pub struct ParseError(usize);
569
570#[cfg(test)]
571mod tests {
572    use super::*;
573
574    #[test]
575    fn test_ok() {
576        let (x, y, z) = CommandParser::parse(b"+SYSGPIOREAD:654,\"true\",-65154\r\nOK\r\n")
577            .expect_identifier(b"+SYSGPIOREAD:")
578            .expect_int_parameter()
579            .expect_string_parameter()
580            .expect_int_parameter()
581            .expect_identifier(b"\r\nOK\r\n")
582            .finish()
583            .unwrap();
584
585        assert_eq!(x, 654);
586        assert_eq!(y, "true");
587        assert_eq!(z, -65154);
588    }
589
590    #[test]
591    fn test_positive_int_param() {
592        let (x,) = CommandParser::parse(b"OK+RP:+20dBm\r\n")
593            .expect_identifier(b"OK+RP:")
594            .expect_int_parameter()
595            .expect_identifier(b"dBm\r\n")
596            .finish()
597            .unwrap();
598
599        assert_eq!(x, 20);
600    }
601
602    #[test]
603    fn test_whitespace() {
604        let (x, y, z) = CommandParser::parse(b"+SYSGPIOREAD: 654, \"true\", -65154 \r\nOK\r\n")
605            .expect_identifier(b"+SYSGPIOREAD:")
606            .expect_int_parameter()
607            .expect_string_parameter()
608            .expect_int_parameter()
609            .expect_identifier(b"\r\nOK\r\n")
610            .finish()
611            .unwrap();
612
613        assert_eq!(x, 654);
614        assert_eq!(y, "true");
615        assert_eq!(z, -65154);
616    }
617
618    #[test]
619    fn string_param_at_end() {
620        let (x, y) = CommandParser::parse(br#"+SYSGPIOREAD: 42, "param at end""#)
621            .expect_identifier(b"+SYSGPIOREAD:")
622            .expect_int_parameter()
623            .expect_string_parameter()
624            .finish()
625            .unwrap();
626
627        assert_eq!(x, 42);
628        assert_eq!(y, "param at end");
629    }
630
631    #[test]
632    fn test_optional_int_parameter_all_present() {
633        let (x, y, z) = CommandParser::parse(b"+SYSGPIOREAD:654,\"true\",-65154\r\nOK\r\n")
634            .expect_identifier(b"+SYSGPIOREAD:")
635            .expect_optional_int_parameter()
636            .expect_optional_string_parameter()
637            .expect_optional_int_parameter()
638            .expect_identifier(b"\r\nOK\r\n")
639            .finish()
640            .unwrap();
641
642        assert_eq!(x, Some(654));
643        assert_eq!(y, Some("true"));
644        assert_eq!(z, Some(-65154));
645    }
646
647    #[test]
648    fn test_optional_int_parameter_middle_not_present() {
649        let (x, y, z) = CommandParser::parse(b"+SYSGPIOREAD:,\"true\"\r\nOK\r\n")
650            .expect_identifier(b"+SYSGPIOREAD:")
651            .expect_optional_int_parameter()
652            .expect_optional_string_parameter()
653            .expect_optional_int_parameter()
654            .expect_identifier(b"\r\nOK\r\n")
655            .finish()
656            .unwrap();
657
658        assert_eq!(x, None);
659        assert_eq!(y, Some("true"));
660        assert_eq!(z, None);
661    }
662
663    #[test]
664    fn test_optional_int_parameter_end_not_present() {
665        let (x, y, z) = CommandParser::parse(b"+SYSGPIOREAD:654,\"true\",\r\nOK\r\n")
666            .expect_identifier(b"+SYSGPIOREAD:")
667            .expect_optional_int_parameter()
668            .expect_optional_string_parameter()
669            .expect_optional_int_parameter()
670            .expect_optional_identifier(b"\r\nOK\r\n")
671            .finish()
672            .unwrap();
673
674        assert_eq!(x, Some(654));
675        assert_eq!(y, Some("true"));
676        assert_eq!(z, None);
677    }
678
679    #[test]
680    fn test_optional_identifier() {
681        let r = CommandParser::parse(b"+SYSGPIOREAD:,\"true\"\r\nK\r\n")
682            .expect_identifier(b"+SYSGPIOREAD:")
683            .expect_optional_int_parameter()
684            .expect_optional_string_parameter()
685            .expect_optional_int_parameter()
686            .expect_optional_identifier(b"\r\nOK\r\n")
687            .finish();
688
689        assert_eq!(r, Err(ParseError(20)));
690
691        let (x, y, z) = CommandParser::parse(b"+SYSGPIOREAD:,\"true\"\r\nOK\r\n")
692            .expect_identifier(b"+SYSGPIOREAD:")
693            .expect_optional_int_parameter()
694            .expect_optional_string_parameter()
695            .expect_optional_int_parameter()
696            .expect_optional_identifier(b"\r\nOK\r\n")
697            .finish()
698            .unwrap();
699
700        assert_eq!(x, None);
701        assert_eq!(y, Some("true"));
702        assert_eq!(z, None);
703
704        let (x, y, z) = CommandParser::parse(b"+SYSGPIOREAD:,\"true\"")
705            .expect_identifier(b"+SYSGPIOREAD:")
706            .expect_optional_int_parameter()
707            .expect_optional_string_parameter()
708            .expect_optional_int_parameter()
709            .expect_optional_identifier(b"\r\nOK\r\n")
710            .finish()
711            .unwrap();
712
713        assert_eq!(x, None);
714        assert_eq!(y, Some("true"));
715        assert_eq!(z, None);
716    }
717
718    /// On this test we will check multiple possible variables for the optional identifier
719    #[test]
720    fn test_optional_identifier_multiple_cases() {
721        const OK_1: &str = "\r\nOK";
722        const OK_2: &str = "\r\nOK\r\n";
723        const OK_3: &str = "OK\r\n";
724        const OK_4: &str = "OK";
725
726        static TEST_CASES: [&str; 4] = [OK_1, OK_2, OK_3, OK_4];
727
728        for test_case in TEST_CASES {
729            let result = CommandParser::parse(test_case.as_bytes())
730                .expect_optional_identifier(b"\r")
731                .expect_optional_identifier(b"\n")
732                .expect_identifier(b"OK")
733                .expect_optional_identifier(b"\r")
734                .expect_optional_identifier(b"\n")
735                .finish();
736
737            assert_eq!(result, Ok(()), "Failed test case: {:?}", test_case);
738        }
739    }
740
741    #[test]
742    fn test_raw_string_parameter() {
743        let (x, y, raw, z) =
744            CommandParser::parse(b"+SYSGPIOREAD:654,\"true\",123ABC,-65154\r\nOK\r\n")
745                .expect_identifier(b"+SYSGPIOREAD:")
746                .expect_int_parameter()
747                .expect_string_parameter()
748                .expect_raw_string_parameter()
749                .expect_int_parameter()
750                .expect_identifier(b"\r\nOK\r\n")
751                .finish()
752                .unwrap();
753
754        assert_eq!(x, 654);
755        assert_eq!(y, "true");
756        assert_eq!(raw, "123ABC");
757        assert_eq!(z, -65154);
758    }
759
760    #[test]
761    fn raw_string_parameters_includes_non_ascii_characters() {
762        let (x, y, raw, z) =
763            CommandParser::parse("+SYSGPIOREAD:654,\"true\",123àABC,-65154\r\nOK\r\n".as_bytes())
764                .expect_identifier(b"+SYSGPIOREAD:")
765                .expect_int_parameter()
766                .expect_string_parameter()
767                .expect_raw_string_parameter()
768                .expect_int_parameter()
769                .expect_identifier(b"\r\nOK\r\n")
770                .finish()
771                .unwrap();
772
773        assert_eq!(x, 654);
774        assert_eq!(y, "true");
775        assert_eq!(raw, "123àABC");
776        assert_eq!(z, -65154);
777    }
778
779    #[test]
780    fn test_trim_whitespaces() {
781        let (x, y, raw, z) = CommandParser::parse(
782            "\r\n +SYSGPIOREAD:654,\"true\",123àABC,-65154\r\nOK\r\n".as_bytes(),
783        )
784        .trim_whitespace()
785        .expect_identifier(b"+SYSGPIOREAD:")
786        .expect_int_parameter()
787        .expect_string_parameter()
788        .expect_raw_string_parameter()
789        .expect_int_parameter()
790        .trim_whitespace()
791        .expect_identifier(b"OK\r\n")
792        .finish()
793        .unwrap();
794
795        assert_eq!(x, 654);
796        assert_eq!(y, "true");
797        assert_eq!(raw, "123àABC");
798        assert_eq!(z, -65154);
799    }
800
801    #[test]
802    fn test_trim_whitespaces_no_whitespace() {
803        let (x, y, raw, z) =
804            CommandParser::parse("+SYSGPIOREAD:654,\"true\",123àABC,-65154\r\nOK\r\n".as_bytes())
805                .trim_whitespace()
806                .expect_identifier(b"+SYSGPIOREAD:")
807                .trim_whitespace()
808                .expect_int_parameter()
809                .trim_whitespace()
810                .expect_string_parameter()
811                .trim_whitespace()
812                .expect_raw_string_parameter()
813                .trim_whitespace()
814                .expect_int_parameter()
815                .expect_identifier(b"\r\nOK\r\n")
816                .trim_whitespace()
817                .finish()
818                .unwrap();
819
820        assert_eq!(x, 654);
821        assert_eq!(y, "true");
822        assert_eq!(raw, "123àABC");
823        assert_eq!(z, -65154);
824    }
825}