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 parse_int_parameter(&self) -> (usize, bool, Option<i32>) {
173 let mut new_buffer_index = self.buffer_index;
174 let parameter_end = self.find_end_of_int_parameter();
176 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 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 let int_slice = if int_slice[0] == b'+' {
194 &int_slice[1..]
195 } else {
196 int_slice
197 };
198
199 let parsed_int = crate::formatter::parse_int(int_slice);
201
202 new_buffer_index = parameter_end + (self.buffer.get(parameter_end) == Some(&b',')) as usize;
204 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 let parameter_end = self.find_end_of_string_parameter();
216 if parameter_end > self.buffer.len() {
217 return (new_buffer_index, true, None);
220 }
221 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 new_buffer_index = parameter_end + has_comma_after_parameter as usize;
232 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 let end = self.find_end_of_raw_string();
244 let string_slice = &self.buffer[new_buffer_index..(end - 1)];
246
247 new_buffer_index = end - 1usize;
249
250 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 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 pub fn expect_int_parameter(self) -> CommandParser<'a, D::Out> {
271 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 pub fn expect_string_parameter(self) -> CommandParser<'a, D::Out> {
305 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 pub fn expect_raw_string(self) -> CommandParser<'a, D::Out> {
337 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
368impl<'a, D: TupleConcat<Option<i32>>> CommandParser<'a, D> {
373 pub fn expect_optional_int_parameter(self) -> CommandParser<'a, D::Out> {
375 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 pub fn expect_optional_string_parameter(self) -> CommandParser<'a, D::Out> {
399 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 pub fn expect_optional_raw_string(self) -> CommandParser<'a, D::Out> {
421 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#[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}