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 self.data_valid = self.buffer[self.buffer_index..]
91 .iter()
92 .zip(identifier)
93 .all(|(buffer, id)| *buffer == *id);
94 self.buffer_index += identifier.len();
96
97 self.trim_space()
98 }
99
100 fn trim_space(mut self) -> Self {
102 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 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 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 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 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 let parameter_end = self.find_end_of_int_parameter();
186 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 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 let int_slice = if int_slice[0] == b'+' {
204 &int_slice[1..]
205 } else {
206 int_slice
207 };
208
209 let parsed_int = crate::formatter::parse_int(int_slice);
211
212 new_buffer_index = parameter_end + (self.buffer.get(parameter_end) == Some(&b',')) as usize;
214 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 let parameter_end = self.find_end_of_string_parameter();
226 if parameter_end > self.buffer.len() {
227 return (new_buffer_index, true, None);
230 }
231 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 new_buffer_index = parameter_end + has_comma_after_parameter as usize;
242 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 let end = self.find_end_of_raw_string();
254 let string_slice = &self.buffer[new_buffer_index..(end - 1)];
256
257 new_buffer_index = end - 1usize;
259
260 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 let parameter_end = self.find_end_of_raw_string_parameter();
272 let string_slice = &self.buffer[new_buffer_index..parameter_end];
274
275 if string_slice.is_empty() {
276 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 new_buffer_index = parameter_end + has_comma_after_parameter as usize;
292 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 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 pub fn expect_int_parameter(self) -> CommandParser<'a, D::Out> {
313 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 pub fn expect_string_parameter(self) -> CommandParser<'a, D::Out> {
347 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 pub fn expect_raw_string(self) -> CommandParser<'a, D::Out> {
379 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 pub fn expect_raw_string_parameter(self) -> CommandParser<'a, D::Out> {
411 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
442impl<'a, D: TupleConcat<Option<i32>>> CommandParser<'a, D> {
447 pub fn expect_optional_int_parameter(self) -> CommandParser<'a, D::Out> {
449 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 pub fn expect_optional_string_parameter(self) -> CommandParser<'a, D::Out> {
473 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 pub fn expect_optional_raw_string(self) -> CommandParser<'a, D::Out> {
495 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 pub fn expect_optional_raw_string_parameter(self) -> CommandParser<'a, D::Out> {
517 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#[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}