1pub mod ascii;
2pub mod capture;
3pub mod event;
4pub mod iter;
5pub mod signature;
6
7use smallvec::SmallVec;
8
9use ascii::AsciiControl;
10use event::{CSI, DCS, Esc, EscInvalid, SS2, SS3, VTEvent, VTIntermediate};
11
12const ESC: u8 = AsciiControl::Esc as _;
13const BEL: u8 = AsciiControl::Bel as _;
14const DEL: u8 = AsciiControl::Del as _;
15const CAN: u8 = AsciiControl::Can as _;
16const SUB: u8 = AsciiControl::Sub as _;
17const CSI: u8 = b'[';
18const OSC: u8 = b']';
19const SS2: u8 = b'N';
20const SS3: u8 = b'O';
21const DCS: u8 = b'P';
22const APC: u8 = b'_';
23const PM: u8 = b'^';
24const SOS: u8 = b'X';
25const ST_FINAL: u8 = b'\\';
26
27pub use signature::VTEscapeSignature;
29
30use crate::event::{Param, ParamBuf, Params};
31
32enum VTAction<'a> {
34 None,
37 Event(VTEvent<'a>),
40 End(VTEnd),
42 Buffer(VTEmit),
45 Hold(VTEmit),
48 Cancel(VTEmit),
50}
51
52#[derive(Debug, Copy, Clone, PartialEq, Eq)]
53enum VTEmit {
54 Ground,
56 Dcs,
58 Osc,
60}
61
62#[derive(Debug, Copy, Clone, PartialEq, Eq)]
63enum VTEnd {
64 Dcs,
66 Osc { used_bel: bool },
68}
69
70#[inline]
71const fn is_c0(b: u8) -> bool {
72 b <= 0x1F && b != b'\r' && b != b'\n' && b != b'\t'
74}
75#[inline]
76fn is_printable(b: u8) -> bool {
77 (0x20..=0x7E).contains(&b)
78}
79#[inline]
80fn is_intermediate(b: u8) -> bool {
81 (0x20..=0x2F).contains(&b)
82}
83#[inline]
84const fn is_final(b: u8) -> bool {
85 b >= 0x40 && b <= 0x7E
86}
87#[inline]
88fn is_digit(b: u8) -> bool {
89 (b'0'..=b'9').contains(&b)
90}
91#[inline]
92fn is_priv(b: u8) -> bool {
93 matches!(b, b'<' | b'=' | b'>' | b'?')
94}
95
96macro_rules! byte_predicate {
97 (|$p:ident| $body:block) => {{
98 let mut out: [bool; 256] = [false; 256];
99 let mut i = 0;
100 while i < 256 {
101 let $p: u8 = i as u8;
102 out[i] = $body;
103 i += 1;
104 }
105 out
106 }};
107}
108
109const ENDS_CSI: [bool; 256] =
110 byte_predicate!(|b| { is_final(b) || b == ESC || b == CAN || b == SUB });
111
112const ENDS_GROUND: [bool; 256] = byte_predicate!(|b| { is_c0(b) || b == DEL });
113
114#[derive(Debug, Copy, Clone, PartialEq, Eq)]
115enum State {
116 Ground,
117 Escape,
118 EscInt,
119 EscSs2,
120 EscSs3,
121 CsiEntry,
122 CsiParam,
123 CsiInt,
124 CsiIgnore,
125 DcsEntry,
126 DcsParam,
127 DcsInt,
128 DcsIgnore,
129 DcsIgnoreEsc,
130 DcsPassthrough,
131 DcsEsc,
132 OscString,
133 OscEsc,
134 SosPmApcString,
135 SpaEsc,
136}
137
138pub const VT_PARSER_INTEREST_NONE: u8 = 0;
139pub const VT_PARSER_INTEREST_CSI: u8 = 1 << 0;
141pub const VT_PARSER_INTEREST_DCS: u8 = 1 << 1;
143pub const VT_PARSER_INTEREST_OSC: u8 = 1 << 2;
145pub const VT_PARSER_INTEREST_ESCAPE_RECOVERY: u8 = 1 << 4;
147pub const VT_PARSER_INTEREST_OTHER: u8 = 1 << 5;
149
150pub const VT_PARSER_INTEREST_ALL: u8 = VT_PARSER_INTEREST_CSI
152 | VT_PARSER_INTEREST_DCS
153 | VT_PARSER_INTEREST_OSC
154 | VT_PARSER_INTEREST_ESCAPE_RECOVERY
155 | VT_PARSER_INTEREST_OTHER;
156
157pub const VT_PARSER_INTEREST_DEFAULT: u8 = VT_PARSER_INTEREST_CSI
159 | VT_PARSER_INTEREST_DCS
160 | VT_PARSER_INTEREST_OSC
161 | VT_PARSER_INTEREST_OTHER;
162
163#[must_use]
164trait MaybeAbortable {
165 fn abort(self) -> bool;
166}
167
168impl MaybeAbortable for bool {
169 #[inline(always)]
170 fn abort(self) -> bool {
171 !self
172 }
173}
174
175impl MaybeAbortable for () {
176 #[inline(always)]
177 fn abort(self) -> bool {
178 false
179 }
180}
181
182pub struct VTPushParser<const INTEREST: u8 = VT_PARSER_INTEREST_DEFAULT> {
183 st: State,
184
185 ints: VTIntermediate,
187 params: Params,
188 cur_param: Param,
189 priv_prefix: Option<u8>,
190 held_byte: Option<u8>,
191}
192
193impl VTPushParser {
194 pub const fn new() -> Self {
195 VTPushParser::new_with()
196 }
197
198 pub fn decode_buffer<'a>(input: &'a [u8], mut cb: impl for<'b> FnMut(VTEvent<'b>)) {
200 let mut parser = VTPushParser::new();
201 parser.feed_with(input, &mut cb);
202 parser.finish(&mut cb);
203 }
204
205 pub const fn new_with_interest<const INTEREST: u8>() -> VTPushParser<INTEREST> {
206 VTPushParser::new_with()
207 }
208}
209
210macro_rules! invalid {
212 ($self:ident .ints, $b:expr) => {
213 if $self.ints.len() == 1 {
214 VTEvent::EscInvalid(EscInvalid::Two($self.ints.data[0], $b))
215 } else {
216 VTEvent::EscInvalid(EscInvalid::Three(
217 $self.ints.data[0],
218 $self.ints.data[1],
219 $b,
220 ))
221 }
222 };
223 ($self:ident .ints) => {
224 if $self.ints.len() == 1 {
225 VTEvent::EscInvalid(EscInvalid::One($self.ints.data[0]))
226 } else {
227 VTEvent::EscInvalid(EscInvalid::Two($self.ints.data[0], $self.ints.data[1]))
228 }
229 };
230 ($a:expr) => {
231 VTEvent::EscInvalid(EscInvalid::One($a))
232 };
233 ($a:expr, $b:expr) => {
234 VTEvent::EscInvalid(EscInvalid::Two($a, $b))
235 };
236}
237
238impl<const INTEREST: u8> VTPushParser<INTEREST> {
239 const fn new_with() -> Self {
240 Self {
241 st: State::Ground,
242 ints: VTIntermediate::empty(),
243 params: SmallVec::new_const(),
244 cur_param: SmallVec::new_const(),
245 priv_prefix: None,
246 held_byte: None,
247 }
248 }
249
250 #[inline]
262 pub fn feed_with<'this, 'input, F: for<'any> FnMut(VTEvent<'any>)>(
263 &'this mut self,
264 input: &'input [u8],
265 cb: &mut F,
266 ) {
267 self.feed_with_internal(input, cb);
268 }
269
270 #[inline]
283 pub fn feed_with_abortable<'this, 'input, F: for<'any> FnMut(VTEvent<'any>) -> bool>(
284 &'this mut self,
285 input: &'input [u8],
286 cb: &mut F,
287 ) -> usize {
288 self.feed_with_internal(input, cb)
289 }
290
291 #[inline(always)]
292 fn feed_with_internal<
293 'this,
294 'input,
295 R: MaybeAbortable,
296 F: for<'any> FnMut(VTEvent<'any>) -> R,
297 >(
298 &'this mut self,
299 input: &'input [u8],
300 cb: &mut F,
301 ) -> usize {
302 if input.is_empty() {
303 return 0;
304 }
305
306 #[derive(Debug)]
307 struct FeedState {
308 buffer_idx: usize,
309 current_emit: Option<VTEmit>,
310 hold: bool,
311 }
312
313 let mut state = FeedState {
314 buffer_idx: 0,
315 current_emit: None,
316 hold: self.held_byte.is_some(),
317 };
318
319 macro_rules! emit {
320 ($state:ident, $i:expr, $cb:expr, $end:expr, $used_bel:expr) => {
321 let hold = std::mem::take(&mut $state.hold);
322 if let Some(emit) = $state.current_emit.take() {
323 let i = $i;
324 let range = $state.buffer_idx..(i - hold as usize);
325 if $end {
326 if match emit {
327 VTEmit::Ground => unreachable!(),
328 VTEmit::Dcs => $cb(VTEvent::DcsEnd(&input[range])),
329 VTEmit::Osc => $cb(VTEvent::OscEnd {
330 data: &input[range],
331 used_bel: $used_bel,
332 }),
333 }
334 .abort()
335 {
336 return i + 1;
337 }
338 } else if range.len() > 0 {
339 if match emit {
340 VTEmit::Ground => $cb(VTEvent::Raw(&input[range])),
341 VTEmit::Dcs => $cb(VTEvent::DcsData(&input[range])),
342 VTEmit::Osc => $cb(VTEvent::OscData(&input[range])),
343 }
344 .abort()
345 {
346 return i + 1;
347 }
348 }
349 }
350 };
351 }
352
353 let mut held_byte = self.held_byte.take();
354 let mut i = 0;
355
356 while i < input.len() {
357 if self.st == State::Ground {
359 let start = i;
360 loop {
361 if i >= input.len() {
362 cb(VTEvent::Raw(&input[start..]));
363 return input.len();
364 }
365 if ENDS_GROUND[input[i] as usize] {
366 break;
367 }
368 i += 1;
369 }
370
371 if start != i {
372 if cb(VTEvent::Raw(&input[start..i])).abort() {
373 return i;
374 }
375 }
376
377 if input[i] == ESC {
378 self.clear_hdr_collectors();
379 self.st = State::Escape;
380 i = i + 1;
381 continue;
382 }
383 }
384
385 if self.st == State::CsiIgnore {
387 loop {
388 if i >= input.len() {
389 return input.len();
390 }
391 if ENDS_CSI[input[i] as usize] {
392 break;
393 }
394 i += 1;
395 }
396
397 if input[i] == ESC {
398 self.st = State::Escape;
399 } else {
400 self.st = State::Ground;
401 }
402 i += 1;
403 continue;
404 }
405
406 let action = self.push_with(input[i]);
407
408 match action {
409 VTAction::None => {
410 if let Some(emit) = state.current_emit {
411 let range = state.buffer_idx..(i - state.hold as usize);
413 if !range.is_empty() {
414 if match emit {
415 VTEmit::Ground => cb(VTEvent::Raw(&input[range])),
416 VTEmit::Dcs => cb(VTEvent::DcsData(&input[range])),
417 VTEmit::Osc => cb(VTEvent::OscData(&input[range])),
418 }
419 .abort()
420 {
421 if state.hold {
422 self.held_byte = Some(0x1b);
423 }
424 return i + 1;
425 }
426 }
427 if state.hold {
428 held_byte = Some(0x1b);
429 }
430 state.current_emit = None;
431 }
432 }
433 VTAction::Event(e) => {
434 if cb(e).abort() {
435 return i + 1;
436 }
437 }
438 VTAction::End(VTEnd::Dcs) => {
439 held_byte = None;
440 emit!(state, i, cb, true, false);
441 }
442 VTAction::End(VTEnd::Osc { used_bel }) => {
443 held_byte = None;
444 emit!(state, i, cb, true, used_bel);
445 }
446 VTAction::Buffer(emit) | VTAction::Hold(emit) => {
447 if state.current_emit.is_none() {
448 if let Some(h) = held_byte.take() {
449 if match emit {
450 VTEmit::Ground => cb(VTEvent::Raw(&[h])),
451 VTEmit::Dcs => cb(VTEvent::DcsData(&[h])),
452 VTEmit::Osc => cb(VTEvent::OscData(&[h])),
453 }
454 .abort()
455 {
456 if matches!(action, VTAction::Hold(_)) {
457 self.held_byte = Some(0x1b);
458 return 1;
459 }
460 return 0;
461 }
462 }
463 }
464
465 debug_assert!(state.current_emit.is_none() || state.current_emit == Some(emit));
466
467 state.hold = matches!(action, VTAction::Hold(_));
468 if state.current_emit.is_none() {
469 state.buffer_idx = i;
470 state.current_emit = Some(emit);
471 }
472 }
473 VTAction::Cancel(emit) => {
474 state.current_emit = None;
475 state.hold = false;
476 if match emit {
477 VTEmit::Ground => unreachable!(),
478 VTEmit::Dcs => cb(VTEvent::DcsCancel),
479 VTEmit::Osc => cb(VTEvent::OscCancel),
480 }
481 .abort()
482 {
483 return i + 1;
484 }
485 }
486 };
487 i += 1;
488 }
489
490 if state.hold {
492 self.held_byte = Some(0x1b);
493 }
494
495 if let Some(emit) = state.current_emit.take() {
496 let range = &input[state.buffer_idx..input.len() - state.hold as usize];
497 if !range.is_empty() {
498 match emit {
499 VTEmit::Ground => cb(VTEvent::Raw(range)),
500 VTEmit::Dcs => cb(VTEvent::DcsData(range)),
501 VTEmit::Osc => cb(VTEvent::OscData(range)),
502 };
503 }
504 };
505
506 input.len()
508 }
509
510 pub fn is_ground(&self) -> bool {
512 self.st == State::Ground
513 }
514
515 pub fn idle(&mut self) -> Option<VTEvent<'static>> {
519 match self.st {
520 State::Escape => {
521 self.st = State::Ground;
522 Some(VTEvent::C0(ESC))
523 }
524 State::EscInt => {
525 self.st = State::Ground;
526 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
527 None
528 } else {
529 Some(invalid!(self.ints))
530 }
531 }
532 State::EscSs2 | State::EscSs3 => {
533 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
534 self.st = State::Ground;
535 None
536 } else {
537 let c = match self.st {
538 State::EscSs2 => SS2,
539 State::EscSs3 => SS3,
540 _ => unreachable!(),
541 };
542 self.st = State::Ground;
543 Some(invalid!(c))
544 }
545 }
546 _ => None,
547 }
548 }
549
550 fn push_with(&mut self, b: u8) -> VTAction {
551 use State::*;
552 match self.st {
553 Ground => self.on_ground(b),
554 Escape => self.on_escape(b),
555 EscInt => self.on_esc_int(b),
556 EscSs2 => self.on_esc_ss2(b),
557 EscSs3 => self.on_esc_ss3(b),
558
559 CsiEntry => self.on_csi_entry(b),
560 CsiParam => self.on_csi_param(b),
561 CsiInt => self.on_csi_int(b),
562 CsiIgnore => self.on_csi_ignore(b),
563
564 DcsEntry => self.on_dcs_entry(b),
565 DcsParam => self.on_dcs_param(b),
566 DcsInt => self.on_dcs_int(b),
567 DcsIgnore => self.on_dcs_ignore(b),
568 DcsIgnoreEsc => self.on_dcs_ignore_esc(b),
569 DcsPassthrough => self.on_dcs_pass(b),
570 DcsEsc => self.on_dcs_esc(b),
571
572 OscString => self.on_osc_string(b),
573 OscEsc => self.on_osc_esc(b),
574
575 SosPmApcString => self.on_spa_string(b),
576 SpaEsc => self.on_spa_esc(b),
577 }
578 }
579
580 pub fn finish<F: FnMut(VTEvent)>(&mut self, _cb: &mut F) {
581 self.reset_collectors();
582 self.st = State::Ground;
583
584 }
586
587 fn clear_hdr_collectors(&mut self) {
592 self.ints.clear();
593 self.params.clear();
594 self.cur_param.clear();
595 self.priv_prefix = None;
596 }
597
598 fn reset_collectors(&mut self) {
599 self.clear_hdr_collectors();
600 }
601
602 fn next_param(&mut self) {
603 self.params.push(std::mem::take(&mut self.cur_param));
604 }
605
606 fn finish_params_if_any(&mut self) {
607 if !self.cur_param.is_empty() || !self.params.is_empty() {
608 self.next_param();
609 }
610 }
611
612 fn emit_csi(&mut self, final_byte: u8) -> VTAction {
613 self.finish_params_if_any();
614
615 let mut borrowed: SmallVec<[&[u8]; 4]> = SmallVec::new();
617 borrowed.extend(self.params.iter().map(|v| v.as_slice()));
618
619 let privp = self.priv_prefix.take();
620 VTAction::Event(VTEvent::Csi(CSI {
621 private: privp,
622 params: ParamBuf {
623 params: &self.params,
624 },
625 intermediates: self.ints,
626 final_byte,
627 }))
628 }
629
630 fn dcs_start(&mut self, final_byte: u8) -> VTAction {
631 self.finish_params_if_any();
632
633 let privp = self.priv_prefix.take();
634 VTAction::Event(VTEvent::DcsStart(DCS {
635 private: privp,
636 params: ParamBuf {
637 params: &self.params,
638 },
639 intermediates: self.ints,
640 final_byte,
641 }))
642 }
643
644 fn on_ground(&mut self, b: u8) -> VTAction {
649 match b {
650 ESC => {
651 self.clear_hdr_collectors();
652 self.st = State::Escape;
653 VTAction::None
654 }
655 DEL => VTAction::Event(VTEvent::C0(DEL)),
656 c if is_c0(c) => VTAction::Event(VTEvent::C0(c)),
657 p if is_printable(p) => VTAction::Buffer(VTEmit::Ground),
658 _ => VTAction::Buffer(VTEmit::Ground), }
660 }
661
662 fn on_escape(&mut self, b: u8) -> VTAction {
663 use State::*;
664 match b {
665 CAN | SUB => {
666 self.st = Ground;
667 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
668 VTAction::None
669 } else {
670 VTAction::Event(invalid!(b))
671 }
672 }
673 DEL => {
676 self.st = Ground;
677 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
678 VTAction::None
679 } else {
680 VTAction::Event(invalid!(b))
681 }
682 }
683 c if is_intermediate(c) => {
684 if self.ints.push(c) {
685 self.st = EscInt;
686 } else {
687 self.st = Ground;
688 }
689 VTAction::None
690 }
691 CSI => {
692 if INTEREST & VT_PARSER_INTEREST_CSI == 0 {
693 self.st = CsiIgnore;
694 } else {
695 self.st = CsiEntry;
696 }
697 VTAction::None
698 }
699 DCS => {
700 if INTEREST & VT_PARSER_INTEREST_DCS == 0 {
701 self.st = DcsIgnore;
702 } else {
703 self.st = DcsEntry;
704 }
705 VTAction::None
706 }
707 OSC => {
708 self.st = OscString;
709 VTAction::Event(VTEvent::OscStart)
710 }
711 SS2 => {
712 self.st = EscSs2;
713 VTAction::None
714 }
715 SS3 => {
716 self.st = EscSs3;
717 VTAction::None
718 }
719 SOS | PM | APC => {
720 self.st = State::SosPmApcString;
721 VTAction::None
722 }
723 c if is_final(c) || is_digit(c) => {
724 self.st = Ground;
725 VTAction::Event(VTEvent::Esc(Esc {
726 intermediates: self.ints,
727 final_byte: c,
728 }))
729 }
730 ESC => {
731 VTAction::Event(VTEvent::C0(ESC))
733 }
734 _ => {
735 self.st = Ground;
736 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
737 VTAction::None
738 } else {
739 VTAction::Event(invalid!(b))
740 }
741 }
742 }
743 }
744
745 fn on_esc_int(&mut self, b: u8) -> VTAction {
746 use State::*;
747 match b {
748 CAN | SUB => {
749 self.st = Ground;
750 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
751 VTAction::None
752 } else {
753 VTAction::Event(invalid!(self.ints, b))
754 }
755 }
756 DEL => {
759 self.st = Ground;
760 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
761 VTAction::None
762 } else {
763 VTAction::Event(invalid!(self.ints, b))
764 }
765 }
766 c if is_intermediate(c) => {
767 if !self.ints.push(c) {
768 self.st = Ground;
769 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
770 VTAction::None
771 } else {
772 VTAction::Event(invalid!(self.ints, b))
773 }
774 } else {
775 VTAction::None
776 }
777 }
778 c if is_final(c) || is_digit(c) => {
779 self.st = Ground;
780 VTAction::Event(VTEvent::Esc(Esc {
781 intermediates: self.ints,
782 final_byte: c,
783 }))
784 }
785 ESC => {
788 self.st = Escape;
789 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
790 VTAction::None
791 } else {
792 VTAction::Event(invalid!(self.ints))
793 }
794 }
795 c => {
796 self.st = Ground;
797 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
798 VTAction::None
799 } else {
800 VTAction::Event(invalid!(self.ints, c))
801 }
802 }
803 }
804 }
805
806 fn on_esc_ss2(&mut self, b: u8) -> VTAction {
807 use State::*;
808 self.st = Ground;
809 match b {
810 CAN | SUB => {
811 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
812 VTAction::None
813 } else {
814 VTAction::Event(invalid!(SS2, b))
815 }
816 }
817 ESC => {
820 self.st = Escape;
821 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
822 VTAction::None
823 } else {
824 VTAction::Event(invalid!(SS2))
825 }
826 }
827 c => VTAction::Event(VTEvent::Ss2(SS2 { char: c })),
828 }
829 }
830
831 fn on_esc_ss3(&mut self, b: u8) -> VTAction {
832 use State::*;
833 self.st = Ground;
834 match b {
835 CAN | SUB => {
836 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
837 VTAction::None
838 } else {
839 VTAction::Event(invalid!(SS3, b))
840 }
841 }
842 ESC => {
845 self.st = Escape;
846 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
847 VTAction::None
848 } else {
849 VTAction::Event(invalid!(SS3))
850 }
851 }
852 c => VTAction::Event(VTEvent::Ss3(SS3 { char: c })),
853 }
854 }
855
856 fn on_csi_entry(&mut self, b: u8) -> VTAction {
858 use State::*;
859 match b {
860 CAN | SUB => {
861 self.st = Ground;
862 VTAction::None
863 }
864 DEL => VTAction::None,
865 ESC => {
866 self.st = Escape;
867 VTAction::None
868 }
869 c if is_priv(c) => {
870 self.priv_prefix = Some(c);
871 self.st = CsiParam;
872 VTAction::None
873 }
874 d if is_digit(d) => {
875 self.cur_param.push(d);
876 self.st = CsiParam;
877 VTAction::None
878 }
879 b';' => {
880 self.next_param();
881 self.st = CsiParam;
882 VTAction::None
883 }
884 b':' => {
885 self.cur_param.push(b':');
886 self.st = CsiParam;
887 VTAction::None
888 }
889 c if is_intermediate(c) => {
890 if self.ints.push(c) {
891 self.st = CsiInt;
892 } else {
893 self.st = Ground;
894 }
895 VTAction::None
896 }
897 c if is_final(c) => {
898 self.st = Ground;
899 self.emit_csi(c)
900 }
901 _ => {
902 self.st = CsiIgnore;
903 VTAction::None
904 }
905 }
906 }
907
908 fn on_csi_param(&mut self, b: u8) -> VTAction {
909 use State::*;
910 match b {
911 CAN | SUB => {
912 self.st = Ground;
913 VTAction::None
914 }
915 DEL => VTAction::None,
916 ESC => {
917 self.st = Escape;
918 VTAction::None
919 }
920 d if is_digit(d) => {
921 self.cur_param.push(d);
922 VTAction::None
923 }
924 b';' => {
925 self.next_param();
926 VTAction::None
927 }
928 b':' => {
929 self.cur_param.push(b':');
930 VTAction::None
931 }
932 c if is_intermediate(c) => {
933 if self.ints.push(c) {
934 self.st = CsiInt;
935 } else {
936 self.st = Ground;
937 }
938 VTAction::None
939 }
940 c if is_final(c) => {
941 self.st = Ground;
942 self.emit_csi(c)
943 }
944 _ => {
945 self.st = CsiIgnore;
946 VTAction::None
947 }
948 }
949 }
950
951 fn on_csi_int(&mut self, b: u8) -> VTAction {
952 use State::*;
953 match b {
954 CAN | SUB => {
955 self.st = Ground;
956 VTAction::None
957 }
958 DEL => VTAction::None,
959 ESC => {
960 self.st = Escape;
961 VTAction::None
962 }
963 c if is_intermediate(c) => {
964 if self.ints.push(c) {
965 self.st = CsiInt;
966 } else {
967 self.st = Ground;
968 }
969 VTAction::None
970 }
971 c if is_final(c) => {
972 self.st = Ground;
973 self.emit_csi(c)
974 }
975 _ => {
976 self.st = CsiIgnore;
977 VTAction::None
978 }
979 }
980 }
981
982 fn on_csi_ignore(&mut self, b: u8) -> VTAction {
983 use State::*;
984 match b {
985 CAN | SUB => {
986 self.st = Ground;
987 VTAction::None
988 }
989 DEL => VTAction::None,
990 ESC => {
991 self.st = Escape;
992 VTAction::None
993 }
994 c if is_final(c) => {
995 self.st = Ground;
996 VTAction::None
997 }
998 _ => VTAction::None,
999 }
1000 }
1001
1002 fn on_dcs_entry(&mut self, b: u8) -> VTAction {
1004 use State::*;
1005 match b {
1006 CAN | SUB => {
1007 self.st = Ground;
1008 VTAction::None
1009 }
1010 DEL => VTAction::None,
1011 ESC => {
1012 self.st = Escape;
1013 VTAction::None
1014 }
1015 c if is_priv(c) => {
1016 self.priv_prefix = Some(c);
1017 self.st = DcsParam;
1018 VTAction::None
1019 }
1020 d if is_digit(d) => {
1021 self.cur_param.push(d);
1022 self.st = DcsParam;
1023 VTAction::None
1024 }
1025 b';' => {
1026 self.next_param();
1027 self.st = DcsParam;
1028 VTAction::None
1029 }
1030 b':' => {
1031 self.st = DcsIgnore;
1032 VTAction::None
1033 }
1034 c if is_intermediate(c) => {
1035 if self.ints.push(c) {
1036 self.st = DcsInt;
1037 } else {
1038 self.st = Ground;
1039 }
1040 VTAction::None
1041 }
1042 c if is_final(c) => {
1043 self.st = DcsPassthrough;
1044 self.dcs_start(c)
1045 }
1046 _ => {
1047 self.st = DcsIgnore;
1048 VTAction::None
1049 }
1050 }
1051 }
1052
1053 fn on_dcs_param(&mut self, b: u8) -> VTAction {
1054 use State::*;
1055 match b {
1056 CAN | SUB => {
1057 self.st = Ground;
1058 VTAction::None
1059 }
1060 DEL => VTAction::None,
1061 ESC => {
1062 self.st = Escape;
1063 VTAction::None
1064 }
1065 d if is_digit(d) => {
1066 self.cur_param.push(d);
1067 VTAction::None
1068 }
1069 b';' => {
1070 self.next_param();
1071 VTAction::None
1072 }
1073 b':' => {
1074 self.st = DcsIgnore;
1075 VTAction::None
1076 }
1077 c if is_intermediate(c) => {
1078 if self.ints.push(c) {
1079 self.st = DcsInt;
1080 } else {
1081 self.st = Ground;
1082 }
1083 self.st = DcsInt;
1084 VTAction::None
1085 }
1086 c if is_final(c) => {
1087 self.st = DcsPassthrough;
1088 self.dcs_start(c)
1089 }
1090 _ => {
1091 self.st = DcsIgnore;
1092 VTAction::None
1093 }
1094 }
1095 }
1096
1097 fn on_dcs_int(&mut self, b: u8) -> VTAction {
1098 use State::*;
1099 match b {
1100 CAN | SUB => {
1101 self.st = Ground;
1102 VTAction::None
1103 }
1104 DEL => VTAction::None,
1105 ESC => {
1106 self.st = Escape;
1107 VTAction::None
1108 }
1109 c if is_intermediate(c) => {
1110 if self.ints.push(c) {
1111 self.st = DcsInt;
1112 } else {
1113 self.st = Ground;
1114 }
1115 VTAction::None
1116 }
1117 c if is_final(c) || is_digit(c) || c == b':' || c == b';' => {
1118 self.st = DcsPassthrough;
1119 self.dcs_start(c)
1120 }
1121 _ => {
1122 self.st = DcsIgnore;
1123 VTAction::None
1124 }
1125 }
1126 }
1127
1128 fn on_dcs_ignore(&mut self, b: u8) -> VTAction {
1129 use State::*;
1130 match b {
1131 CAN | SUB => {
1132 self.st = Ground;
1133 VTAction::None
1134 }
1135 DEL => VTAction::None,
1136 ESC => {
1137 self.st = DcsIgnoreEsc;
1138 VTAction::None
1139 }
1140 _ => VTAction::None,
1141 }
1142 }
1143
1144 fn on_dcs_ignore_esc(&mut self, b: u8) -> VTAction {
1145 use State::*;
1146 match b {
1147 CAN | SUB => {
1148 self.st = Ground;
1149 VTAction::None
1150 }
1151 ST_FINAL => {
1152 self.st = Ground;
1153 VTAction::None
1154 }
1155 DEL => VTAction::None,
1156 ESC => VTAction::None,
1157 _ => {
1158 self.st = DcsIgnore;
1159 VTAction::None
1160 }
1161 }
1162 }
1163
1164 fn on_dcs_pass(&mut self, b: u8) -> VTAction {
1165 use State::*;
1166 match b {
1167 CAN | SUB => {
1168 self.st = Ground;
1169 VTAction::Cancel(VTEmit::Dcs)
1170 }
1171 DEL => VTAction::None,
1172 ESC => {
1173 self.st = DcsEsc;
1174 VTAction::Hold(VTEmit::Dcs)
1175 }
1176 _ => VTAction::Buffer(VTEmit::Dcs),
1177 }
1178 }
1179
1180 fn on_dcs_esc(&mut self, b: u8) -> VTAction {
1181 use State::*;
1182 match b {
1183 ST_FINAL => {
1184 self.st = Ground;
1185 VTAction::End(VTEnd::Dcs)
1186 }
1187 DEL => VTAction::None,
1188 ESC => {
1189 VTAction::Hold(VTEmit::Dcs)
1191 }
1192 _ => {
1193 self.st = DcsPassthrough;
1195 VTAction::Buffer(VTEmit::Dcs)
1196 }
1197 }
1198 }
1199
1200 fn on_osc_string(&mut self, b: u8) -> VTAction {
1202 use State::*;
1203 match b {
1204 CAN | SUB => {
1205 self.st = Ground;
1206 VTAction::Cancel(VTEmit::Osc)
1207 }
1208 DEL => VTAction::None,
1209 BEL => {
1210 self.st = Ground;
1211 VTAction::End(VTEnd::Osc { used_bel: true })
1212 }
1213 ESC => {
1214 self.st = OscEsc;
1215 VTAction::Hold(VTEmit::Osc)
1216 }
1217 p if is_printable(p) => VTAction::Buffer(VTEmit::Osc),
1218 _ => VTAction::None, }
1220 }
1221
1222 fn on_osc_esc(&mut self, b: u8) -> VTAction {
1223 use State::*;
1224 match b {
1225 ST_FINAL => {
1226 self.st = Ground;
1227 VTAction::End(VTEnd::Osc { used_bel: false })
1228 } ESC => VTAction::Hold(VTEmit::Osc),
1230 DEL => VTAction::None,
1231 _ => {
1232 self.st = OscString;
1233 VTAction::Buffer(VTEmit::Osc)
1234 }
1235 }
1236 }
1237
1238 fn on_spa_string(&mut self, b: u8) -> VTAction {
1240 use State::*;
1241 match b {
1242 CAN | SUB => {
1243 self.st = Ground;
1244 VTAction::None
1245 }
1246 DEL => VTAction::None,
1247 ESC => {
1248 self.st = SpaEsc;
1249 VTAction::None
1250 }
1251 _ => VTAction::None,
1252 }
1253 }
1254
1255 fn on_spa_esc(&mut self, b: u8) -> VTAction {
1256 use State::*;
1257 match b {
1258 ST_FINAL => {
1259 self.st = Ground;
1260 VTAction::None
1261 }
1262 DEL => VTAction::None,
1263 ESC => {
1264 VTAction::None
1266 }
1267 _ => {
1268 self.st = State::SosPmApcString;
1269 VTAction::None
1270 }
1271 }
1272 }
1273}
1274
1275#[cfg(test)]
1276mod tests {
1277 use super::*;
1278 use pretty_assertions::assert_eq;
1279
1280 #[test]
1281 fn test_edge_cases() {
1282 let mut result = String::new();
1284 VTPushParser::decode_buffer(&[], |e| result.push_str(&format!("{:?}\n", e)));
1285 assert_eq!(result.trim(), "");
1286
1287 let mut result = String::new();
1289 VTPushParser::decode_buffer(b"\x1b", |e| result.push_str(&format!("{:?}\n", e)));
1290 assert_eq!(result.trim(), "");
1291
1292 let mut result = String::new();
1294 VTPushParser::decode_buffer(b"\x1b[", |e| result.push_str(&format!("{:?}\n", e)));
1295 assert_eq!(result.trim(), "");
1296
1297 let mut result = String::new();
1299 VTPushParser::decode_buffer(b"\x1bP", |e| result.push_str(&format!("{:?}\n", e)));
1300 assert_eq!(result.trim(), "");
1301
1302 let mut result = String::new();
1304 VTPushParser::decode_buffer(b"\x1b]", |e| result.push_str(&format!("{:?}\n", e)));
1305 assert_eq!(result.trim(), "OscStart");
1306 }
1307
1308 #[test]
1309 fn test_streaming_behavior() {
1310 let mut parser = VTPushParser::new(); let mut result = String::new();
1313 let mut callback = |vt_input: VTEvent<'_>| {
1314 result.push_str(&format!("{:?}\n", vt_input));
1315 };
1316
1317 parser.feed_with(b"\x1bP1;2;3 |", &mut callback);
1319 parser.feed_with(b"data", &mut callback);
1320 parser.feed_with(b" more", &mut callback);
1321 parser.feed_with(b"\x1b\\", &mut callback);
1322
1323 assert_eq!(
1324 result.trim(),
1325 "DcsStart(, '1', '2', '3', ' ', |)\nDcsData('data')\nDcsData(' more')\nDcsEnd('')"
1326 );
1327 }
1328
1329 #[test]
1330 fn test_finish_method() {
1331 let mut parser = VTPushParser::new();
1332 let mut result = String::new();
1333 let mut callback = |vt_input: VTEvent<'_>| {
1334 result.push_str(&format!("{:?}\n", vt_input));
1335 };
1336
1337 parser.feed_with(b"\x1b[1;2;3", &mut callback);
1339
1340 parser.finish(&mut callback);
1342
1343 assert_eq!(result.trim(), "");
1344 }
1345
1346 fn collect_events(input: &[u8]) -> Vec<String> {
1426 let mut out = Vec::new();
1427 let mut p = VTPushParser::new();
1428 p.feed_with(input, &mut |ev| out.push(format!("{:?}", ev)));
1429 out
1430 }
1431
1432 #[test]
1433 fn dcs_esc_esc_del() {
1434 let ev = collect_events(b"\x1bP1;2;3|\x1b\x1b\x7fdata\x1b\\");
1436 eprintln!("{ev:?}");
1438 }
1439
1440 #[test]
1441 fn dcs_header_with_colon_is_ignored_case1() {
1442 let ev = collect_events(b"\x1bP1:2qHELLO\x1b\\");
1444 assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1446 }
1447
1448 #[test]
1449 fn dcs_header_with_colon_is_ignored_case2() {
1450 let ev = collect_events(b"\x1bP:1qDATA\x1b\\");
1452 assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1453 }
1454
1455 #[test]
1456 fn dcs_header_with_colon_is_ignored_case3() {
1457 let ev = collect_events(b"\x1bP12:34!qPAYLOAD\x1b\\");
1459 assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1460 }
1461
1462 #[test]
1463 fn osc_aborted_by_can_mid_body() {
1464 let mut s = Vec::new();
1466 s.extend_from_slice(b"\x1b]0;Title");
1467 s.push(CAN);
1468 s.extend_from_slice(b"more\x07");
1469
1470 let ev = collect_debug(&s);
1471
1472 assert!(ev.iter().any(|e| e.starts_with("OscStart")), "{ev:#?}");
1477 assert!(!ev.iter().any(|e| e.starts_with("OscData")), "{ev:#?}");
1478 assert!(!ev.iter().any(|e| e.starts_with("OscEnd")), "{ev:#?}");
1479 }
1480
1481 #[test]
1482 fn osc_aborted_by_sub_before_terminator() {
1483 let mut s = Vec::new();
1484 s.extend_from_slice(b"\x1b]52;c;YWJjZA==");
1485 s.push(SUB); s.extend_from_slice(b"\x1b\\"); let ev = collect_debug(&s);
1489 assert!(ev.iter().any(|e| e.starts_with("OscStart")), "{ev:#?}");
1493 assert!(!ev.iter().any(|e| e.starts_with("OscData")), "{ev:#?}");
1494 assert!(!ev.iter().any(|e| e.starts_with("OscEnd")), "{ev:#?}");
1495 }
1496
1497 fn collect_debug(input: &[u8]) -> Vec<String> {
1499 let mut out = Vec::new();
1500 let mut p = VTPushParser::new();
1501 p.feed_with(input, &mut |ev| out.push(format!("{:?}", ev)));
1502 out
1503 }
1504
1505 #[test]
1506 fn dcs_aborted_by_can_before_body() {
1507 let mut s = Vec::new();
1509 s.extend_from_slice(b"\x1bPq"); s.push(CAN);
1511 s.extend_from_slice(b"IGNORED\x1b\\"); let ev = collect_debug(&s);
1514
1515 assert_eq!(ev.len(), 4, "{ev:#?}");
1516 assert_eq!(ev[0], "DcsStart(, '', q)");
1517 assert_eq!(ev[1], "DcsCancel");
1518 assert_eq!(ev[2], "Raw('IGNORED')");
1519 assert_eq!(ev[3], "Esc('', \\)");
1520 }
1521
1522 #[test]
1523 fn dcs_aborted_by_can_mid_body() {
1524 let mut s = Vec::new();
1526 s.extend_from_slice(b"\x1bPqABC");
1527 s.push(CAN);
1528 s.extend_from_slice(b"MORE\x1b\\"); let ev = collect_debug(&s);
1531
1532 assert_eq!(ev.len(), 4, "{ev:#?}");
1533 assert_eq!(ev[0], "DcsStart(, '', q)");
1534 assert_eq!(ev[1], "DcsCancel");
1535 assert_eq!(ev[2], "Raw('MORE')");
1536 assert_eq!(ev[3], "Esc('', \\)");
1537 }
1538
1539 #[test]
1542 fn spa_aborted_by_can_is_ignored() {
1543 let mut s = Vec::new();
1545 s.extend_from_slice(b"\x1b_hello");
1546 s.push(CAN);
1547 s.extend_from_slice(b"world\x1b\\");
1548
1549 let ev = collect_debug(&s);
1550 assert_eq!(ev.len(), 2, "{ev:#?}");
1551 assert_eq!(ev[0], "Raw('world')");
1552 assert_eq!(ev[1], "Esc('', \\)");
1553 }
1554
1555 #[test]
1556 fn spa_sub_aborts_too() {
1557 let mut s = Vec::new();
1558 s.extend_from_slice(b"\x1bXhello");
1559 s.push(SUB);
1560 s.extend_from_slice(b"world\x1b\\");
1561 let ev = collect_debug(&s);
1562 assert_eq!(ev.len(), 2, "{ev:#?}");
1563 assert_eq!(ev[0], "Raw('world')");
1564 assert_eq!(ev[1], "Esc('', \\)");
1565 }
1566
1567 #[test]
1570 fn can_in_ground_is_c0() {
1571 let mut s = Vec::new();
1572 s.extend_from_slice(b"abc");
1573 s.push(CAN);
1574 s.extend_from_slice(b"def");
1575 let ev = collect_debug(&s);
1576 assert_eq!(ev.len(), 3, "{ev:#?}");
1578 assert_eq!(ev[0], "Raw('abc')");
1579 assert_eq!(ev[1], "C0(18)");
1580 assert_eq!(ev[2], "Raw('def')");
1581 }
1582
1583 #[test]
1586 fn three_byte_sequences_capturable() {
1587 let mut bytes = vec![];
1588 for i in 0..=0xFFFFFF_u32 {
1589 bytes.clear();
1590 let test_bytes = i.to_le_bytes();
1591 let test_bytes = &test_bytes[..3];
1592 if test_bytes.iter().any(|b| b == &0) {
1593 continue;
1594 }
1595 if test_bytes[0] == 0x1b && matches!(test_bytes[1], CSI | DCS | OSC | APC | PM | SOS) {
1596 continue;
1597 }
1598 if test_bytes[1] == 0x1b && matches!(test_bytes[2], CSI | DCS | OSC | APC | PM | SOS) {
1599 continue;
1600 }
1601
1602 let mut parser = VTPushParser::<VT_PARSER_INTEREST_ALL>::new_with();
1603 parser.feed_with(test_bytes, &mut |event| {
1604 let mut chunk = [0_u8; 3];
1605 let b = event.encode(&mut chunk).unwrap();
1606 bytes.extend_from_slice(&chunk[..b]);
1607 });
1608 if let Some(event) = parser.idle() {
1609 let mut chunk = [0_u8; 3];
1610 let b = event.encode(&mut chunk).unwrap();
1611 bytes.extend_from_slice(&chunk[..b]);
1612 }
1613
1614 if bytes.len() != 3 || bytes != test_bytes {
1615 eprintln!("Failed to parse:");
1616 parser.feed_with(test_bytes, &mut |event| {
1617 eprintln!("{:?}", event);
1618 });
1619 if let Some(event) = parser.idle() {
1620 eprintln!("{:?}", event);
1621 }
1622 assert_eq!(bytes, test_bytes, "{test_bytes:X?} -> {bytes:X?}");
1623 }
1624 }
1625 }
1626}