1pub mod ascii;
42pub mod capture;
43pub mod event;
44pub mod iter;
45pub mod signature;
46
47use ascii::AsciiControl;
48use event::{CSI, DCS, Esc, EscInvalid, SS2, SS3, VTEvent, VTIntermediate};
49
50const ESC: u8 = AsciiControl::Esc as _;
51const BEL: u8 = AsciiControl::Bel as _;
52const DEL: u8 = AsciiControl::Del as _;
53const CAN: u8 = AsciiControl::Can as _;
54const SUB: u8 = AsciiControl::Sub as _;
55const CSI: u8 = b'[';
56const OSC: u8 = b']';
57const SS2: u8 = b'N';
58const SS3: u8 = b'O';
59const DCS: u8 = b'P';
60const APC: u8 = b'_';
61const PM: u8 = b'^';
62const SOS: u8 = b'X';
63const ST_FINAL: u8 = b'\\';
64
65use crate::event::{Param, ParamBuf, ParamList};
66
67#[allow(private_bounds)]
69pub trait VTEventCallback: VTEventCallbackMaybeAbortable<()> {
70 fn event(&mut self, event: VTEvent<'_>) -> ();
71}
72
73impl<T: FnMut(VTEvent<'_>)> VTEventCallback for T {
74 #[inline(always)]
75 fn event(&mut self, event: VTEvent<'_>) {
76 self(event)
77 }
78}
79
80impl<T: VTEventCallback> VTEventCallbackMaybeAbortable<()> for T {
81 #[inline(always)]
82 fn event(&mut self, event: VTEvent<'_>) {
83 VTEventCallback::event(self, event)
84 }
85}
86
87#[allow(private_bounds)]
90pub trait VTEventCallbackAbortable: VTEventCallbackMaybeAbortable<bool> {
91 fn event(&mut self, event: VTEvent<'_>) -> bool;
92}
93
94impl<T: VTEventCallbackAbortable> VTEventCallbackMaybeAbortable<bool> for T {
95 #[inline(always)]
96 fn event(&mut self, event: VTEvent<'_>) -> bool {
97 VTEventCallbackAbortable::event(self, event)
98 }
99}
100
101impl<T: FnMut(VTEvent<'_>) -> bool> VTEventCallbackAbortable for T {
102 #[inline(always)]
103 fn event(&mut self, event: VTEvent<'_>) -> bool {
104 self(event)
105 }
106}
107
108trait VTEventCallbackMaybeAbortable<R: MaybeAbortable> {
109 fn event<'e>(&mut self, event: VTEvent<'e>) -> R;
110}
111
112enum VTAction<'a> {
114 None,
117 Event(VTEvent<'a>),
120 End(VTEnd),
122 Buffer(VTEmit),
125 Hold(VTEmit),
128 Cancel(VTEmit),
130}
131
132#[derive(Debug, Copy, Clone, PartialEq, Eq)]
133enum VTEmit {
134 Ground,
136 Dcs,
138 Osc,
140}
141
142#[derive(Debug, Copy, Clone, PartialEq, Eq)]
143enum VTEnd {
144 Dcs,
146 Osc { used_bel: bool },
148}
149
150macro_rules! def_pattern {
151 ($name:ident => $pattern:pat) => {
152 #[inline]
153 #[allow(unused)]
154 const fn $name(b: u8) -> bool {
155 matches!(b, $pattern)
156 }
157
158 #[allow(unused)]
159 macro_rules! $name {
160 () => {
161 $pattern
162 };
163 }
164 };
165}
166
167def_pattern!(is_c0 => 0x00..=0x08 | 0x0b..=0x0c | 0x0e..=0x1f);
168def_pattern!(is_any_c0 => 0x00..=0x1f);
169def_pattern!(is_printable => b' '..=b'~');
170def_pattern!(is_intermediate => b' '..=b'/');
171def_pattern!(is_final => 0x40..=0x7e);
172def_pattern!(is_priv => b'<' | b'=' | b'>' | b'?');
173def_pattern!(is_priv_no_q => b'<' | b'=' | b'>');
174def_pattern!(is_digit => b'0'..=b'9');
175
176macro_rules! byte_predicate {
177 (|$p:ident| $body:block) => {{
178 let mut out: [bool; 256] = [false; 256];
179 let mut i = 0;
180 while i < 256 {
181 let $p: u8 = i as u8;
182 out[i] = $body;
183 i += 1;
184 }
185 out
186 }};
187}
188
189const ENDS_CSI: [bool; 256] =
190 byte_predicate!(|b| { is_final(b) || b == ESC || b == CAN || b == SUB });
191
192const ENDS_GROUND: [bool; 256] = byte_predicate!(|b| { is_c0(b) || b == DEL });
193
194#[derive(Debug, Copy, Clone, PartialEq, Eq)]
195enum State {
196 Ground,
197 Escape,
198 EscInt,
199 EscSs2,
200 EscSs3,
201 CsiEntry,
202 CsiParam,
203 CsiInt,
204 CsiIgnore,
205 DcsEntry,
206 DcsParam,
207 DcsInt,
208 DcsIgnore,
209 DcsIgnoreEsc,
210 DcsPassthrough,
211 DcsEsc,
212 OscString,
213 OscEsc,
214 SosPmApcString,
215 SpaEsc,
216}
217
218pub const VT_PARSER_INTEREST_NONE: u8 = 0;
220pub const VT_PARSER_INTEREST_CSI: u8 = 1 << 0;
222pub const VT_PARSER_INTEREST_DCS: u8 = 1 << 1;
224pub const VT_PARSER_INTEREST_OSC: u8 = 1 << 2;
226pub const VT_PARSER_INTEREST_ESCAPE_RECOVERY: u8 = 1 << 4;
228pub const VT_PARSER_INTEREST_OTHER: u8 = 1 << 5;
230
231pub const VT_PARSER_INTEREST_ALL: u8 = VT_PARSER_INTEREST_CSI
233 | VT_PARSER_INTEREST_DCS
234 | VT_PARSER_INTEREST_OSC
235 | VT_PARSER_INTEREST_ESCAPE_RECOVERY
236 | VT_PARSER_INTEREST_OTHER;
237
238pub const VT_PARSER_INTEREST_DEFAULT: u8 = VT_PARSER_INTEREST_CSI
240 | VT_PARSER_INTEREST_DCS
241 | VT_PARSER_INTEREST_OSC
242 | VT_PARSER_INTEREST_OTHER;
243
244#[must_use]
245trait MaybeAbortable {
246 fn abort(self) -> bool;
247}
248
249impl MaybeAbortable for bool {
250 #[inline(always)]
251 fn abort(self) -> bool {
252 !self
253 }
254}
255
256impl MaybeAbortable for () {
257 #[inline(always)]
258 fn abort(self) -> bool {
259 false
260 }
261}
262
263pub struct VTPushParser<const INTEREST: u8 = VT_PARSER_INTEREST_DEFAULT> {
268 st: State,
269
270 ints: VTIntermediate,
272 params: ParamList,
273 cur_param: Param,
274 priv_prefix: Option<u8>,
275 held_byte: Option<u8>,
276}
277
278impl Default for VTPushParser {
279 fn default() -> Self {
280 Self::new()
281 }
282}
283
284impl VTPushParser {
285 pub const fn new() -> Self {
286 VTPushParser::new_with()
287 }
288
289 pub fn decode_buffer<'a>(input: &'a [u8], mut cb: impl for<'b> FnMut(VTEvent<'b>)) {
291 let mut parser = VTPushParser::new();
292 parser.feed_with(input, &mut cb);
293 }
294
295 pub const fn new_with_interest<const INTEREST: u8>() -> VTPushParser<INTEREST> {
296 VTPushParser::new_with()
297 }
298}
299
300macro_rules! invalid {
302 ($self:ident .priv_prefix, $self_:ident .ints, $b:expr) => {
303 if let Some(p) = $self.priv_prefix {
304 if $self.ints.len() == 0 {
305 VTEvent::EscInvalid(EscInvalid::Two(p, $b))
306 } else if $self.ints.len() == 1 {
307 VTEvent::EscInvalid(EscInvalid::Three(p, $self.ints.data[0], $b))
308 } else {
309 VTEvent::EscInvalid(EscInvalid::Four(
310 p,
311 $self.ints.data[0],
312 $self.ints.data[1],
313 $b,
314 ))
315 }
316 } else {
317 if $self.ints.len() == 0 {
318 VTEvent::EscInvalid(EscInvalid::One($b))
319 } else if $self.ints.len() == 1 {
320 VTEvent::EscInvalid(EscInvalid::Two($self.ints.data[0], $b))
321 } else {
322 VTEvent::EscInvalid(EscInvalid::Three(
323 $self.ints.data[0],
324 $self.ints.data[1],
325 $b,
326 ))
327 }
328 }
329 };
330 ($self:ident .priv_prefix, $self_:ident .ints) => {
331 if let Some(p) = $self.priv_prefix {
332 if $self.ints.len() == 0 {
333 VTEvent::EscInvalid(EscInvalid::One(p))
334 } else if $self.ints.len() == 1 {
335 VTEvent::EscInvalid(EscInvalid::Two(p, $self.ints.data[0]))
336 } else {
337 VTEvent::EscInvalid(EscInvalid::Three(p, $self.ints.data[0], $self.ints.data[1]))
338 }
339 } else {
340 if $self.ints.len() == 0 {
341 VTEvent::C0(0x1b)
343 } else if $self.ints.len() == 1 {
344 VTEvent::EscInvalid(EscInvalid::One($self.ints.data[0]))
345 } else {
346 VTEvent::EscInvalid(EscInvalid::Two($self.ints.data[0], $self.ints.data[1]))
347 }
348 }
349 };
350 ($a:expr) => {
351 VTEvent::EscInvalid(EscInvalid::One($a))
352 };
353 ($a:expr, $b:expr) => {
354 VTEvent::EscInvalid(EscInvalid::Two($a, $b))
355 };
356}
357
358impl<const INTEREST: u8> VTPushParser<INTEREST> {
359 const fn new_with() -> Self {
360 Self {
361 st: State::Ground,
362 ints: VTIntermediate::empty(),
363 params: ParamList::EMPTY,
364 cur_param: Param::EMPTY,
365 priv_prefix: None,
366 held_byte: None,
367 }
368 }
369
370 #[inline]
414 pub fn feed_with<F: VTEventCallback>(&mut self, input: &[u8], mut cb: F) {
415 self.feed_with_internal(input, &mut cb);
416 }
417
418 #[inline]
431 pub fn feed_with_abortable<F: VTEventCallbackAbortable>(
432 &mut self,
433 input: &[u8],
434 mut cb: F,
435 ) -> usize {
436 self.feed_with_internal(input, &mut cb)
437 }
438
439 #[inline(always)]
440 fn feed_with_internal<R: MaybeAbortable, F: VTEventCallbackMaybeAbortable<R>>(
441 &mut self,
442 input: &[u8],
443 cb: &mut F,
444 ) -> usize {
445 if input.is_empty() {
446 return 0;
447 }
448
449 #[derive(Debug)]
450 struct FeedState {
451 buffer_idx: usize,
452 current_emit: Option<VTEmit>,
453 hold: bool,
454 }
455
456 let mut state = FeedState {
457 buffer_idx: 0,
458 current_emit: None,
459 hold: self.held_byte.is_some(),
460 };
461
462 let mut held_byte = self.held_byte.take();
463 let mut i = 0;
464
465 while i < input.len() {
466 if self.st == State::Ground {
468 let start = i;
469 loop {
470 if i >= input.len() {
471 cb.event(VTEvent::Raw(&input[start..]));
472 return input.len();
473 }
474 if ENDS_GROUND[input[i] as usize] {
475 break;
476 }
477 i += 1;
478 }
479
480 if start != i && cb.event(VTEvent::Raw(&input[start..i])).abort() {
481 return i;
482 }
483
484 if input[i] == ESC {
485 self.clear_hdr_collectors();
486 self.st = State::Escape;
487 i += 1;
488 continue;
489 }
490 }
491
492 if self.st == State::CsiIgnore {
494 loop {
495 if i >= input.len() {
496 return input.len();
497 }
498 if ENDS_CSI[input[i] as usize] {
499 break;
500 }
501 i += 1;
502 }
503
504 if input[i] == ESC {
505 self.st = State::Escape;
506 } else {
507 self.st = State::Ground;
508 }
509 i += 1;
510 continue;
511 }
512
513 let action = self.push_with(input[i]);
514
515 match action {
516 VTAction::None => {
517 if let Some(emit) = state.current_emit {
518 let range = state.buffer_idx..(i - state.hold as usize);
520 if !range.is_empty()
521 && match emit {
522 VTEmit::Ground => cb.event(VTEvent::Raw(&input[range])),
523 VTEmit::Dcs => cb.event(VTEvent::DcsData(&input[range])),
524 VTEmit::Osc => cb.event(VTEvent::OscData(&input[range])),
525 }
526 .abort()
527 {
528 if state.hold {
529 self.held_byte = Some(0x1b);
530 }
531 return i + 1;
532 }
533 if state.hold {
534 held_byte = Some(0x1b);
535 }
536 state.current_emit = None;
537 }
538 }
539 VTAction::Event(e) => {
540 if cb.event(e).abort() {
541 return i + 1;
542 }
543 }
544 VTAction::End(VTEnd::Dcs) => {
545 state.current_emit = None;
546 let hold = std::mem::take(&mut state.hold);
547 let range = state.buffer_idx..(i.saturating_sub(hold as usize));
548 if cb.event(VTEvent::DcsEnd(&input[range])).abort() {
549 return i + 1;
550 }
551 held_byte = None;
552 }
553 VTAction::End(VTEnd::Osc { used_bel }) => {
554 state.current_emit = None;
555 let hold = std::mem::take(&mut state.hold);
556 let range = state.buffer_idx..(i.saturating_sub(hold as usize));
557 if cb
558 .event(VTEvent::OscEnd {
559 data: &input[range],
560 used_bel,
561 })
562 .abort()
563 {
564 return i + 1;
565 }
566 held_byte = None;
567 }
568 VTAction::Buffer(emit) | VTAction::Hold(emit) => {
569 if state.current_emit.is_none()
570 && let Some(h) = held_byte.take()
571 && match emit {
572 VTEmit::Ground => cb.event(VTEvent::Raw(&[h])),
573 VTEmit::Dcs => cb.event(VTEvent::DcsData(&[h])),
574 VTEmit::Osc => cb.event(VTEvent::OscData(&[h])),
575 }
576 .abort()
577 {
578 if matches!(action, VTAction::Hold(_)) {
579 self.held_byte = Some(0x1b);
580 return 1;
581 }
582 return 0;
583 }
584
585 debug_assert!(state.current_emit.is_none() || state.current_emit == Some(emit));
586
587 state.hold = matches!(action, VTAction::Hold(_));
588 if state.current_emit.is_none() {
589 state.buffer_idx = i;
590 state.current_emit = Some(emit);
591 }
592 }
593 VTAction::Cancel(emit) => {
594 state.current_emit = None;
595 state.hold = false;
596 if match emit {
597 VTEmit::Ground => unreachable!(),
598 VTEmit::Dcs => cb.event(VTEvent::DcsCancel),
599 VTEmit::Osc => cb.event(VTEvent::OscCancel),
600 }
601 .abort()
602 {
603 return i + 1;
604 }
605 }
606 };
607 i += 1;
608 }
609
610 if state.hold {
612 self.held_byte = Some(0x1b);
613 }
614
615 if let Some(emit) = state.current_emit.take() {
616 let range = &input[state.buffer_idx..input.len() - state.hold as usize];
617 if !range.is_empty() {
618 match emit {
619 VTEmit::Ground => cb.event(VTEvent::Raw(range)),
620 VTEmit::Dcs => cb.event(VTEvent::DcsData(range)),
621 VTEmit::Osc => cb.event(VTEvent::OscData(range)),
622 };
623 }
624 };
625
626 input.len()
628 }
629
630 pub fn is_ground(&self) -> bool {
632 self.st == State::Ground
633 }
634
635 pub fn idle(&mut self) -> Option<VTEvent<'static>> {
639 match self.st {
640 State::Escape => {
641 self.st = State::Ground;
642 Some(VTEvent::C0(ESC))
643 }
644 State::EscInt => {
645 self.st = State::Ground;
646 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
647 None
648 } else {
649 Some(invalid!(self.priv_prefix, self.ints))
650 }
651 }
652 State::EscSs2 | State::EscSs3 => {
653 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
654 self.st = State::Ground;
655 None
656 } else {
657 let c = match self.st {
658 State::EscSs2 => SS2,
659 State::EscSs3 => SS3,
660 _ => unreachable!(),
661 };
662 self.st = State::Ground;
663 Some(invalid!(c))
664 }
665 }
666 _ => None,
667 }
668 }
669
670 pub fn finish<F: FnMut(VTEvent)>(&mut self, mut cb: F) {
683 let held = self.held_byte.take();
684
685 if let Some(b) = held {
687 let buf = [b];
688 let event = match self.st {
689 State::OscString | State::OscEsc => VTEvent::OscData(&buf),
690 State::DcsPassthrough | State::DcsEsc => VTEvent::DcsData(&buf),
691 _ => VTEvent::Raw(&buf),
692 };
693 cb(event);
694 }
695
696 let event = match self.st {
698 State::OscString | State::OscEsc => Some(VTEvent::OscCancel),
699 State::DcsPassthrough | State::DcsEsc => Some(VTEvent::DcsCancel),
700 State::Escape => Some(VTEvent::C0(ESC)),
701 State::EscInt => {
702 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
703 None
704 } else {
705 Some(invalid!(self.priv_prefix, self.ints))
706 }
707 }
708 State::EscSs2 | State::EscSs3 => {
709 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
710 None
711 } else {
712 let c = match self.st {
713 State::EscSs2 => SS2,
714 State::EscSs3 => SS3,
715 _ => unreachable!(),
716 };
717 Some(invalid!(c))
718 }
719 }
720 _ => None,
721 };
722
723 self.reset_collectors();
724 self.st = State::Ground;
725
726 if let Some(event) = event {
727 cb(event);
728 }
729 }
730
731 fn clear_hdr_collectors(&mut self) {
736 self.ints.clear();
737 self.params.clear();
738 self.cur_param.clear();
739 self.priv_prefix = None;
740 }
741
742 fn reset_collectors(&mut self) {
743 self.clear_hdr_collectors();
744 }
745
746 #[inline]
747 fn push_cur_param(&mut self, b: u8) {
748 self.cur_param.push(b);
750 }
751
752 #[inline]
753 fn next_param(&mut self) {
754 if self.cur_param.overflowed() {
755 self.params.mark_overflowed();
756 }
757 self.params.push_param(&self.cur_param);
758 self.cur_param.clear();
759 }
760
761 fn finish_params_if_any(&mut self) {
762 if !self.cur_param.is_empty() || !self.params.is_empty() {
767 self.next_param();
768 }
769 }
770
771 fn emit_csi(&mut self, final_byte: u8) -> VTAction<'_> {
772 self.finish_params_if_any();
773
774 let privp = self.priv_prefix.take();
775 VTAction::Event(VTEvent::Csi(CSI {
776 private: privp,
777 params: ParamBuf::from_param_list(&self.params),
778 intermediates: self.ints,
779 final_byte,
780 }))
781 }
782
783 fn dcs_start(&mut self, final_byte: u8) -> VTAction<'_> {
784 self.finish_params_if_any();
785
786 let privp = self.priv_prefix.take();
787 VTAction::Event(VTEvent::DcsStart(DCS {
788 private: privp,
789 params: ParamBuf::from_param_list(&self.params),
790 intermediates: self.ints,
791 final_byte,
792 }))
793 }
794
795 #[inline(always)]
800 fn push_with(&mut self, b: u8) -> VTAction<'_> {
801 use State::*;
802
803 macro_rules! state_machine {
804 (def $c:ident $(
805 $state:ident => { $( $p:pat
806 $(if $( $if_ident:ident $( ($call_ident:ident) )? $( == $literal:literal)? $(||)? )+)?
807 => $block:expr $(,)? )* }
808 )*) => {
809 #[allow(unused, clippy::redundant_pattern)]
810 match self.st {
811 $(
812 $state => {
813 match b {
814 $(
815 $c @ state_machine!(pattern $p $(if $( ($if_ident ($($call_ident)?) ($($literal)?)) )+ )?) => $block,
816 )*
817 }
818 }
819 )*
820 }
821 };
822 (pattern $pat:pat if $( ($if_ident:ident ($($call_ident:ident)?) ($($literal:literal)?)) )+ ) => {
823 $(
824 state_machine!(pattern_if ($if_ident) ($($call_ident)?) ($($literal)?) )
825 )|+
826 };
827 (pattern $pat:pat) => {
828 $pat
829 };
830 (pattern_if ($any:ident) ($call_ident:ident) ()) => {
831 $any!()
832 };
833 (pattern_if ($if_ident:ident) () ($literal:literal)) => {
834 $literal
835 };
836 }
837
838 state_machine! {
845 def c
846 Ground => {
847 ESC => {
848 self.clear_hdr_collectors();
849 self.st = State::Escape;
850 VTAction::None
851 }
852 DEL => VTAction::Event(VTEvent::C0(DEL)),
853 c if is_c0(c) => VTAction::Event(VTEvent::C0(c)),
854 _ => VTAction::Buffer(VTEmit::Ground),
855 }
856 Escape => {
857 CAN | SUB => {
858 self.st = Ground;
859 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
860 VTAction::None
861 } else {
862 VTAction::Event(invalid!(b))
863 }
864 }
865 DEL => {
868 self.st = Ground;
869 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
870 VTAction::None
871 } else {
872 VTAction::Event(invalid!(b))
873 }
874 }
875 c if is_intermediate(c) => {
876 if self.ints.push(c) {
877 self.st = EscInt;
878 } else {
879 self.st = Ground;
880 }
881 VTAction::None
882 }
883 b'?' => {
885 self.priv_prefix = Some(b);
886 self.st = EscInt;
887 VTAction::None
888 }
889 c if is_priv_no_q(c) => {
891 self.st = Ground;
892 VTAction::Event(VTEvent::Esc(Esc {
893 intermediates: VTIntermediate::empty(),
894 private: None,
895 final_byte: b,
896 }))
897 }
898 CSI => {
899 if INTEREST & VT_PARSER_INTEREST_CSI == 0 {
900 self.st = CsiIgnore;
901 } else {
902 self.st = CsiEntry;
903 }
904 VTAction::None
905 }
906 DCS => {
907 if INTEREST & VT_PARSER_INTEREST_DCS == 0 {
908 self.st = DcsIgnore;
909 } else {
910 self.st = DcsEntry;
911 }
912 VTAction::None
913 }
914 OSC => {
915 self.st = OscString;
916 VTAction::Event(VTEvent::OscStart)
917 }
918 SS2 => {
919 self.st = EscSs2;
920 VTAction::None
921 }
922 SS3 => {
923 self.st = EscSs3;
924 VTAction::None
925 }
926 SOS | PM | APC => {
927 self.st = State::SosPmApcString;
928 VTAction::None
929 }
930 c if is_final(c) || is_digit(c) => {
931 self.st = Ground;
932 VTAction::Event(VTEvent::Esc(Esc {
933 intermediates: self.ints,
934 private: self.priv_prefix.take(),
935 final_byte: c,
936 }))
937 }
938 ESC => {
939 VTAction::Event(VTEvent::C0(ESC))
941 }
942 _ => {
943 self.st = Ground;
944 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
945 VTAction::None
946 } else {
947 VTAction::Event(invalid!(b))
948 }
949 }
950 }
951 EscInt => {
952 CAN | SUB => {
953 self.st = Ground;
954 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
955 VTAction::None
956 } else {
957 VTAction::Event(invalid!(self.priv_prefix, self.ints, b))
958 }
959 }
960 DEL => {
963 self.st = Ground;
964 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
965 VTAction::None
966 } else {
967 VTAction::Event(invalid!(self.priv_prefix, self.ints, b))
968 }
969 }
970 c if is_intermediate(c) => {
971 if !self.ints.push(c) {
972 self.st = Ground;
973 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
974 VTAction::None
975 } else {
976 VTAction::Event(invalid!(self.priv_prefix, self.ints, b))
977 }
978 } else {
979 VTAction::None
980 }
981 }
982 c if is_final(c) || is_digit(c) => {
983 self.st = Ground;
984 VTAction::Event(VTEvent::Esc(Esc {
985 intermediates: self.ints,
986 private: self.priv_prefix.take(),
987 final_byte: c,
988 }))
989 }
990 ESC => {
993 self.st = Escape;
994 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
995 VTAction::None
996 } else {
997 VTAction::Event(invalid!(self.priv_prefix, self.ints))
998 }
999 }
1000 _ => {
1001 self.st = Ground;
1002 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
1003 VTAction::None
1004 } else {
1005 VTAction::Event(invalid!(self.priv_prefix, self.ints, c))
1006 }
1007 }
1008 }
1009 EscSs2 => {
1010 CAN | SUB => {
1011 self.st = Ground;
1012 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
1013 VTAction::None
1014 } else {
1015 VTAction::Event(invalid!(SS2, b))
1016 }
1017 }
1018 ESC => {
1021 self.st = Escape;
1022 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
1023 VTAction::None
1024 } else {
1025 VTAction::Event(invalid!(SS2))
1026 }
1027 }
1028 _ => {
1029 self.st = Ground;
1030 VTAction::Event(VTEvent::Ss2(SS2 { char: c }))
1031 }
1032 }
1033 EscSs3 => {
1034 CAN | SUB => {
1035 self.st = Ground;
1036 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
1037 VTAction::None
1038 } else {
1039 VTAction::Event(invalid!(SS3, b))
1040 }
1041 }
1042 ESC => {
1045 self.st = Escape;
1046 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
1047 VTAction::None
1048 } else {
1049 VTAction::Event(invalid!(SS3))
1050 }
1051 }
1052 _ => {
1053 self.st = Ground;
1054 VTAction::Event(VTEvent::Ss3(SS3 { char: c }))
1055 }
1056 }
1057 CsiEntry => {
1058 CAN | SUB => {
1059 self.st = Ground;
1060 VTAction::None
1061 }
1062 DEL => VTAction::None,
1063 ESC => {
1064 self.st = Escape;
1065 VTAction::None
1066 }
1067 c if is_any_c0(c) => VTAction::Event(VTEvent::C0(c)),
1069 c if is_priv(c) => {
1070 self.priv_prefix = Some(c);
1071 self.st = CsiParam;
1072 VTAction::None
1073 }
1074 c if is_digit(c) => {
1075 self.push_cur_param(c);
1076 self.st = CsiParam;
1077 VTAction::None
1078 }
1079 b';' => {
1080 self.next_param();
1081 self.st = CsiParam;
1082 VTAction::None
1083 }
1084 b':' => {
1085 self.push_cur_param(b':');
1086 self.st = CsiParam;
1087 VTAction::None
1088 }
1089 c if is_intermediate(c) => {
1090 if self.ints.push(c) {
1091 self.st = CsiInt;
1092 } else {
1093 self.st = Ground;
1094 }
1095 VTAction::None
1096 }
1097 c if is_final(c) => {
1098 self.st = Ground;
1099 self.emit_csi(c)
1100 }
1101 _ => {
1102 self.st = CsiIgnore;
1103 VTAction::None
1104 }
1105 }
1106 CsiParam => {
1107 CAN | SUB => {
1108 self.st = Ground;
1109 VTAction::None
1110 }
1111 DEL => VTAction::None,
1112 ESC => {
1113 self.st = Escape;
1114 VTAction::None
1115 }
1116 c if is_any_c0(c) => VTAction::Event(VTEvent::C0(c)),
1118 c if is_digit(c) => {
1119 self.push_cur_param(c);
1120 VTAction::None
1121 }
1122 b';' => {
1123 self.next_param();
1124 VTAction::None
1125 }
1126 b':' => {
1127 self.push_cur_param(b':');
1128 VTAction::None
1129 }
1130 c if is_intermediate(c) => {
1131 if self.ints.push(c) {
1132 self.st = CsiInt;
1133 } else {
1134 self.st = Ground;
1135 }
1136 VTAction::None
1137 }
1138 c if is_final(c) => {
1139 self.st = Ground;
1140 self.emit_csi(c)
1141 }
1142 _ => {
1143 self.st = CsiIgnore;
1144 VTAction::None
1145 }
1146 }
1147 CsiInt => {
1148 CAN | SUB => {
1149 self.st = Ground;
1150 VTAction::None
1151 }
1152 DEL => VTAction::None,
1153 ESC => {
1154 self.st = Escape;
1155 VTAction::None
1156 }
1157 c if is_any_c0(c) => VTAction::Event(VTEvent::C0(c)),
1159 c if is_intermediate(c) => {
1160 if self.ints.push(c) {
1161 self.st = CsiInt;
1162 } else {
1163 self.st = Ground;
1164 }
1165 VTAction::None
1166 }
1167 c if is_final(c) => {
1168 self.st = Ground;
1169 self.emit_csi(c)
1170 }
1171 _ => {
1172 self.st = CsiIgnore;
1173 VTAction::None
1174 }
1175 }
1176 CsiIgnore => {
1177 CAN | SUB => {
1178 self.st = Ground;
1179 VTAction::None
1180 }
1181 DEL => VTAction::None,
1182 ESC => {
1183 self.st = Escape;
1184 VTAction::None
1185 }
1186 c if is_final(c) => {
1187 self.st = Ground;
1188 VTAction::None
1189 }
1190 _ => VTAction::None,
1191 }
1192 DcsEntry => {
1193 CAN | SUB => {
1194 self.st = Ground;
1195 VTAction::None
1196 }
1197 DEL => VTAction::None,
1198 ESC => {
1199 self.st = Escape;
1200 VTAction::None
1201 }
1202 c if is_priv(c) => {
1203 self.priv_prefix = Some(c);
1204 self.st = DcsParam;
1205 VTAction::None
1206 }
1207 c if is_digit(c) => {
1208 self.push_cur_param(c);
1209 self.st = DcsParam;
1210 VTAction::None
1211 }
1212 b';' => {
1213 self.next_param();
1214 self.st = DcsParam;
1215 VTAction::None
1216 }
1217 b':' => {
1218 self.st = DcsIgnore;
1219 VTAction::None
1220 }
1221 c if is_intermediate(c) => {
1222 if self.ints.push(c) {
1223 self.st = DcsInt;
1224 } else {
1225 self.st = Ground;
1226 }
1227 VTAction::None
1228 }
1229 c if is_final(c) => {
1230 self.st = DcsPassthrough;
1231 self.dcs_start(c)
1232 }
1233 _ => {
1234 self.st = DcsIgnore;
1235 VTAction::None
1236 }
1237 }
1238 DcsParam => {
1239 CAN | SUB => {
1240 self.st = Ground;
1241 VTAction::None
1242 }
1243 DEL => VTAction::None,
1244 ESC => {
1245 self.st = Escape;
1246 VTAction::None
1247 }
1248 c if is_digit(c) => {
1249 self.push_cur_param(c);
1250 VTAction::None
1251 }
1252 b';' => {
1253 self.next_param();
1254 VTAction::None
1255 }
1256 b':' => {
1257 self.st = DcsIgnore;
1258 VTAction::None
1259 }
1260 c if is_intermediate(c) => {
1261 if self.ints.push(c) {
1262 self.st = DcsInt;
1263 } else {
1264 self.st = Ground;
1265 }
1266 self.st = DcsInt;
1267 VTAction::None
1268 }
1269 c if is_final(c) => {
1270 self.st = DcsPassthrough;
1271 self.dcs_start(c)
1272 }
1273 _ => {
1274 self.st = DcsIgnore;
1275 VTAction::None
1276 }
1277 }
1278 DcsInt => {
1279 CAN | SUB => {
1280 self.st = Ground;
1281 VTAction::None
1282 }
1283 DEL => VTAction::None,
1284 ESC => {
1285 self.st = Escape;
1286 VTAction::None
1287 }
1288 c if is_intermediate(c) => {
1289 if self.ints.push(c) {
1290 self.st = DcsInt;
1291 } else {
1292 self.st = Ground;
1293 }
1294 VTAction::None
1295 }
1296 c if is_final(c) || is_digit(c) || c == b':' || c == b';' => {
1297 self.st = DcsPassthrough;
1298 self.dcs_start(c)
1299 }
1300 _ => {
1301 self.st = DcsIgnore;
1302 VTAction::None
1303 }
1304 }
1305 DcsIgnore => {
1306 CAN | SUB => {
1307 self.st = Ground;
1308 VTAction::None
1309 }
1310 DEL => VTAction::None,
1311 ESC => {
1312 self.st = DcsIgnoreEsc;
1313 VTAction::None
1314 }
1315 _ => VTAction::None,
1316 }
1317 DcsIgnoreEsc => {
1318 CAN | SUB => {
1319 self.st = Ground;
1320 VTAction::None
1321 }
1322 ST_FINAL => {
1323 self.st = Ground;
1324 VTAction::None
1325 }
1326 DEL => VTAction::None,
1327 ESC => VTAction::None,
1328 _ => {
1329 self.st = DcsIgnore;
1330 VTAction::None
1331 }
1332 }
1333 DcsPassthrough => {
1334 CAN | SUB => {
1335 self.st = Ground;
1336 VTAction::Cancel(VTEmit::Dcs)
1337 }
1338 DEL => VTAction::None,
1339 ESC => {
1340 self.st = DcsEsc;
1341 VTAction::Hold(VTEmit::Dcs)
1342 }
1343 _ => VTAction::Buffer(VTEmit::Dcs),
1344 }
1345 DcsEsc => {
1346 ST_FINAL => {
1347 self.st = Ground;
1348 VTAction::End(VTEnd::Dcs)
1349 }
1350 DEL => VTAction::None,
1351 ESC => {
1352 VTAction::Hold(VTEmit::Dcs)
1354 }
1355 _ => {
1356 self.st = DcsPassthrough;
1358 VTAction::Buffer(VTEmit::Dcs)
1359 }
1360 }
1361 OscString => {
1362 CAN | SUB => {
1363 self.st = Ground;
1364 VTAction::Cancel(VTEmit::Osc)
1365 }
1366 DEL => VTAction::None,
1367 BEL => {
1368 self.st = Ground;
1369 VTAction::End(VTEnd::Osc { used_bel: true })
1370 }
1371 ESC => {
1372 self.st = OscEsc;
1373 VTAction::Hold(VTEmit::Osc)
1374 }
1375 p if is_printable(p) => VTAction::Buffer(VTEmit::Osc),
1376 _ => VTAction::None, }
1378 OscEsc => {
1379 ST_FINAL => {
1380 self.st = Ground;
1381 VTAction::End(VTEnd::Osc { used_bel: false })
1382 } ESC => VTAction::Hold(VTEmit::Osc),
1384 DEL => VTAction::None,
1385 _ => {
1386 self.st = OscString;
1387 VTAction::Buffer(VTEmit::Osc)
1388 }
1389 }
1390 SosPmApcString => {
1391 CAN | SUB => {
1392 self.st = Ground;
1393 VTAction::None
1394 }
1395 DEL => VTAction::None,
1396 ESC => {
1397 self.st = SpaEsc;
1398 VTAction::None
1399 }
1400 _ => VTAction::None,
1401 }
1402 SpaEsc => {
1403 ST_FINAL => {
1404 self.st = Ground;
1405 VTAction::None
1406 }
1407 DEL => VTAction::None,
1408 ESC => {
1409 VTAction::None
1411 }
1412 _ => {
1413 self.st = State::SosPmApcString;
1414 VTAction::None
1415 }
1416 }
1417 }
1418 }
1419}
1420
1421#[cfg(test)]
1422mod tests {
1423 use crate::event::{MAX_PARAM_LEN, VTOwnedEvent};
1424
1425 use super::*;
1426 use pretty_assertions::assert_eq;
1427
1428 #[test]
1429 fn test_edge_cases() {
1430 let mut result = String::new();
1432 VTPushParser::decode_buffer(&[], |e| result.push_str(&format!("{e:?}\n")));
1433 assert_eq!(result.trim(), "");
1434
1435 let mut result = String::new();
1437 VTPushParser::decode_buffer(b"\x1b", |e| result.push_str(&format!("{e:?}\n")));
1438 assert_eq!(result.trim(), "");
1439
1440 let mut result = String::new();
1442 VTPushParser::decode_buffer(b"\x1b[", |e| result.push_str(&format!("{e:?}\n")));
1443 assert_eq!(result.trim(), "");
1444
1445 let mut result = String::new();
1447 VTPushParser::decode_buffer(b"\x1bP", |e| result.push_str(&format!("{e:?}\n")));
1448 assert_eq!(result.trim(), "");
1449
1450 let mut result = String::new();
1452 VTPushParser::decode_buffer(b"\x1b]", |e| result.push_str(&format!("{e:?}\n")));
1453 assert_eq!(result.trim(), "OscStart");
1454 }
1455
1456 #[test]
1457 fn test_streaming_behavior() {
1458 let mut parser = VTPushParser::new(); let mut result = String::new();
1461 let mut callback = |vt_input: VTEvent<'_>| {
1462 result.push_str(&format!("{vt_input:?}\n"));
1463 };
1464
1465 parser.feed_with(b"\x1bP1;2;3 |", &mut callback);
1467 parser.feed_with(b"data", &mut callback);
1468 parser.feed_with(b" more", &mut callback);
1469 parser.feed_with(b"\x1b\\", &mut callback);
1470
1471 assert_eq!(
1472 result.trim(),
1473 "DcsStart('1', '2', '3', ' ', |)\nDcsData('data')\nDcsData(' more')\nDcsEnd('')"
1474 );
1475 }
1476
1477 #[test]
1478 fn test_dcs_payload_passthrough() {
1479 let dcs_cases: &[(&[u8], &str)] = &[
1487 (b"\x1bPq\x1b[38:2:12:34:56m\x1b\\", "<ESC>[38:2:12:34:56m"),
1489 (b"\x1bPq\x1b[48:2:0:0:0m;xyz\x1b\\", "<ESC>[48:2:0:0:0m;xyz"),
1491 (
1493 b"\x1bP1$r\x1b[38:2:10:20:30;58:2::200:100:0m\x1b\\",
1494 "<ESC>[38:2:10:20:30;58:2::200:100:0m",
1495 ),
1496 (
1498 b"\x1bPqABC\x1b\x1bDEF\x1bXG\x1b\\",
1499 "ABC<ESC><ESC>DEF<ESC>XG",
1500 ),
1501 (b"\x1bPqDATA\x07MORE\x1b\\", "DATA<BEL>MORE"),
1503 (b"\x1bP!|\x1b[38:5:208m\x1b\\", "<ESC>[38:5:208m"),
1505 (b"\x1bP>|Hello world\x1b\\", "Hello world"),
1507 (
1509 b"\x1bPq\x1b[38:2:1:2:3m\x1b[48:5:17m\x1b\\",
1510 "<ESC>[38:2:1:2:3m<ESC>[48:5:17m",
1511 ),
1512 (
1514 b"\x1bPq\x1b[58:2::000:007:042m\x1b\\",
1515 "<ESC>[58:2::000:007:042m",
1516 ),
1517 ];
1518
1519 for (input, expected_body) in dcs_cases {
1520 let events = collect_owned_events(input);
1521
1522 let mut actual_body = Vec::new();
1524 for event in &events {
1525 match event {
1526 VTOwnedEvent::DcsData(data) | VTOwnedEvent::DcsEnd(data) => {
1527 actual_body.extend(data);
1528 }
1529 _ => {}
1530 }
1531 }
1532
1533 let actual_body = String::from_utf8(actual_body).unwrap();
1534 let actual_body = actual_body
1535 .replace("\x1b", "<ESC>")
1536 .replace("\x07", "<BEL>");
1537
1538 assert_eq!(
1539 actual_body, *expected_body,
1540 "DCS payload mismatch for input {:?}. Full events: {:#?}",
1541 input, events
1542 );
1543 }
1544 }
1545
1546 fn collect_events(input: &[u8]) -> Vec<String> {
1547 let mut out = Vec::new();
1548 let mut p = VTPushParser::new();
1549 p.feed_with(input, |ev: VTEvent| out.push(format!("{ev:?}")));
1550 out
1551 }
1552
1553 fn collect_owned_events(input: &[u8]) -> Vec<VTOwnedEvent> {
1554 let mut out = Vec::new();
1555 let mut p = VTPushParser::new();
1556 p.feed_with(input, &mut |ev: VTEvent| out.push(ev.to_owned()));
1557 out
1558 }
1559
1560 #[test]
1561 fn dcs_esc_esc_del() {
1562 let ev = collect_events(b"\x1bP1;2;3|\x1b\x1b\x7fdata\x1b\\");
1563 eprintln!("{ev:?}");
1564 }
1565
1566 #[test]
1567 fn osc_bel() {
1568 let mut parser = VTPushParser::new();
1569 let mut output = String::new();
1570 for b in b"\x1b]X\x07" {
1571 parser.feed_with(&[*b], &mut |ev: VTEvent| {
1572 output.push_str(&format!("{ev:?}\n"));
1573 });
1574 }
1575 assert_eq!(output.trim(), "OscStart\nOscData('X')\nOscEnd('')");
1576 }
1577
1578 #[test]
1579 fn dcs_header_with_colon_is_ignored_case1() {
1580 let ev = collect_events(b"\x1bP1:2qHELLO\x1b\\");
1582 assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1584 }
1585
1586 #[test]
1587 fn dcs_header_with_colon_is_ignored_case2() {
1588 let ev = collect_events(b"\x1bP:1qDATA\x1b\\");
1590 assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1591 }
1592
1593 #[test]
1594 fn dcs_header_with_colon_is_ignored_case3() {
1595 let ev = collect_events(b"\x1bP12:34!qPAYLOAD\x1b\\");
1597 assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1598 }
1599
1600 #[test]
1601 fn osc_aborted_by_can_mid_body() {
1602 let mut s = Vec::new();
1604 s.extend_from_slice(b"\x1b]0;Title");
1605 s.push(CAN);
1606 s.extend_from_slice(b"more\x07");
1607
1608 let ev = collect_debug(&s);
1609
1610 assert!(ev.iter().any(|e| e.starts_with("OscStart")), "{ev:#?}");
1615 assert!(!ev.iter().any(|e| e.starts_with("OscData")), "{ev:#?}");
1616 assert!(!ev.iter().any(|e| e.starts_with("OscEnd")), "{ev:#?}");
1617 }
1618
1619 #[test]
1620 fn osc_aborted_by_sub_before_terminator() {
1621 let mut s = Vec::new();
1622 s.extend_from_slice(b"\x1b]52;c;YWJjZA==");
1623 s.push(SUB); s.extend_from_slice(b"\x1b\\"); let ev = collect_debug(&s);
1627 assert!(ev.iter().any(|e| e.starts_with("OscStart")), "{ev:#?}");
1631 assert!(!ev.iter().any(|e| e.starts_with("OscData")), "{ev:#?}");
1632 assert!(!ev.iter().any(|e| e.starts_with("OscEnd")), "{ev:#?}");
1633 }
1634
1635 fn collect_debug(input: &[u8]) -> Vec<String> {
1637 let mut out = Vec::new();
1638 let mut p = VTPushParser::new();
1639 p.feed_with(input, |ev: VTEvent| out.push(format!("{ev:?}")));
1640 out
1641 }
1642
1643 #[test]
1644 fn dcs_aborted_by_can_before_body() {
1645 let mut s = Vec::new();
1647 s.extend_from_slice(b"\x1bPq"); s.push(CAN);
1649 s.extend_from_slice(b"IGNORED\x1b\\"); let ev = collect_debug(&s);
1652
1653 assert_eq!(ev.len(), 4, "{ev:#?}");
1654 assert_eq!(ev[0], "DcsStart('', q)");
1655 assert_eq!(ev[1], "DcsCancel");
1656 assert_eq!(ev[2], "Raw('IGNORED')");
1657 assert_eq!(ev[3], "Esc('', \\)");
1658 }
1659
1660 #[test]
1661 fn dcs_aborted_by_can_mid_body() {
1662 let mut s = Vec::new();
1664 s.extend_from_slice(b"\x1bPqABC");
1665 s.push(CAN);
1666 s.extend_from_slice(b"MORE\x1b\\"); let ev = collect_debug(&s);
1669
1670 assert_eq!(ev.len(), 4, "{ev:#?}");
1671 assert_eq!(ev[0], "DcsStart('', q)");
1672 assert_eq!(ev[1], "DcsCancel");
1673 assert_eq!(ev[2], "Raw('MORE')");
1674 assert_eq!(ev[3], "Esc('', \\)");
1675 }
1676
1677 #[test]
1680 fn spa_aborted_by_can_is_ignored() {
1681 let mut s = Vec::new();
1683 s.extend_from_slice(b"\x1b_hello");
1684 s.push(CAN);
1685 s.extend_from_slice(b"world\x1b\\");
1686
1687 let ev = collect_debug(&s);
1688 assert_eq!(ev.len(), 2, "{ev:#?}");
1689 assert_eq!(ev[0], "Raw('world')");
1690 assert_eq!(ev[1], "Esc('', \\)");
1691 }
1692
1693 #[test]
1694 fn spa_sub_aborts_too() {
1695 let mut s = Vec::new();
1696 s.extend_from_slice(b"\x1bXhello");
1697 s.push(SUB);
1698 s.extend_from_slice(b"world\x1b\\");
1699 let ev = collect_debug(&s);
1700 assert_eq!(ev.len(), 2, "{ev:#?}");
1701 assert_eq!(ev[0], "Raw('world')");
1702 assert_eq!(ev[1], "Esc('', \\)");
1703 }
1704
1705 #[test]
1708 fn can_in_ground_is_c0() {
1709 let mut s = Vec::new();
1710 s.extend_from_slice(b"abc");
1711 s.push(CAN);
1712 s.extend_from_slice(b"def");
1713 let ev = collect_debug(&s);
1714 assert_eq!(ev.len(), 3, "{ev:#?}");
1716 assert_eq!(ev[0], "Raw('abc')");
1717 assert_eq!(ev[1], "C0(18)");
1718 assert_eq!(ev[2], "Raw('def')");
1719 }
1720
1721 #[test]
1724 fn three_byte_sequences_capturable() {
1725 let mut bytes = vec![];
1726 for i in 0..=0xFFFFFF_u32 {
1727 bytes.clear();
1728 let test_bytes = i.to_le_bytes();
1729 let test_bytes = &test_bytes[..3];
1730 if test_bytes.iter().any(|b| b == &0) {
1731 continue;
1732 }
1733 if test_bytes[0] == 0x1b && matches!(test_bytes[1], CSI | DCS | OSC | APC | PM | SOS) {
1734 continue;
1735 }
1736 if test_bytes[1] == 0x1b && matches!(test_bytes[2], CSI | DCS | OSC | APC | PM | SOS) {
1737 continue;
1738 }
1739
1740 let mut parser = VTPushParser::<VT_PARSER_INTEREST_ALL>::new_with();
1741 parser.feed_with(test_bytes, |event: VTEvent| {
1742 let mut chunk = [0_u8; 3];
1743 let b = event.encode(&mut chunk).unwrap_or_else(|_| {
1744 panic!("Failed to encode event {test_bytes:02X?} -> {event:?}")
1745 });
1746 bytes.extend_from_slice(&chunk[..b]);
1747 });
1748 if let Some(event) = parser.idle() {
1749 let mut chunk = [0_u8; 3];
1750 let b = event.encode(&mut chunk).unwrap_or_else(|_| {
1751 panic!("Failed to encode event {test_bytes:02X?} -> {event:?}")
1752 });
1753 bytes.extend_from_slice(&chunk[..b]);
1754 }
1755
1756 if bytes.len() != 3 || bytes != test_bytes {
1757 eprintln!("Failed to parse:");
1758 parser.feed_with(test_bytes, |event: VTEvent| {
1759 eprintln!("{event:?}");
1760 });
1761 if let Some(event) = parser.idle() {
1762 eprintln!("{event:?}");
1763 }
1764 assert_eq!(bytes, test_bytes, "{test_bytes:02X?} -> {bytes:02X?}");
1765 }
1766 }
1767 }
1768
1769 #[test]
1772 fn finish_after_esc_emits_c0_esc() {
1773 let mut parser = VTPushParser::new();
1774 let mut out = Vec::new();
1775 parser.feed_with(b"abc\x1b", |ev: VTEvent| out.push(format!("{ev:?}")));
1776 assert_eq!(out, ["Raw('abc')"]);
1777
1778 parser.finish(|ev: VTEvent| out.push(format!("{ev:?}")));
1779 assert_eq!(out, ["Raw('abc')", "C0(1b)"]);
1780 assert!(parser.is_ground());
1781 }
1782
1783 #[test]
1784 fn finish_mid_dcs_cancels() {
1785 let mut parser = VTPushParser::new();
1786 let mut out = Vec::new();
1787 parser.feed_with(b"\x1bPqdata\x1b", |ev: VTEvent| out.push(format!("{ev:?}")));
1788 parser.finish(|ev: VTEvent| out.push(format!("{ev:?}")));
1789 assert_eq!(
1790 out,
1791 [
1792 "DcsStart('', q)",
1793 "DcsData('data')",
1794 "DcsData('<ESC>')",
1795 "DcsCancel"
1796 ]
1797 );
1798 assert!(parser.is_ground());
1799 }
1800
1801 #[test]
1802 fn finish_mid_osc_cancels() {
1803 let mut parser = VTPushParser::new();
1804 let mut out = Vec::new();
1805 parser.feed_with(b"\x1b]0;title\x1b", |ev: VTEvent| {
1806 out.push(format!("{ev:?}"))
1807 });
1808 parser.finish(|ev: VTEvent| out.push(format!("{ev:?}")));
1809 assert_eq!(
1810 out,
1811 [
1812 "OscStart",
1813 "OscData('0;title')",
1814 "OscData('<ESC>')",
1815 "OscCancel"
1816 ]
1817 );
1818 assert!(parser.is_ground());
1819 }
1820
1821 #[test]
1822 fn finish_in_csi_header_drops_params() {
1823 let mut parser = VTPushParser::new();
1824 let mut out = Vec::new();
1825 parser.feed_with(b"\x1b[1;2;3", |ev: VTEvent| out.push(format!("{ev:?}")));
1826 parser.finish(|ev: VTEvent| out.push(format!("{ev:?}")));
1827 assert!(out.is_empty(), "{out:#?}");
1828 assert!(parser.is_ground());
1829
1830 parser.feed_with(b"\x1b[0m", |ev: VTEvent| out.push(format!("{ev:?}")));
1832 assert_eq!(out, ["Csi('0', '', 'm')"], "{out:#?}");
1833 }
1834
1835 #[test]
1836 fn finish_is_idempotent() {
1837 let mut parser = VTPushParser::new();
1838 let mut count = 0;
1839 parser.feed_with(b"\x1b]0;x\x1b", |_ev: VTEvent| {});
1840 parser.finish(|_ev: VTEvent| count += 1);
1841 parser.finish(|_ev: VTEvent| count += 1);
1842 assert_eq!(count, 2);
1844 }
1845
1846 #[test]
1847 fn param_clamping_at_max_params() {
1848 let mut input = b"\x1b[".to_vec();
1851 for i in 0..200 {
1852 if i > 0 {
1853 input.push(b';');
1854 }
1855 input.extend_from_slice(i.to_string().as_bytes());
1856 }
1857 input.push(b'm');
1858 input.extend_from_slice(b"after");
1859
1860 let ev = collect_events(&input);
1861 let csi = ev
1862 .iter()
1863 .find(|e| e.starts_with("Csi("))
1864 .expect("no Csi event");
1865 assert!(csi.contains("'127'"), "param 127 missing: {csi}");
1866 assert!(
1867 !csi.contains("'128'"),
1868 "param 128 should have been clamped: {csi}"
1869 );
1870 assert_eq!(ev.last().unwrap(), "Raw('after')");
1871
1872 let mut parser = VTPushParser::new();
1874 let mut overflowed = false;
1875 parser.feed_with(&input, &mut |ev: VTEvent| {
1876 if let VTEvent::Csi(csi) = ev {
1877 overflowed = csi.params.overflowed();
1878 }
1879 });
1880 assert!(overflowed, "params should report overflow");
1881
1882 let mut parser = VTPushParser::new();
1884 parser.feed_with(&input, |_ev: VTEvent| {});
1885 assert!(parser.is_ground());
1886 }
1887
1888 #[test]
1889 fn param_clamping_per_param_length() {
1890 let mut input = b"\x1b[".to_vec();
1892 input.extend_from_slice(&[b'7'; 40]);
1893 input.push(b'm');
1894
1895 let mut parser = VTPushParser::new();
1896 let mut params: Vec<Vec<u8>> = Vec::new();
1897 parser.feed_with(&input, &mut |ev: VTEvent| {
1898 if let VTEvent::Csi(csi) = ev {
1899 for p in &csi.params {
1900 params.push(p.to_vec());
1901 }
1902 }
1903 });
1904 assert_eq!(params.len(), 1);
1905 assert_eq!(params[0].len(), MAX_PARAM_LEN);
1906 assert!(params[0].iter().all(|&b| b == b'7'));
1907
1908 let mut parser = VTPushParser::new();
1910 let mut overflowed = false;
1911 parser.feed_with(&input, &mut |ev: VTEvent| {
1912 if let VTEvent::Csi(csi) = ev {
1913 overflowed = csi.params.overflowed();
1914 }
1915 });
1916 assert!(overflowed, "params should report overflow");
1917 }
1918
1919 #[test]
1920 fn param_clamping_byte_by_byte() {
1921 let mut input = b"\x1b[".to_vec();
1924 for i in 0..200 {
1925 if i > 0 {
1926 input.push(b';');
1927 }
1928 input.extend_from_slice(i.to_string().as_bytes());
1929 }
1930 input.push(b'm');
1931
1932 let mut parser = VTPushParser::new();
1933 let mut count = 0;
1934 let mut total_len = 0;
1935 let mut overflowed = false;
1936 for b in &input {
1937 parser.feed_with(&[*b], &mut |ev: VTEvent| {
1938 if let VTEvent::Csi(csi) = ev {
1939 count = csi.params.len();
1940 total_len = csi.params.byte_len();
1941 overflowed = csi.params.overflowed();
1942 }
1943 });
1944 }
1945 assert_eq!(count, 128);
1946 let digits = 10 + 90 * 2 + 28 * 3;
1949 assert_eq!(total_len, digits + 127);
1950 assert!(overflowed, "params should report overflow");
1951 }
1952
1953 #[test]
1954 fn param_list_capacity_is_inline_for_corpus() {
1955 let mut input = b"\x1b[?25;".to_vec();
1957 for i in 1..=100 {
1958 if i > 1 {
1959 input.push(b';');
1960 }
1961 input.extend_from_slice(i.to_string().as_bytes());
1962 }
1963 input.push(b'm');
1964
1965 let mut parser = VTPushParser::new();
1966 let mut last: Option<(usize, usize)> = None;
1967 parser.feed_with(&input, &mut |ev: VTEvent| {
1968 if let VTEvent::Csi(csi) = ev {
1969 last = Some((csi.params.len(), csi.params.byte_len()));
1970 }
1971 });
1972 let (count, byte_len) = last.expect("no Csi event");
1973 assert_eq!(count, 101); assert_eq!(byte_len, 2 + 9 + 180 + 3 + 100);
1977
1978 let mut parser = VTPushParser::new();
1980 let mut overflowed = true;
1981 parser.feed_with(&input, &mut |ev: VTEvent| {
1982 if let VTEvent::Csi(csi) = ev {
1983 overflowed = csi.params.overflowed();
1984 }
1985 });
1986 assert!(!overflowed, "params should not report overflow");
1987 }
1988}