1use std::{collections::VecDeque, ops::Range};
46
47use imap_types::{
48 core::{LiteralMode, Tag},
49 secret::Secret,
50};
51
52use crate::decode::Decoder;
53
54#[derive(Clone, Debug)]
64pub struct Fragmentizer {
65 unparsed_buffer: VecDeque<u8>,
67 max_message_size: Option<u32>,
69 max_message_size_exceeded: bool,
71 message_poisoned: bool,
74 message_buffer: Vec<u8>,
77 parser: Option<Parser>,
80}
81
82impl Fragmentizer {
83 pub fn new(max_message_size: u32) -> Self {
87 Self {
88 unparsed_buffer: VecDeque::new(),
89 max_message_size: Some(max_message_size),
90 max_message_size_exceeded: false,
91 message_poisoned: false,
92 message_buffer: Vec::new(),
93 parser: Some(Parser::Line(LineParser::new(0))),
94 }
95 }
96
97 pub fn without_max_message_size() -> Self {
104 Self {
105 unparsed_buffer: VecDeque::new(),
106 max_message_size: None,
107 max_message_size_exceeded: false,
108 message_poisoned: false,
109 message_buffer: Vec::new(),
110 parser: Some(Parser::Line(LineParser::new(0))),
111 }
112 }
113
114 pub fn progress(&mut self) -> Option<FragmentInfo> {
121 let parser = match &mut self.parser {
122 Some(parser) => {
123 parser
125 }
126 None => {
127 self.max_message_size_exceeded = false;
129 self.message_poisoned = false;
130 self.message_buffer.clear();
131 self.parser.insert(Parser::Line(LineParser::new(0)))
132 }
133 };
134
135 let (parsed_byte_count, fragment) = match parser {
137 Parser::Line(parser) => parser.parse(&self.unparsed_buffer),
138 Parser::Literal(parser) => parser.parse(&self.unparsed_buffer),
139 };
140 self.dequeue_parsed_bytes(parsed_byte_count);
141
142 if let Some(fragment) = fragment {
143 self.parser = match fragment {
144 FragmentInfo::Line {
146 announcement: None, ..
147 } => None,
148 FragmentInfo::Line {
150 end,
151 announcement: Some(LiteralAnnouncement { length, .. }),
152 ..
153 } => Some(Parser::Literal(LiteralParser::new(end, length))),
154 FragmentInfo::Literal { end, .. } => Some(Parser::Line(LineParser::new(end))),
156 }
157 }
158
159 fragment
160 }
161
162 pub fn enqueue_bytes(&mut self, bytes: &[u8]) {
168 self.unparsed_buffer.extend(bytes);
169 }
170
171 pub fn fragment_bytes(&self, fragment_info: FragmentInfo) -> &[u8] {
173 let (start, end) = match fragment_info {
174 FragmentInfo::Line { start, end, .. } => (start, end),
175 FragmentInfo::Literal { start, end } => (start, end),
176 };
177 let start = start.min(self.message_buffer.len());
178 let end = end.min(self.message_buffer.len());
179 &self.message_buffer[start..end]
180 }
181
182 pub fn is_message_complete(&self) -> bool {
188 self.parser.is_none()
189 }
190
191 pub fn message_bytes(&self) -> &[u8] {
200 &self.message_buffer
201 }
202
203 pub fn is_max_message_size_exceeded(&self) -> bool {
205 self.max_message_size_exceeded
206 }
207
208 pub fn is_message_poisoned(&self) -> bool {
210 self.message_poisoned
211 }
212
213 pub fn skip_message(&mut self) {
221 self.max_message_size_exceeded = false;
222 self.message_poisoned = false;
223 self.message_buffer.clear();
224 self.parser = Some(Parser::Line(LineParser::new(0)));
225 }
226
227 pub fn poison_message(&mut self) {
235 self.message_poisoned = true;
236 }
237
238 pub fn decode_tag(&self) -> Option<Tag> {
245 parse_tag(&self.message_buffer)
246 }
247
248 pub fn decode_message<'a, C: Decoder>(
254 &'a self,
255 codec: &C,
256 ) -> Result<C::Message<'a>, DecodeMessageError<'a, C>> {
257 if self.max_message_size_exceeded {
258 return Err(DecodeMessageError::MessageTooLong {
259 initial: Secret::new(&self.message_buffer),
260 });
261 }
262
263 if self.message_poisoned {
264 return Err(DecodeMessageError::MessagePoisoned {
265 discarded: Secret::new(&self.message_buffer),
266 });
267 }
268
269 let (remainder, message) = match codec.decode(&self.message_buffer) {
270 Ok(res) => res,
271 Err(err) => return Err(DecodeMessageError::DecodingFailure(err)),
272 };
273
274 if !remainder.is_empty() {
275 return Err(DecodeMessageError::DecodingRemainder {
276 message,
277 remainder: Secret::new(remainder),
278 });
279 }
280
281 Ok(message)
282 }
283
284 fn dequeue_parsed_bytes(&mut self, parsed_byte_count: usize) {
285 let parsed_bytes = self.unparsed_buffer.drain(..parsed_byte_count);
287 let remaining_size = self
289 .max_message_size
290 .map(|size| size as usize - self.message_buffer.len());
291
292 match remaining_size {
294 Some(remaining_size) if remaining_size < parsed_byte_count => {
295 let remaining_bytes = parsed_bytes.take(remaining_size);
296 self.message_buffer.extend(remaining_bytes);
297 self.max_message_size_exceeded = true;
298 }
299 _ => {
300 self.message_buffer.extend(parsed_bytes);
301 }
302 }
303 }
304}
305
306#[derive(Clone, Debug)]
308enum Parser {
309 Line(LineParser),
310 Literal(LiteralParser),
311}
312
313#[derive(Clone, Debug)]
315struct LineParser {
316 start: usize,
318 end: usize,
320 latest_byte: LatestByte,
322}
323
324impl LineParser {
325 fn new(start: usize) -> Self {
326 Self {
327 start,
328 end: start,
329 latest_byte: LatestByte::Other,
330 }
331 }
332
333 fn parse(&mut self, unprocessed_bytes: &VecDeque<u8>) -> (usize, Option<FragmentInfo>) {
334 let mut parsed_byte_count = 0;
335 let mut parsed_line = None;
336
337 for &next_byte in unprocessed_bytes {
339 parsed_byte_count += 1;
340 self.end += 1;
341
342 self.latest_byte = match self.latest_byte {
343 LatestByte::Other => match next_byte {
344 b'\r' => LatestByte::Cr { announcement: None },
345 b'\n' => {
346 parsed_line = Some(FragmentInfo::Line {
347 start: self.start,
348 end: self.end,
349 announcement: None,
350 ending: LineEnding::Lf,
351 });
352 LatestByte::Other
353 }
354 b'{' => LatestByte::OpeningBracket,
355 _ => LatestByte::Other,
356 },
357 LatestByte::OpeningBracket => match next_byte {
358 b'\r' => LatestByte::Cr { announcement: None },
359 b'\n' => {
360 parsed_line = Some(FragmentInfo::Line {
361 start: self.start,
362 end: self.end,
363 announcement: None,
364 ending: LineEnding::Lf,
365 });
366 LatestByte::Other
367 }
368 b'{' => LatestByte::OpeningBracket,
369 b'0'..=b'9' => {
370 let digit = (next_byte - b'0') as u32;
371 LatestByte::Digit { length: digit }
372 }
373 _ => LatestByte::Other,
374 },
375 LatestByte::Plus { length } => match next_byte {
376 b'\r' => LatestByte::Cr { announcement: None },
377 b'\n' => {
378 parsed_line = Some(FragmentInfo::Line {
379 start: self.start,
380 end: self.end,
381 announcement: None,
382 ending: LineEnding::Lf,
383 });
384 LatestByte::Other
385 }
386 b'{' => LatestByte::OpeningBracket,
387 b'}' => LatestByte::ClosingBracket {
388 announcement: LiteralAnnouncement {
389 mode: LiteralMode::NonSync,
390 length,
391 },
392 },
393 _ => LatestByte::Other,
394 },
395 LatestByte::Digit { length } => match next_byte {
396 b'\r' => LatestByte::Cr { announcement: None },
397 b'\n' => {
398 parsed_line = Some(FragmentInfo::Line {
399 start: self.start,
400 end: self.end,
401 announcement: None,
402 ending: LineEnding::Lf,
403 });
404 LatestByte::Other
405 }
406 b'{' => LatestByte::OpeningBracket,
407 b'0'..=b'9' => {
408 let digit = (next_byte - b'0') as u32;
409 let new_length = length.checked_mul(10).and_then(|x| x.checked_add(digit));
410 match new_length {
411 None => LatestByte::Other,
412 Some(length) => LatestByte::Digit { length },
413 }
414 }
415 b'+' => LatestByte::Plus { length },
416 b'}' => LatestByte::ClosingBracket {
417 announcement: LiteralAnnouncement {
418 mode: LiteralMode::Sync,
419 length,
420 },
421 },
422 _ => LatestByte::Other,
423 },
424 LatestByte::ClosingBracket { announcement } => match next_byte {
425 b'\r' => LatestByte::Cr {
426 announcement: Some(announcement),
427 },
428 b'\n' => {
429 parsed_line = Some(FragmentInfo::Line {
430 start: self.start,
431 end: self.end,
432 announcement: Some(announcement),
433 ending: LineEnding::Lf,
434 });
435 LatestByte::Other
436 }
437 b'{' => LatestByte::OpeningBracket,
438 _ => LatestByte::Other,
439 },
440 LatestByte::Cr { announcement } => match next_byte {
441 b'\r' => LatestByte::Cr { announcement: None },
442 b'\n' => {
443 parsed_line = Some(FragmentInfo::Line {
444 start: self.start,
445 end: self.end,
446 announcement,
447 ending: LineEnding::CrLf,
448 });
449 LatestByte::Other
450 }
451 b'{' => LatestByte::OpeningBracket,
452 _ => LatestByte::Other,
453 },
454 };
455
456 if parsed_line.is_some() {
457 break;
459 }
460 }
461
462 (parsed_byte_count, parsed_line)
463 }
464}
465
466#[derive(Clone, Debug)]
468enum LatestByte {
469 Other,
470 OpeningBracket,
471 Digit {
472 length: u32,
473 },
474 Plus {
475 length: u32,
476 },
477 ClosingBracket {
478 announcement: LiteralAnnouncement,
479 },
480 Cr {
481 announcement: Option<LiteralAnnouncement>,
482 },
483}
484
485#[derive(Clone, Debug)]
487struct LiteralParser {
488 start: usize,
490 end: usize,
492 remaining: u32,
494}
495
496impl LiteralParser {
497 fn new(start: usize, length: u32) -> Self {
498 Self {
499 start,
500 end: start,
501 remaining: length,
502 }
503 }
504
505 fn parse(&mut self, unprocessed_bytes: &VecDeque<u8>) -> (usize, Option<FragmentInfo>) {
506 if unprocessed_bytes.len() < self.remaining as usize {
507 let parsed_byte_count = unprocessed_bytes.len();
509 self.end += parsed_byte_count;
510 self.remaining -= parsed_byte_count as u32;
511 (parsed_byte_count, None)
512 } else {
513 let parsed_byte_count = self.remaining as usize;
515 self.end += parsed_byte_count;
516 self.remaining = 0;
517 let parsed_literal = FragmentInfo::Literal {
518 start: self.start,
519 end: self.end,
520 };
521 (parsed_byte_count, Some(parsed_literal))
522 }
523 }
524}
525
526#[derive(Clone, Copy, Debug, Eq, PartialEq)]
532pub enum FragmentInfo {
533 Line {
535 start: usize,
537 end: usize,
539 announcement: Option<LiteralAnnouncement>,
541 ending: LineEnding,
543 },
544 Literal {
546 start: usize,
548 end: usize,
550 },
551}
552
553impl FragmentInfo {
554 pub fn range(self) -> Range<usize> {
556 match self {
557 FragmentInfo::Line { start, end, .. } => start..end,
558 FragmentInfo::Literal { start, end } => start..end,
559 }
560 }
561}
562
563#[derive(Clone, Copy, Debug, Eq, PartialEq)]
565pub struct LiteralAnnouncement {
566 pub mode: LiteralMode,
568 pub length: u32,
570}
571
572#[derive(Clone, Copy, Debug, Eq, PartialEq)]
574pub enum LineEnding {
575 Lf,
577 CrLf,
579}
580
581#[derive(Clone, Debug, Eq, PartialEq)]
583pub enum DecodeMessageError<'a, C: Decoder> {
584 DecodingFailure(C::Error<'a>),
586 DecodingRemainder {
588 message: C::Message<'a>,
590 remainder: Secret<&'a [u8]>,
592 },
593 MessageTooLong { initial: Secret<&'a [u8]> },
595 MessagePoisoned { discarded: Secret<&'a [u8]> },
597}
598
599fn parse_tag(message_bytes: &[u8]) -> Option<Tag> {
600 let mut bytes = message_bytes.iter().enumerate();
601 let sp = loop {
602 let (i, byte) = bytes.next()?;
603 match byte {
604 b' ' => break i,
606 b'\n' => return None,
608 _ => continue,
610 }
611 };
612
613 Tag::try_from(&message_bytes[..sp]).ok()
614}
615
616#[cfg(test)]
617mod tests {
618 use core::panic;
619 use std::collections::VecDeque;
620
621 use imap_types::{
622 command::{Command, CommandBody},
623 core::{LiteralMode, Tag},
624 secret::Secret,
625 };
626
627 use super::{
628 FragmentInfo, Fragmentizer, LineEnding, LineParser, LiteralAnnouncement, parse_tag,
629 };
630 use crate::{
631 CommandCodec, ResponseCodec, decode::ResponseDecodeError, fragmentizer::DecodeMessageError,
632 };
633
634 #[test]
635 fn fragmentizer_progress_nothing() {
636 let mut fragmentizer = Fragmentizer::without_max_message_size();
637
638 let fragment_info = fragmentizer.progress();
639
640 assert_eq!(fragment_info, None);
641 assert_eq!(fragmentizer.message_bytes(), b"");
642 assert!(!fragmentizer.is_message_complete());
643
644 fragmentizer.enqueue_bytes(&[]);
645 let fragment_info = fragmentizer.progress();
646
647 assert_eq!(fragment_info, None);
648 assert_eq!(fragmentizer.message_bytes(), b"");
649 assert!(!fragmentizer.is_message_complete());
650 }
651
652 #[test]
653 fn fragmentizer_progress_single_message() {
654 let mut fragmentizer = Fragmentizer::without_max_message_size();
655 fragmentizer.enqueue_bytes(b"* OK ...\r\n");
656
657 let fragment_info = fragmentizer.progress().unwrap();
658
659 assert_eq!(
660 fragment_info,
661 FragmentInfo::Line {
662 start: 0,
663 end: 10,
664 announcement: None,
665 ending: LineEnding::CrLf,
666 }
667 );
668 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"* OK ...\r\n");
669 assert!(fragmentizer.is_message_complete());
670
671 let fragment_info = fragmentizer.progress();
672
673 assert_eq!(fragment_info, None);
674 assert_eq!(fragmentizer.message_bytes(), b"");
675 assert!(!fragmentizer.is_message_complete());
676 }
677
678 #[test]
679 fn fragmentizer_progress_multiple_messages() {
680 let mut fragmentizer = Fragmentizer::without_max_message_size();
681 fragmentizer.enqueue_bytes(b"A1 OK ...\r\n");
682 fragmentizer.enqueue_bytes(b"A2 BAD ...\r\n");
683
684 let fragment_info = fragmentizer.progress().unwrap();
685
686 assert_eq!(
687 fragment_info,
688 FragmentInfo::Line {
689 start: 0,
690 end: 11,
691 announcement: None,
692 ending: LineEnding::CrLf,
693 }
694 );
695 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"A1 OK ...\r\n");
696 assert!(fragmentizer.is_message_complete());
697
698 let fragment_info = fragmentizer.progress().unwrap();
699
700 assert_eq!(
701 fragment_info,
702 FragmentInfo::Line {
703 start: 0,
704 end: 12,
705 announcement: None,
706 ending: LineEnding::CrLf,
707 }
708 );
709 assert_eq!(
710 fragmentizer.fragment_bytes(fragment_info),
711 b"A2 BAD ...\r\n"
712 );
713 assert!(fragmentizer.is_message_complete());
714
715 let fragment_info = fragmentizer.progress();
716
717 assert_eq!(fragment_info, None);
718 assert_eq!(fragmentizer.message_bytes(), b"");
719 assert!(!fragmentizer.is_message_complete());
720 }
721
722 #[test]
723 fn fragmentizer_progress_multiple_messages_with_lf() {
724 let mut fragmentizer = Fragmentizer::without_max_message_size();
725 fragmentizer.enqueue_bytes(b"A1 NOOP\n");
726 fragmentizer.enqueue_bytes(b"A2 LOGIN {5}\n");
727 fragmentizer.enqueue_bytes(b"ABCDE");
728 fragmentizer.enqueue_bytes(b" EFGIJ\n");
729
730 let fragment_info = fragmentizer.progress().unwrap();
731
732 assert_eq!(
733 fragment_info,
734 FragmentInfo::Line {
735 start: 0,
736 end: 8,
737 announcement: None,
738 ending: LineEnding::Lf,
739 }
740 );
741 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"A1 NOOP\n");
742 assert!(fragmentizer.is_message_complete());
743
744 let fragment_info = fragmentizer.progress().unwrap();
745
746 assert_eq!(
747 fragment_info,
748 FragmentInfo::Line {
749 start: 0,
750 end: 13,
751 announcement: Some(LiteralAnnouncement {
752 mode: LiteralMode::Sync,
753 length: 5
754 }),
755 ending: LineEnding::Lf,
756 }
757 );
758 assert_eq!(
759 fragmentizer.fragment_bytes(fragment_info),
760 b"A2 LOGIN {5}\n"
761 );
762 assert!(!fragmentizer.is_message_complete());
763
764 let fragment_info = fragmentizer.progress().unwrap();
765
766 assert_eq!(fragment_info, FragmentInfo::Literal { start: 13, end: 18 });
767 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"ABCDE");
768 assert!(!fragmentizer.is_message_complete());
769
770 let fragment_info = fragmentizer.progress().unwrap();
771
772 assert_eq!(
773 fragment_info,
774 FragmentInfo::Line {
775 start: 18,
776 end: 25,
777 announcement: None,
778 ending: LineEnding::Lf,
779 }
780 );
781 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b" EFGIJ\n");
782 assert!(fragmentizer.is_message_complete());
783
784 let fragment_info = fragmentizer.progress();
785
786 assert_eq!(fragment_info, None);
787 assert_eq!(fragmentizer.message_bytes(), b"");
788 assert!(!fragmentizer.is_message_complete());
789 }
790
791 #[test]
792 fn fragmentizer_progress_message_with_multiple_literals() {
793 let mut fragmentizer = Fragmentizer::without_max_message_size();
794 fragmentizer.enqueue_bytes(b"A1 LOGIN {5}\r\n");
795 fragmentizer.enqueue_bytes(b"ABCDE");
796 fragmentizer.enqueue_bytes(b" {5}\r\n");
797 fragmentizer.enqueue_bytes(b"FGHIJ");
798 fragmentizer.enqueue_bytes(b"\r\n");
799
800 let fragment_info = fragmentizer.progress().unwrap();
801
802 assert_eq!(
803 fragment_info,
804 FragmentInfo::Line {
805 start: 0,
806 end: 14,
807 announcement: Some(LiteralAnnouncement {
808 mode: LiteralMode::Sync,
809 length: 5,
810 }),
811 ending: LineEnding::CrLf,
812 }
813 );
814 assert_eq!(
815 fragmentizer.fragment_bytes(fragment_info),
816 b"A1 LOGIN {5}\r\n"
817 );
818 assert!(!fragmentizer.is_message_complete());
819
820 let fragment_info = fragmentizer.progress().unwrap();
821
822 assert_eq!(fragment_info, FragmentInfo::Literal { start: 14, end: 19 });
823 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"ABCDE");
824 assert!(!fragmentizer.is_message_complete());
825
826 let fragment_info = fragmentizer.progress().unwrap();
827
828 assert_eq!(
829 fragment_info,
830 FragmentInfo::Line {
831 start: 19,
832 end: 25,
833 announcement: Some(LiteralAnnouncement {
834 mode: LiteralMode::Sync,
835 length: 5,
836 }),
837 ending: LineEnding::CrLf,
838 }
839 );
840 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b" {5}\r\n");
841 assert!(!fragmentizer.is_message_complete());
842
843 let fragment_info = fragmentizer.progress().unwrap();
844
845 assert_eq!(fragment_info, FragmentInfo::Literal { start: 25, end: 30 });
846 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"FGHIJ");
847 assert!(!fragmentizer.is_message_complete());
848
849 let fragment_info = fragmentizer.progress().unwrap();
850
851 assert_eq!(
852 fragment_info,
853 FragmentInfo::Line {
854 start: 30,
855 end: 32,
856 announcement: None,
857 ending: LineEnding::CrLf,
858 }
859 );
860 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"\r\n");
861 assert!(fragmentizer.is_message_complete());
862
863 let fragment_info = fragmentizer.progress();
864
865 assert_eq!(fragment_info, None);
866 assert_eq!(fragmentizer.message_bytes(), b"");
867 assert!(!fragmentizer.is_message_complete());
868 }
869
870 #[test]
871 fn fragmentizer_progress_message_and_skip_after_literal_announcement() {
872 let mut fragmentizer = Fragmentizer::without_max_message_size();
873 fragmentizer.enqueue_bytes(b"A1 LOGIN {5}\r\n");
874 fragmentizer.enqueue_bytes(b"A2 NOOP\r\n");
875
876 let fragment_info = fragmentizer.progress().unwrap();
877
878 assert_eq!(
879 fragment_info,
880 FragmentInfo::Line {
881 start: 0,
882 end: 14,
883 announcement: Some(LiteralAnnouncement {
884 mode: LiteralMode::Sync,
885 length: 5,
886 }),
887 ending: LineEnding::CrLf,
888 }
889 );
890 assert_eq!(
891 fragmentizer.fragment_bytes(fragment_info),
892 b"A1 LOGIN {5}\r\n"
893 );
894 assert!(!fragmentizer.is_message_complete());
895
896 fragmentizer.skip_message();
897
898 let fragment_info = fragmentizer.progress().unwrap();
899
900 assert_eq!(
901 fragment_info,
902 FragmentInfo::Line {
903 start: 0,
904 end: 9,
905 announcement: None,
906 ending: LineEnding::CrLf,
907 }
908 );
909 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"A2 NOOP\r\n");
910 assert!(fragmentizer.is_message_complete());
911
912 let fragment_info = fragmentizer.progress();
913
914 assert_eq!(fragment_info, None);
915 assert_eq!(fragmentizer.message_bytes(), b"");
916 assert!(!fragmentizer.is_message_complete());
917 }
918
919 #[test]
920 fn fragmentizer_progress_message_byte_by_byte() {
921 let mut fragmentizer = Fragmentizer::without_max_message_size();
922 let mut bytes = VecDeque::new();
923 bytes.extend(b"A1 LOGIN {5}\r\n");
924 bytes.extend(b"ABCDE");
925 bytes.extend(b" FGHIJ\r\n");
926
927 for _ in 0..14 {
928 let fragment_info = fragmentizer.progress();
929
930 assert_eq!(fragment_info, None);
931 assert!(!fragmentizer.is_message_complete());
932
933 fragmentizer.enqueue_bytes(&[bytes.pop_front().unwrap()]);
934 }
935
936 let fragment_info = fragmentizer.progress().unwrap();
937
938 assert_eq!(
939 fragment_info,
940 FragmentInfo::Line {
941 start: 0,
942 end: 14,
943 announcement: Some(LiteralAnnouncement {
944 mode: LiteralMode::Sync,
945 length: 5,
946 }),
947 ending: LineEnding::CrLf,
948 }
949 );
950 assert_eq!(
951 fragmentizer.fragment_bytes(fragment_info),
952 b"A1 LOGIN {5}\r\n"
953 );
954 assert!(!fragmentizer.is_message_complete());
955
956 for _ in 0..5 {
957 let fragment_info = fragmentizer.progress();
958
959 assert_eq!(fragment_info, None);
960 assert!(!fragmentizer.is_message_complete());
961
962 fragmentizer.enqueue_bytes(&[bytes.pop_front().unwrap()]);
963 }
964
965 let fragment_info = fragmentizer.progress().unwrap();
966
967 assert_eq!(fragment_info, FragmentInfo::Literal { start: 14, end: 19 });
968 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"ABCDE");
969 assert!(!fragmentizer.is_message_complete());
970
971 for _ in 0..8 {
972 let fragment_info = fragmentizer.progress();
973
974 assert_eq!(fragment_info, None);
975 assert!(!fragmentizer.is_message_complete());
976
977 fragmentizer.enqueue_bytes(&[bytes.pop_front().unwrap()]);
978 }
979
980 let fragment_info = fragmentizer.progress().unwrap();
981
982 assert_eq!(
983 fragment_info,
984 FragmentInfo::Line {
985 start: 19,
986 end: 27,
987 announcement: None,
988 ending: LineEnding::CrLf,
989 }
990 );
991 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b" FGHIJ\r\n");
992 assert!(fragmentizer.is_message_complete());
993
994 let fragment_info = fragmentizer.progress();
995
996 assert_eq!(fragment_info, None);
997 assert_eq!(fragmentizer.message_bytes(), b"");
998 assert!(!fragmentizer.is_message_complete());
999 }
1000
1001 #[track_caller]
1002 fn assert_is_line(
1003 unprocessed_bytes: &[u8],
1004 line_byte_count: usize,
1005 expected_announcement: Option<LiteralAnnouncement>,
1006 expected_ending: LineEnding,
1007 ) {
1008 let mut line_parser = LineParser::new(0);
1009 let unprocessed_bytes = unprocessed_bytes.iter().copied().collect();
1010
1011 let (parsed_byte_count, fragment_info) = line_parser.parse(&unprocessed_bytes);
1012
1013 assert_eq!(parsed_byte_count, line_byte_count);
1014
1015 let Some(FragmentInfo::Line {
1016 start,
1017 end,
1018 announcement,
1019 ending,
1020 }) = fragment_info
1021 else {
1022 panic!("Unexpected fragment: {fragment_info:?}");
1023 };
1024
1025 assert_eq!(start, 0);
1026 assert_eq!(end, line_byte_count);
1027 assert_eq!(announcement, expected_announcement);
1028 assert_eq!(ending, expected_ending);
1029 }
1030
1031 #[test]
1032 fn fragmentizer_progress_multiple_messages_longer_than_max_size() {
1033 let mut fragmentizer = Fragmentizer::new(17);
1034 fragmentizer.enqueue_bytes(b"A1 NOOP\r\n");
1035 fragmentizer.enqueue_bytes(b"A2 LOGIN ABCDE EFGIJ\r\n");
1036 fragmentizer.enqueue_bytes(b"A3 LOGIN {5}\r\n");
1037 fragmentizer.enqueue_bytes(b"ABCDE");
1038 fragmentizer.enqueue_bytes(b" EFGIJ\r\n");
1039 fragmentizer.enqueue_bytes(b"A4 LOGIN A B\r\n");
1040
1041 let fragment_info = fragmentizer.progress().unwrap();
1042
1043 assert_eq!(
1044 fragment_info,
1045 FragmentInfo::Line {
1046 start: 0,
1047 end: 9,
1048 announcement: None,
1049 ending: LineEnding::CrLf,
1050 }
1051 );
1052 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"A1 NOOP\r\n");
1053 assert_eq!(fragmentizer.message_bytes(), b"A1 NOOP\r\n");
1054 assert!(fragmentizer.is_message_complete());
1055 assert!(!fragmentizer.is_max_message_size_exceeded());
1056
1057 let fragment_info = fragmentizer.progress().unwrap();
1058
1059 assert_eq!(
1060 fragment_info,
1061 FragmentInfo::Line {
1062 start: 0,
1063 end: 22,
1064 announcement: None,
1065 ending: LineEnding::CrLf,
1066 }
1067 );
1068 assert_eq!(
1069 fragmentizer.fragment_bytes(fragment_info),
1070 b"A2 LOGIN ABCDE EF"
1071 );
1072 assert_eq!(fragmentizer.message_bytes(), b"A2 LOGIN ABCDE EF");
1073 assert!(fragmentizer.is_message_complete());
1074 assert!(fragmentizer.is_max_message_size_exceeded());
1075
1076 let fragment_info = fragmentizer.progress().unwrap();
1077
1078 assert_eq!(
1079 fragment_info,
1080 FragmentInfo::Line {
1081 start: 0,
1082 end: 14,
1083 announcement: Some(LiteralAnnouncement {
1084 mode: LiteralMode::Sync,
1085 length: 5
1086 }),
1087 ending: LineEnding::CrLf,
1088 }
1089 );
1090 assert_eq!(
1091 fragmentizer.fragment_bytes(fragment_info),
1092 b"A3 LOGIN {5}\r\n"
1093 );
1094 assert_eq!(fragmentizer.message_bytes(), b"A3 LOGIN {5}\r\n");
1095 assert!(!fragmentizer.is_message_complete());
1096 assert!(!fragmentizer.is_max_message_size_exceeded());
1097
1098 let fragment_info = fragmentizer.progress().unwrap();
1099
1100 assert_eq!(fragment_info, FragmentInfo::Literal { start: 14, end: 19 });
1101 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"ABC");
1102 assert_eq!(fragmentizer.message_bytes(), b"A3 LOGIN {5}\r\nABC");
1103 assert!(!fragmentizer.is_message_complete());
1104 assert!(fragmentizer.is_max_message_size_exceeded());
1105
1106 let fragment_info = fragmentizer.progress().unwrap();
1107
1108 assert_eq!(
1109 fragment_info,
1110 FragmentInfo::Line {
1111 start: 19,
1112 end: 27,
1113 announcement: None,
1114 ending: LineEnding::CrLf,
1115 }
1116 );
1117 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"");
1118 assert_eq!(fragmentizer.message_bytes(), b"A3 LOGIN {5}\r\nABC");
1119 assert!(fragmentizer.is_message_complete());
1120 assert!(fragmentizer.is_max_message_size_exceeded());
1121
1122 let fragment_info = fragmentizer.progress().unwrap();
1123
1124 assert_eq!(
1125 fragment_info,
1126 FragmentInfo::Line {
1127 start: 0,
1128 end: 14,
1129 announcement: None,
1130 ending: LineEnding::CrLf,
1131 }
1132 );
1133 assert_eq!(
1134 fragmentizer.fragment_bytes(fragment_info),
1135 b"A4 LOGIN A B\r\n"
1136 );
1137 assert_eq!(fragmentizer.message_bytes(), b"A4 LOGIN A B\r\n");
1138 assert!(fragmentizer.is_message_complete());
1139 assert!(!fragmentizer.is_max_message_size_exceeded());
1140
1141 let fragment_info = fragmentizer.progress();
1142
1143 assert_eq!(fragment_info, None);
1144 assert_eq!(fragmentizer.message_bytes(), b"");
1145 assert_eq!(fragmentizer.message_bytes(), b"");
1146 assert!(!fragmentizer.is_message_complete());
1147 assert!(!fragmentizer.is_max_message_size_exceeded());
1148 }
1149
1150 #[test]
1151 fn fragmentizer_progress_messages_with_zero_max_size() {
1152 let mut fragmentizer = Fragmentizer::new(0);
1153 fragmentizer.enqueue_bytes(b"A1 NOOP\r\n");
1154 fragmentizer.enqueue_bytes(b"A2 LOGIN ABCDE EFGIJ\r\n");
1155 fragmentizer.enqueue_bytes(b"A3 LOGIN {5}\r\n");
1156 fragmentizer.enqueue_bytes(b"ABCDE");
1157 fragmentizer.enqueue_bytes(b" EFGIJ\r\n");
1158
1159 let fragment_info = fragmentizer.progress().unwrap();
1160
1161 assert_eq!(
1162 fragment_info,
1163 FragmentInfo::Line {
1164 start: 0,
1165 end: 9,
1166 announcement: None,
1167 ending: LineEnding::CrLf,
1168 }
1169 );
1170 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"");
1171 assert_eq!(fragmentizer.message_bytes(), b"");
1172 assert!(fragmentizer.is_message_complete());
1173 assert!(fragmentizer.is_max_message_size_exceeded());
1174
1175 let fragment_info = fragmentizer.progress().unwrap();
1176
1177 assert_eq!(
1178 fragment_info,
1179 FragmentInfo::Line {
1180 start: 0,
1181 end: 22,
1182 announcement: None,
1183 ending: LineEnding::CrLf,
1184 }
1185 );
1186 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"");
1187 assert_eq!(fragmentizer.message_bytes(), b"");
1188 assert!(fragmentizer.is_message_complete());
1189 assert!(fragmentizer.is_max_message_size_exceeded());
1190
1191 let fragment_info = fragmentizer.progress().unwrap();
1192
1193 assert_eq!(
1194 fragment_info,
1195 FragmentInfo::Line {
1196 start: 0,
1197 end: 14,
1198 announcement: Some(LiteralAnnouncement {
1199 mode: LiteralMode::Sync,
1200 length: 5
1201 }),
1202 ending: LineEnding::CrLf,
1203 }
1204 );
1205 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"");
1206 assert_eq!(fragmentizer.message_bytes(), b"");
1207 assert!(!fragmentizer.is_message_complete());
1208 assert!(fragmentizer.is_max_message_size_exceeded());
1209
1210 let fragment_info = fragmentizer.progress().unwrap();
1211
1212 assert_eq!(fragment_info, FragmentInfo::Literal { start: 14, end: 19 });
1213 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"");
1214 assert_eq!(fragmentizer.message_bytes(), b"");
1215 assert!(!fragmentizer.is_message_complete());
1216 assert!(fragmentizer.is_max_message_size_exceeded());
1217
1218 let fragment_info = fragmentizer.progress().unwrap();
1219
1220 assert_eq!(
1221 fragment_info,
1222 FragmentInfo::Line {
1223 start: 19,
1224 end: 27,
1225 announcement: None,
1226 ending: LineEnding::CrLf,
1227 }
1228 );
1229 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"");
1230 assert_eq!(fragmentizer.message_bytes(), b"");
1231 assert!(fragmentizer.is_message_complete());
1232 assert!(fragmentizer.is_max_message_size_exceeded());
1233
1234 let fragment_info = fragmentizer.progress();
1235
1236 assert_eq!(fragment_info, None);
1237 assert_eq!(fragmentizer.message_bytes(), b"");
1238 assert_eq!(fragmentizer.message_bytes(), b"");
1239 assert!(!fragmentizer.is_message_complete());
1240 assert!(!fragmentizer.is_max_message_size_exceeded());
1241 }
1242
1243 #[test]
1244 fn fragmentizer_decode_message() {
1245 let command_codec = CommandCodec::new();
1246 let response_codec = ResponseCodec::new();
1247
1248 let mut fragmentizer = Fragmentizer::new(10);
1249 fragmentizer.enqueue_bytes(b"A1 NOOP\r\n");
1250 fragmentizer.enqueue_bytes(b"A2 LOGIN ABCDE EFGIJ\r\n");
1251
1252 fragmentizer.progress();
1253 assert_eq!(
1254 fragmentizer.decode_message(&command_codec),
1255 Ok(Command::new("A1", CommandBody::Noop).unwrap()),
1256 );
1257 assert_eq!(
1258 fragmentizer.decode_message(&response_codec),
1259 Err(DecodeMessageError::DecodingFailure(
1260 ResponseDecodeError::Failed
1261 )),
1262 );
1263
1264 fragmentizer.progress();
1265 assert_eq!(
1266 fragmentizer.decode_message(&response_codec),
1267 Err(DecodeMessageError::MessageTooLong {
1268 initial: Secret::new(b"A2 LOGIN A"),
1269 }),
1270 );
1271 }
1272
1273 #[test]
1274 fn fragmentizer_poison_message() {
1275 let command_codec = CommandCodec::new();
1276
1277 let mut fragmentizer = Fragmentizer::without_max_message_size();
1278 fragmentizer.enqueue_bytes(b"A1 NOOP\r\n");
1279 fragmentizer.enqueue_bytes(b"A2 LOGIN {5}\r\n");
1280 fragmentizer.enqueue_bytes(b"ABCDE");
1281 fragmentizer.enqueue_bytes(b" EFGIJ\r\n");
1282
1283 assert!(!fragmentizer.is_message_poisoned());
1284
1285 fragmentizer.poison_message();
1286
1287 assert!(fragmentizer.is_message_poisoned());
1288
1289 let fragment_info = fragmentizer.progress().unwrap();
1290
1291 assert_eq!(
1292 fragment_info,
1293 FragmentInfo::Line {
1294 start: 0,
1295 end: 9,
1296 announcement: None,
1297 ending: LineEnding::CrLf,
1298 }
1299 );
1300 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"A1 NOOP\r\n");
1301 assert_eq!(fragmentizer.message_bytes(), b"A1 NOOP\r\n");
1302 assert!(fragmentizer.is_message_complete());
1303 assert!(fragmentizer.is_message_poisoned());
1304
1305 let decode_err = fragmentizer.decode_message(&command_codec).unwrap_err();
1306
1307 assert_eq!(
1308 decode_err,
1309 DecodeMessageError::MessagePoisoned {
1310 discarded: Secret::new(fragmentizer.message_bytes())
1311 }
1312 );
1313
1314 let fragment_info = fragmentizer.progress().unwrap();
1315
1316 assert_eq!(
1317 fragment_info,
1318 FragmentInfo::Line {
1319 start: 0,
1320 end: 14,
1321 announcement: Some(LiteralAnnouncement {
1322 mode: LiteralMode::Sync,
1323 length: 5
1324 }),
1325 ending: LineEnding::CrLf,
1326 }
1327 );
1328 assert_eq!(
1329 fragmentizer.fragment_bytes(fragment_info),
1330 b"A2 LOGIN {5}\r\n"
1331 );
1332 assert_eq!(fragmentizer.message_bytes(), b"A2 LOGIN {5}\r\n");
1333 assert!(!fragmentizer.is_message_complete());
1334 assert!(!fragmentizer.is_message_poisoned());
1335
1336 let fragment_info = fragmentizer.progress().unwrap();
1337
1338 assert_eq!(fragment_info, FragmentInfo::Literal { start: 14, end: 19 });
1339 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"ABCDE");
1340 assert_eq!(fragmentizer.message_bytes(), b"A2 LOGIN {5}\r\nABCDE");
1341 assert!(!fragmentizer.is_message_complete());
1342 assert!(!fragmentizer.is_message_poisoned());
1343
1344 fragmentizer.poison_message();
1345 assert!(fragmentizer.is_message_poisoned());
1346
1347 let fragment_info = fragmentizer.progress().unwrap();
1348
1349 assert_eq!(
1350 fragment_info,
1351 FragmentInfo::Line {
1352 start: 19,
1353 end: 27,
1354 announcement: None,
1355 ending: LineEnding::CrLf,
1356 }
1357 );
1358 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b" EFGIJ\r\n");
1359 assert_eq!(
1360 fragmentizer.message_bytes(),
1361 b"A2 LOGIN {5}\r\nABCDE EFGIJ\r\n"
1362 );
1363 assert!(fragmentizer.is_message_complete());
1364 assert!(fragmentizer.is_message_poisoned());
1365
1366 let decode_err = fragmentizer.decode_message(&command_codec).unwrap_err();
1367
1368 assert_eq!(
1369 decode_err,
1370 DecodeMessageError::MessagePoisoned {
1371 discarded: Secret::new(fragmentizer.message_bytes())
1372 }
1373 );
1374
1375 let fragment_info = fragmentizer.progress();
1376
1377 assert_eq!(fragment_info, None);
1378 assert_eq!(fragmentizer.message_bytes(), b"");
1379 assert_eq!(fragmentizer.message_bytes(), b"");
1380 assert!(!fragmentizer.is_message_complete());
1381 assert!(!fragmentizer.is_message_poisoned());
1382 }
1383
1384 #[test]
1385 fn fragmentizer_poison_too_long_message() {
1386 let command_codec = CommandCodec::new();
1387
1388 let mut fragmentizer = Fragmentizer::new(5);
1389 fragmentizer.enqueue_bytes(b"A1 NOOP\r\n");
1390
1391 assert!(!fragmentizer.is_message_poisoned());
1392
1393 fragmentizer.poison_message();
1394
1395 assert!(fragmentizer.is_message_poisoned());
1396
1397 let fragment_info = fragmentizer.progress().unwrap();
1398
1399 assert_eq!(
1400 fragment_info,
1401 FragmentInfo::Line {
1402 start: 0,
1403 end: 9,
1404 announcement: None,
1405 ending: LineEnding::CrLf,
1406 }
1407 );
1408 assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"A1 NO");
1409 assert_eq!(fragmentizer.message_bytes(), b"A1 NO");
1410 assert!(fragmentizer.is_message_complete());
1411 assert!(fragmentizer.is_max_message_size_exceeded());
1412 assert!(fragmentizer.is_message_poisoned());
1413
1414 let decode_err = fragmentizer.decode_message(&command_codec).unwrap_err();
1415
1416 assert_eq!(
1417 decode_err,
1418 DecodeMessageError::MessageTooLong {
1419 initial: Secret::new(b"A1 NO")
1420 }
1421 );
1422
1423 let fragment_info = fragmentizer.progress();
1424
1425 assert_eq!(fragment_info, None);
1426 assert_eq!(fragmentizer.message_bytes(), b"");
1427 assert_eq!(fragmentizer.message_bytes(), b"");
1428 assert!(!fragmentizer.is_message_complete());
1429 assert!(!fragmentizer.is_max_message_size_exceeded());
1430 assert!(!fragmentizer.is_message_poisoned());
1431 }
1432
1433 #[track_caller]
1434 fn assert_not_line(not_a_line_bytes: &[u8]) {
1435 let mut line_parser = LineParser::new(0);
1436 let not_a_line_bytes = not_a_line_bytes.iter().copied().collect();
1437
1438 let (parsed_byte_count, fragment_info) = line_parser.parse(¬_a_line_bytes);
1439
1440 assert_eq!(parsed_byte_count, not_a_line_bytes.len());
1441 assert_eq!(fragment_info, None);
1442 }
1443
1444 #[test]
1445 fn parse_line_examples() {
1446 assert_not_line(b"");
1447 assert_not_line(b"foo");
1448
1449 assert_is_line(b"\n", 1, None, LineEnding::Lf);
1450 assert_is_line(b"\r\n", 2, None, LineEnding::CrLf);
1451 assert_is_line(b"\n\r", 1, None, LineEnding::Lf);
1452 assert_is_line(b"foo\n", 4, None, LineEnding::Lf);
1453 assert_is_line(b"foo\r\n", 5, None, LineEnding::CrLf);
1454 assert_is_line(b"foo\n\r", 4, None, LineEnding::Lf);
1455 assert_is_line(b"foo\nbar\n", 4, None, LineEnding::Lf);
1456 assert_is_line(b"foo\r\nbar\r\n", 5, None, LineEnding::CrLf);
1457 assert_is_line(b"\r\nfoo\r\n", 2, None, LineEnding::CrLf);
1458 assert_is_line(
1459 b"{1}\r\n",
1460 5,
1461 Some(LiteralAnnouncement {
1462 length: 1,
1463 mode: LiteralMode::Sync,
1464 }),
1465 LineEnding::CrLf,
1466 );
1467 assert_is_line(
1468 b"{1}\n",
1469 4,
1470 Some(LiteralAnnouncement {
1471 length: 1,
1472 mode: LiteralMode::Sync,
1473 }),
1474 LineEnding::Lf,
1475 );
1476 assert_is_line(
1477 b"foo {1}\r\n",
1478 9,
1479 Some(LiteralAnnouncement {
1480 length: 1,
1481 mode: LiteralMode::Sync,
1482 }),
1483 LineEnding::CrLf,
1484 );
1485 assert_is_line(
1486 b"foo {2} {1}\r\n",
1487 13,
1488 Some(LiteralAnnouncement {
1489 length: 1,
1490 mode: LiteralMode::Sync,
1491 }),
1492 LineEnding::CrLf,
1493 );
1494 assert_is_line(b"foo {1} \r\n", 10, None, LineEnding::CrLf);
1495 assert_is_line(b"foo \n {1}\r\n", 5, None, LineEnding::Lf);
1496 assert_is_line(b"foo {1} foo\r\n", 13, None, LineEnding::CrLf);
1497 assert_is_line(b"foo {1\r\n", 8, None, LineEnding::CrLf);
1498 assert_is_line(b"foo 1}\r\n", 8, None, LineEnding::CrLf);
1499 assert_is_line(b"foo { 1}\r\n", 10, None, LineEnding::CrLf);
1500 assert_is_line(
1501 b"foo {{1}\r\n",
1502 10,
1503 Some(LiteralAnnouncement {
1504 length: 1,
1505 mode: LiteralMode::Sync,
1506 }),
1507 LineEnding::CrLf,
1508 );
1509 assert_is_line(
1510 b"foo {42}\r\n",
1511 10,
1512 Some(LiteralAnnouncement {
1513 length: 42,
1514 mode: LiteralMode::Sync,
1515 }),
1516 LineEnding::CrLf,
1517 );
1518 assert_is_line(
1519 b"foo {42+}\r\n",
1520 11,
1521 Some(LiteralAnnouncement {
1522 length: 42,
1523 mode: LiteralMode::NonSync,
1524 }),
1525 LineEnding::CrLf,
1526 );
1527 assert_is_line(
1528 b"foo +{42}\r\n",
1529 11,
1530 Some(LiteralAnnouncement {
1531 length: 42,
1532 mode: LiteralMode::Sync,
1533 }),
1534 LineEnding::CrLf,
1535 );
1536 assert_is_line(b"foo {+}\r\n", 9, None, LineEnding::CrLf);
1537 assert_is_line(b"foo {42++}\r\n", 12, None, LineEnding::CrLf);
1538 assert_is_line(b"foo {+42+}\r\n", 12, None, LineEnding::CrLf);
1539 assert_is_line(b"foo {+42}\r\n", 11, None, LineEnding::CrLf);
1540 assert_is_line(b"foo {42}+\r\n", 11, None, LineEnding::CrLf);
1541 assert_is_line(b"foo {-42}\r\n", 11, None, LineEnding::CrLf);
1542 assert_is_line(b"foo {42-}\r\n", 11, None, LineEnding::CrLf);
1543 assert_is_line(
1544 b"foo {4294967295}\r\n",
1545 18,
1546 Some(LiteralAnnouncement {
1547 length: 4294967295,
1548 mode: LiteralMode::Sync,
1549 }),
1550 LineEnding::CrLf,
1551 );
1552 assert_is_line(b"foo {4294967296}\r\n", 18, None, LineEnding::CrLf);
1553 }
1554
1555 #[test]
1556 fn parse_line_corner_case() {
1557 assert_is_line(
1565 b"* OK {1}\r\n",
1566 10,
1567 Some(LiteralAnnouncement {
1568 length: 1,
1569 mode: LiteralMode::Sync,
1570 }),
1571 LineEnding::CrLf,
1572 );
1573 }
1574
1575 #[test]
1576 fn parse_tag_examples() {
1577 assert_eq!(parse_tag(b"1 NOOP\r\n"), Tag::try_from("1").ok());
1578 assert_eq!(parse_tag(b"12 NOOP\r\n"), Tag::try_from("12").ok());
1579 assert_eq!(parse_tag(b"123 NOOP\r\n"), Tag::try_from("123").ok());
1580 assert_eq!(parse_tag(b"1234 NOOP\r\n"), Tag::try_from("1234").ok());
1581 assert_eq!(parse_tag(b"12345 NOOP\r\n"), Tag::try_from("12345").ok());
1582
1583 assert_eq!(parse_tag(b"A1 NOOP\r\n"), Tag::try_from("A1").ok());
1584 assert_eq!(parse_tag(b"A1 NOOP"), Tag::try_from("A1").ok());
1585 assert_eq!(parse_tag(b"A1 "), Tag::try_from("A1").ok());
1586 assert_eq!(parse_tag(b"A1 "), Tag::try_from("A1").ok());
1587 assert_eq!(parse_tag(b"A1 \r\n"), Tag::try_from("A1").ok());
1588 assert_eq!(parse_tag(b"A1 \n"), Tag::try_from("A1").ok());
1589 assert_eq!(parse_tag(b"A1"), None);
1590 assert_eq!(parse_tag(b"A1\r\n"), None);
1591 assert_eq!(parse_tag(b"A1\n"), None);
1592 assert_eq!(parse_tag(b" \r\n"), None);
1593 assert_eq!(parse_tag(b"\r\n"), None);
1594 assert_eq!(parse_tag(b""), None);
1595 assert_eq!(parse_tag(b" A1 NOOP\r\n"), None);
1596 }
1597}