1use crate::tuple_concat::TupleConcat;
4
5#[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 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 pub fn expect_identifier(mut self, identifier: &[u8]) -> Self {
51 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 self.data_valid = self.buffer[self.buffer_index..]
63 .iter()
64 .zip(identifier)
65 .all(|(buffer, id)| *buffer == *id);
66 self.buffer_index += identifier.len();
68
69 self.trim_space()
70 }
71
72 pub fn expect_optional_identifier(mut self, identifier: &[u8]) -> Self {
74 if !self.data_valid {
76 return self;
77 }
78
79 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 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 self.buffer_index += identifier.len();
99
100 self.trim_space()
101 } else {
102 self
104 }
105 }
106
107 fn trim_space(mut self) -> Self {
109 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 pub fn trim_whitespace(mut self) -> Self {
128 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 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 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 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 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 let parameter_end = self.find_end_of_int_parameter();
211 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 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 let int_slice = if int_slice[0] == b'+' {
229 &int_slice[1..]
230 } else {
231 int_slice
232 };
233
234 let parsed_int = crate::formatter::parse_int(int_slice);
236
237 new_buffer_index = parameter_end + (self.buffer.get(parameter_end) == Some(&b',')) as usize;
239 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 let parameter_end = self.find_end_of_string_parameter();
251 if parameter_end > self.buffer.len() {
252 return (new_buffer_index, true, None);
255 }
256 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 new_buffer_index = parameter_end + has_comma_after_parameter as usize;
267 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 let end = self.find_end_of_raw_string();
279 let string_slice = &self.buffer[new_buffer_index..(end - 1)];
281
282 new_buffer_index = end - 1usize;
284
285 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 let parameter_end = self.find_end_of_raw_string_parameter();
297 let string_slice = &self.buffer[new_buffer_index..parameter_end];
299
300 if string_slice.is_empty() {
301 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 new_buffer_index = parameter_end + has_comma_after_parameter as usize;
317 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 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 pub fn expect_int_parameter(self) -> CommandParser<'a, D::Out> {
338 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 pub fn expect_string_parameter(self) -> CommandParser<'a, D::Out> {
372 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 pub fn expect_raw_string(self) -> CommandParser<'a, D::Out> {
404 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 pub fn expect_raw_string_parameter(self) -> CommandParser<'a, D::Out> {
436 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
467impl<'a, D: TupleConcat<Option<i32>>> CommandParser<'a, D> {
472 pub fn expect_optional_int_parameter(self) -> CommandParser<'a, D::Out> {
474 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 pub fn expect_optional_string_parameter(self) -> CommandParser<'a, D::Out> {
498 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 pub fn expect_optional_raw_string(self) -> CommandParser<'a, D::Out> {
520 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 pub fn expect_optional_raw_string_parameter(self) -> CommandParser<'a, D::Out> {
542 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#[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 #[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}