1pub mod ascii;
2pub mod event;
3pub mod iter;
4pub mod signature;
5
6use smallvec::SmallVec;
7
8use ascii::AsciiControl;
9use event::{VTEvent, VTIntermediate};
10
11const ESC: u8 = AsciiControl::Esc as _;
12const BEL: u8 = AsciiControl::Bel as _;
13const DEL: u8 = AsciiControl::Del as _;
14const CAN: u8 = AsciiControl::Can as _;
15const SUB: u8 = AsciiControl::Sub as _;
16const CSI: u8 = b'[';
17const OSC: u8 = b']';
18const SS2: u8 = b'N';
19const SS3: u8 = b'O';
20const DCS: u8 = b'P';
21const ST_FINAL: u8 = b'\\';
22
23pub use signature::VTEscapeSignature;
25
26use crate::event::{Param, ParamBuf, Params};
27
28pub enum VTAction<'a> {
30 None,
33 Event(VTEvent<'a>),
36 End(VTEnd),
38 Buffer(VTEmit),
41 Hold(VTEmit),
44 Cancel(VTEmit),
46}
47
48#[derive(Debug, Copy, Clone, PartialEq, Eq)]
49pub enum VTEmit {
50 Ground,
52 Dcs,
54 Osc,
56}
57
58#[derive(Debug, Copy, Clone, PartialEq, Eq)]
59pub enum VTEnd {
60 Dcs,
62 Osc { used_bel: bool },
64}
65
66#[inline]
67const fn is_c0(b: u8) -> bool {
68 b <= 0x1F && b != b'\r' && b != b'\n' && b != b'\t'
70}
71#[inline]
72fn is_printable(b: u8) -> bool {
73 (0x20..=0x7E).contains(&b)
74}
75#[inline]
76fn is_intermediate(b: u8) -> bool {
77 (0x20..=0x2F).contains(&b)
78}
79#[inline]
80const fn is_final(b: u8) -> bool {
81 b >= 0x40 && b <= 0x7E
82}
83#[inline]
84fn is_digit(b: u8) -> bool {
85 (b'0'..=b'9').contains(&b)
86}
87#[inline]
88fn is_priv(b: u8) -> bool {
89 matches!(b, b'<' | b'=' | b'>' | b'?')
90}
91
92macro_rules! byte_predicate {
93 (|$p:ident| $body:block) => {{
94 let mut out: [bool; 256] = [false; 256];
95 let mut i = 0;
96 while i < 256 {
97 let $p: u8 = i as u8;
98 out[i] = $body;
99 i += 1;
100 }
101 out
102 }};
103}
104
105const ENDS_CSI: [bool; 256] =
106 byte_predicate!(|b| { is_final(b) || b == ESC || b == CAN || b == SUB });
107
108const ENDS_GROUND: [bool; 256] = byte_predicate!(|b| { is_c0(b) || b == DEL });
109
110#[derive(Debug, Copy, Clone, PartialEq, Eq)]
111enum State {
112 Ground,
113 Escape,
114 EscInt,
115 EscSs2,
116 EscSs3,
117 CsiEntry,
118 CsiParam,
119 CsiInt,
120 CsiIgnore,
121 DcsEntry,
122 DcsParam,
123 DcsInt,
124 DcsIgnore,
125 DcsIgnoreEsc,
126 DcsPassthrough,
127 DcsEsc,
128 OscString,
129 OscEsc,
130 SosPmApcString,
131 SpaEsc,
132}
133
134pub const VT_PARSER_INTEREST_NONE: u8 = 0;
135pub const VT_PARSER_INTEREST_CSI: u8 = 1 << 0;
137pub const VT_PARSER_INTEREST_DCS: u8 = 1 << 1;
139pub const VT_PARSER_INTEREST_OSC: u8 = 1 << 2;
141pub const VT_PARSER_INTEREST_OTHER: u8 = 1 << 3;
143pub const VT_PARSER_INTEREST_ALL: u8 = VT_PARSER_INTEREST_CSI
144 | VT_PARSER_INTEREST_DCS
145 | VT_PARSER_INTEREST_OSC
146 | VT_PARSER_INTEREST_OTHER;
147
148#[must_use]
149trait MaybeAbortable {
150 fn abort(self) -> bool;
151}
152
153impl MaybeAbortable for bool {
154 #[inline(always)]
155 fn abort(self) -> bool {
156 !self
157 }
158}
159
160impl MaybeAbortable for () {
161 #[inline(always)]
162 fn abort(self) -> bool {
163 false
164 }
165}
166
167pub struct VTPushParser<const INTEREST: u8 = VT_PARSER_INTEREST_ALL> {
168 st: State,
169
170 ints: VTIntermediate,
172 params: Params,
173 cur_param: Param,
174 priv_prefix: Option<u8>,
175 held_byte: Option<u8>,
176}
177
178impl VTPushParser {
179 pub const fn new() -> Self {
180 VTPushParser::new_with()
181 }
182
183 pub fn decode_buffer<'a>(input: &'a [u8], mut cb: impl for<'b> FnMut(VTEvent<'b>)) {
185 let mut parser = VTPushParser::new();
186 parser.feed_with(input, &mut cb);
187 parser.finish(&mut cb);
188 }
189
190 pub const fn new_with_interest<const INTEREST: u8>() -> VTPushParser<INTEREST> {
191 VTPushParser::new_with()
192 }
193}
194
195impl<const INTEREST: u8> VTPushParser<INTEREST> {
196 const fn new_with() -> Self {
197 Self {
198 st: State::Ground,
199 ints: VTIntermediate::empty(),
200 params: SmallVec::new_const(),
201 cur_param: SmallVec::new_const(),
202 priv_prefix: None,
203 held_byte: None,
204 }
205 }
206
207 #[inline]
219 pub fn feed_with<'this, 'input, F: for<'any> FnMut(VTEvent<'any>)>(
220 &'this mut self,
221 input: &'input [u8],
222 cb: &mut F,
223 ) {
224 self.feed_with_internal(input, cb);
225 }
226
227 #[inline]
240 pub fn feed_with_abortable<'this, 'input, F: for<'any> FnMut(VTEvent<'any>) -> bool>(
241 &'this mut self,
242 input: &'input [u8],
243 cb: &mut F,
244 ) -> usize {
245 self.feed_with_internal(input, cb)
246 }
247
248 #[inline(always)]
249 fn feed_with_internal<
250 'this,
251 'input,
252 R: MaybeAbortable,
253 F: for<'any> FnMut(VTEvent<'any>) -> R,
254 >(
255 &'this mut self,
256 input: &'input [u8],
257 cb: &mut F,
258 ) -> usize {
259 if input.is_empty() {
260 return 0;
261 }
262
263 #[derive(Debug)]
264 struct FeedState {
265 buffer_idx: usize,
266 current_emit: Option<VTEmit>,
267 hold: bool,
268 }
269
270 let mut state = FeedState {
271 buffer_idx: 0,
272 current_emit: None,
273 hold: self.held_byte.is_some(),
274 };
275
276 macro_rules! emit {
277 ($state:ident, $i:expr, $cb:expr, $end:expr, $used_bel:expr) => {
278 let hold = std::mem::take(&mut $state.hold);
279 if let Some(emit) = $state.current_emit.take() {
280 let i = $i;
281 let range = $state.buffer_idx..(i - hold as usize);
282 if $end {
283 if match emit {
284 VTEmit::Ground => unreachable!(),
285 VTEmit::Dcs => $cb(VTEvent::DcsEnd(&input[range])),
286 VTEmit::Osc => $cb(VTEvent::OscEnd {
287 data: &input[range],
288 used_bel: $used_bel,
289 }),
290 }
291 .abort()
292 {
293 return i + 1;
294 }
295 } else if range.len() > 0 {
296 if match emit {
297 VTEmit::Ground => $cb(VTEvent::Raw(&input[range])),
298 VTEmit::Dcs => $cb(VTEvent::DcsData(&input[range])),
299 VTEmit::Osc => $cb(VTEvent::OscData(&input[range])),
300 }
301 .abort()
302 {
303 return i + 1;
304 }
305 }
306 }
307 };
308 }
309
310 let mut held_byte = self.held_byte.take();
311 let mut i = 0;
312
313 while i < input.len() {
314 if self.st == State::Ground {
316 let start = i;
317 loop {
318 if i >= input.len() {
319 cb(VTEvent::Raw(&input[start..]));
320 return input.len();
321 }
322 if ENDS_GROUND[input[i] as usize] {
323 break;
324 }
325 i += 1;
326 }
327
328 if start != i {
329 if cb(VTEvent::Raw(&input[start..i])).abort() {
330 return i;
331 }
332 }
333
334 if input[i] == ESC {
335 self.clear_hdr_collectors();
336 self.st = State::Escape;
337 i = i + 1;
338 continue;
339 }
340 }
341
342 if self.st == State::CsiIgnore {
344 loop {
345 if i >= input.len() {
346 return input.len();
347 }
348 if ENDS_CSI[input[i] as usize] {
349 break;
350 }
351 i += 1;
352 }
353
354 if input[i] == ESC {
355 self.st = State::Escape;
356 } else {
357 self.st = State::Ground;
358 }
359 i += 1;
360 continue;
361 }
362
363 let action = self.push_with(input[i]);
364
365 match action {
366 VTAction::None => {
367 if let Some(emit) = state.current_emit {
368 let range = state.buffer_idx..(i - state.hold as usize);
370 if !range.is_empty() {
371 if match emit {
372 VTEmit::Ground => cb(VTEvent::Raw(&input[range])),
373 VTEmit::Dcs => cb(VTEvent::DcsData(&input[range])),
374 VTEmit::Osc => cb(VTEvent::OscData(&input[range])),
375 }
376 .abort()
377 {
378 if state.hold {
379 self.held_byte = Some(0x1b);
380 }
381 return i + 1;
382 }
383 }
384 if state.hold {
385 held_byte = Some(0x1b);
386 }
387 state.current_emit = None;
388 }
389 }
390 VTAction::Event(e) => {
391 if cb(e).abort() {
392 return i + 1;
393 }
394 }
395 VTAction::End(VTEnd::Dcs) => {
396 held_byte = None;
397 emit!(state, i, cb, true, false);
398 }
399 VTAction::End(VTEnd::Osc { used_bel }) => {
400 held_byte = None;
401 emit!(state, i, cb, true, used_bel);
402 }
403 VTAction::Buffer(emit) | VTAction::Hold(emit) => {
404 if state.current_emit.is_none() {
405 if let Some(h) = held_byte.take() {
406 if match emit {
407 VTEmit::Ground => cb(VTEvent::Raw(&[h])),
408 VTEmit::Dcs => cb(VTEvent::DcsData(&[h])),
409 VTEmit::Osc => cb(VTEvent::OscData(&[h])),
410 }
411 .abort()
412 {
413 if matches!(action, VTAction::Hold(_)) {
414 self.held_byte = Some(0x1b);
415 return 1;
416 }
417 return 0;
418 }
419 }
420 }
421
422 debug_assert!(state.current_emit.is_none() || state.current_emit == Some(emit));
423
424 state.hold = matches!(action, VTAction::Hold(_));
425 if state.current_emit.is_none() {
426 state.buffer_idx = i;
427 state.current_emit = Some(emit);
428 }
429 }
430 VTAction::Cancel(emit) => {
431 state.current_emit = None;
432 state.hold = false;
433 if match emit {
434 VTEmit::Ground => unreachable!(),
435 VTEmit::Dcs => cb(VTEvent::DcsCancel),
436 VTEmit::Osc => cb(VTEvent::OscCancel),
437 }
438 .abort()
439 {
440 return i + 1;
441 }
442 }
443 };
444 i += 1;
445 }
446
447 if state.hold {
449 self.held_byte = Some(0x1b);
450 }
451
452 if let Some(emit) = state.current_emit.take() {
453 let range = &input[state.buffer_idx..input.len() - state.hold as usize];
454 if !range.is_empty() {
455 match emit {
456 VTEmit::Ground => cb(VTEvent::Raw(range)),
457 VTEmit::Dcs => cb(VTEvent::DcsData(range)),
458 VTEmit::Osc => cb(VTEvent::OscData(range)),
459 };
460 }
461 };
462
463 input.len()
465 }
466
467 pub fn idle(&mut self) -> Option<VTEvent<'static>> {
471 match self.st {
472 State::Escape => {
473 self.st = State::Ground;
474 Some(VTEvent::C0(ESC))
475 }
476 State::EscInt | State::EscSs2 | State::EscSs3 => {
477 self.st = State::Ground;
478 None
479 }
480 _ => None,
481 }
482 }
483
484 fn push_with(&mut self, b: u8) -> VTAction {
485 use State::*;
486 match self.st {
487 Ground => self.on_ground(b),
488 Escape => self.on_escape(b),
489 EscInt => self.on_esc_int(b),
490 EscSs2 => self.on_esc_ss2(b),
491 EscSs3 => self.on_esc_ss3(b),
492
493 CsiEntry => self.on_csi_entry(b),
494 CsiParam => self.on_csi_param(b),
495 CsiInt => self.on_csi_int(b),
496 CsiIgnore => self.on_csi_ignore(b),
497
498 DcsEntry => self.on_dcs_entry(b),
499 DcsParam => self.on_dcs_param(b),
500 DcsInt => self.on_dcs_int(b),
501 DcsIgnore => self.on_dcs_ignore(b),
502 DcsIgnoreEsc => self.on_dcs_ignore_esc(b),
503 DcsPassthrough => self.on_dcs_pass(b),
504 DcsEsc => self.on_dcs_esc(b),
505
506 OscString => self.on_osc_string(b),
507 OscEsc => self.on_osc_esc(b),
508
509 SosPmApcString => self.on_spa_string(b),
510 SpaEsc => self.on_spa_esc(b),
511 }
512 }
513
514 pub fn finish<F: FnMut(VTEvent)>(&mut self, cb: &mut F) {
515 self.reset_collectors();
516 self.st = State::Ground;
517
518 }
520
521 fn clear_hdr_collectors(&mut self) {
526 self.ints.clear();
527 self.params.clear();
528 self.cur_param.clear();
529 self.priv_prefix = None;
530 }
531
532 fn reset_collectors(&mut self) {
533 self.clear_hdr_collectors();
534 }
535
536 fn next_param(&mut self) {
537 self.params.push(std::mem::take(&mut self.cur_param));
538 }
539
540 fn finish_params_if_any(&mut self) {
541 if !self.cur_param.is_empty() || !self.params.is_empty() {
542 self.next_param();
543 }
544 }
545
546 fn emit_csi(&mut self, final_byte: u8) -> VTAction {
547 self.finish_params_if_any();
548
549 let mut borrowed: SmallVec<[&[u8]; 4]> = SmallVec::new();
551 borrowed.extend(self.params.iter().map(|v| v.as_slice()));
552
553 let privp = self.priv_prefix.take();
554 VTAction::Event(VTEvent::Csi {
555 private: privp,
556 params: ParamBuf {
557 params: &self.params,
558 },
559 intermediates: self.ints,
560 final_byte,
561 })
562 }
563
564 fn dcs_start(&mut self, final_byte: u8) -> VTAction {
565 self.finish_params_if_any();
566
567 let privp = self.priv_prefix.take();
568 VTAction::Event(VTEvent::DcsStart {
569 private: privp,
570 params: ParamBuf {
571 params: &self.params,
572 },
573 intermediates: self.ints,
574 final_byte,
575 })
576 }
577
578 fn on_ground(&mut self, b: u8) -> VTAction {
583 match b {
584 ESC => {
585 self.clear_hdr_collectors();
586 self.st = State::Escape;
587 VTAction::None
588 }
589 DEL => VTAction::Event(VTEvent::C0(DEL)),
590 c if is_c0(c) => VTAction::Event(VTEvent::C0(c)),
591 p if is_printable(p) => VTAction::Buffer(VTEmit::Ground),
592 _ => VTAction::Buffer(VTEmit::Ground), }
594 }
595
596 fn on_escape(&mut self, b: u8) -> VTAction {
597 use State::*;
598 match b {
599 CAN | SUB => {
600 self.st = Ground;
601 VTAction::None
602 }
603 DEL => VTAction::None,
604 c if is_intermediate(c) => {
605 if self.ints.push(c) {
606 self.st = EscInt;
607 } else {
608 self.st = Ground;
609 }
610 VTAction::None
611 }
612 CSI => {
613 if INTEREST & VT_PARSER_INTEREST_CSI == 0 {
614 self.st = CsiIgnore;
615 } else {
616 self.st = CsiEntry;
617 }
618 VTAction::None
619 }
620 DCS => {
621 if INTEREST & VT_PARSER_INTEREST_DCS == 0 {
622 self.st = DcsIgnore;
623 } else {
624 self.st = DcsEntry;
625 }
626 VTAction::None
627 }
628 OSC => {
629 self.st = OscString;
630 VTAction::Event(VTEvent::OscStart)
631 }
632 SS2 => {
633 self.st = EscSs2;
634 VTAction::None
635 }
636 SS3 => {
637 self.st = EscSs3;
638 VTAction::None
639 }
640 b'X' | b'^' | b'_' => {
641 self.st = State::SosPmApcString;
642 VTAction::None
643 }
644 c if is_final(c) || is_digit(c) => {
645 self.st = Ground;
646 VTAction::Event(VTEvent::Esc {
647 intermediates: self.ints,
648 final_byte: c,
649 })
650 }
651 ESC => {
652 VTAction::Event(VTEvent::C0(ESC))
654 }
655 _ => {
656 self.st = Ground;
657 VTAction::None
658 }
659 }
660 }
661
662 fn on_esc_int(&mut self, b: u8) -> VTAction {
663 use State::*;
664 match b {
665 CAN | SUB => {
666 self.st = Ground;
667 VTAction::None
668 }
669 DEL => VTAction::None,
670 c if is_intermediate(c) => {
671 if !self.ints.push(c) {
672 self.st = Ground;
673 }
674 VTAction::None
675 }
676 c if is_final(c) || is_digit(c) => {
677 self.st = Ground;
678 VTAction::Event(VTEvent::Esc {
679 intermediates: self.ints,
680 final_byte: c,
681 })
682 }
683 _ => {
684 self.st = Ground;
685 VTAction::None
686 }
687 }
688 }
689
690 fn on_esc_ss2(&mut self, b: u8) -> VTAction {
691 use State::*;
692 self.st = Ground;
693 match b {
694 CAN | SUB => VTAction::None,
695 c => VTAction::Event(VTEvent::Ss2 { char: c }),
696 }
697 }
698
699 fn on_esc_ss3(&mut self, b: u8) -> VTAction {
700 use State::*;
701 self.st = Ground;
702 match b {
703 CAN | SUB => VTAction::None,
704 c => VTAction::Event(VTEvent::Ss3 { char: c }),
705 }
706 }
707
708 fn on_csi_entry(&mut self, b: u8) -> VTAction {
710 use State::*;
711 match b {
712 CAN | SUB => {
713 self.st = Ground;
714 VTAction::None
715 }
716 DEL => VTAction::None,
717 ESC => {
718 self.st = Escape;
719 VTAction::None
720 }
721 c if is_priv(c) => {
722 self.priv_prefix = Some(c);
723 self.st = CsiParam;
724 VTAction::None
725 }
726 d if is_digit(d) => {
727 self.cur_param.push(d);
728 self.st = CsiParam;
729 VTAction::None
730 }
731 b';' => {
732 self.next_param();
733 self.st = CsiParam;
734 VTAction::None
735 }
736 b':' => {
737 self.cur_param.push(b':');
738 self.st = CsiParam;
739 VTAction::None
740 }
741 c if is_intermediate(c) => {
742 if self.ints.push(c) {
743 self.st = CsiInt;
744 } else {
745 self.st = Ground;
746 }
747 VTAction::None
748 }
749 c if is_final(c) => {
750 self.st = Ground;
751 self.emit_csi(c)
752 }
753 _ => {
754 self.st = CsiIgnore;
755 VTAction::None
756 }
757 }
758 }
759
760 fn on_csi_param(&mut self, b: u8) -> VTAction {
761 use State::*;
762 match b {
763 CAN | SUB => {
764 self.st = Ground;
765 VTAction::None
766 }
767 DEL => VTAction::None,
768 ESC => {
769 self.st = Escape;
770 VTAction::None
771 }
772 d if is_digit(d) => {
773 self.cur_param.push(d);
774 VTAction::None
775 }
776 b';' => {
777 self.next_param();
778 VTAction::None
779 }
780 b':' => {
781 self.cur_param.push(b':');
782 VTAction::None
783 }
784 c if is_intermediate(c) => {
785 if self.ints.push(c) {
786 self.st = CsiInt;
787 } else {
788 self.st = Ground;
789 }
790 VTAction::None
791 }
792 c if is_final(c) => {
793 self.st = Ground;
794 self.emit_csi(c)
795 }
796 _ => {
797 self.st = CsiIgnore;
798 VTAction::None
799 }
800 }
801 }
802
803 fn on_csi_int(&mut self, b: u8) -> VTAction {
804 use State::*;
805 match b {
806 CAN | SUB => {
807 self.st = Ground;
808 VTAction::None
809 }
810 DEL => VTAction::None,
811 ESC => {
812 self.st = Escape;
813 VTAction::None
814 }
815 c if is_intermediate(c) => {
816 if self.ints.push(c) {
817 self.st = CsiInt;
818 } else {
819 self.st = Ground;
820 }
821 VTAction::None
822 }
823 c if is_final(c) => {
824 self.st = Ground;
825 self.emit_csi(c)
826 }
827 _ => {
828 self.st = CsiIgnore;
829 VTAction::None
830 }
831 }
832 }
833
834 fn on_csi_ignore(&mut self, b: u8) -> VTAction {
835 use State::*;
836 match b {
837 CAN | SUB => {
838 self.st = Ground;
839 VTAction::None
840 }
841 DEL => VTAction::None,
842 ESC => {
843 self.st = Escape;
844 VTAction::None
845 }
846 c if is_final(c) => {
847 self.st = Ground;
848 VTAction::None
849 }
850 _ => VTAction::None,
851 }
852 }
853
854 fn on_dcs_entry(&mut self, b: u8) -> VTAction {
856 use State::*;
857 match b {
858 CAN | SUB => {
859 self.st = Ground;
860 VTAction::None
861 }
862 DEL => VTAction::None,
863 ESC => {
864 self.st = Escape;
865 VTAction::None
866 }
867 c if is_priv(c) => {
868 self.priv_prefix = Some(c);
869 self.st = DcsParam;
870 VTAction::None
871 }
872 d if is_digit(d) => {
873 self.cur_param.push(d);
874 self.st = DcsParam;
875 VTAction::None
876 }
877 b';' => {
878 self.next_param();
879 self.st = DcsParam;
880 VTAction::None
881 }
882 b':' => {
883 self.st = DcsIgnore;
884 VTAction::None
885 }
886 c if is_intermediate(c) => {
887 if self.ints.push(c) {
888 self.st = DcsInt;
889 } else {
890 self.st = Ground;
891 }
892 VTAction::None
893 }
894 c if is_final(c) => {
895 self.st = DcsPassthrough;
896 self.dcs_start(c)
897 }
898 _ => {
899 self.st = DcsIgnore;
900 VTAction::None
901 }
902 }
903 }
904
905 fn on_dcs_param(&mut self, b: u8) -> VTAction {
906 use State::*;
907 match b {
908 CAN | SUB => {
909 self.st = Ground;
910 VTAction::None
911 }
912 DEL => VTAction::None,
913 ESC => {
914 self.st = Escape;
915 VTAction::None
916 }
917 d if is_digit(d) => {
918 self.cur_param.push(d);
919 VTAction::None
920 }
921 b';' => {
922 self.next_param();
923 VTAction::None
924 }
925 b':' => {
926 self.st = DcsIgnore;
927 VTAction::None
928 }
929 c if is_intermediate(c) => {
930 if self.ints.push(c) {
931 self.st = DcsInt;
932 } else {
933 self.st = Ground;
934 }
935 self.st = DcsInt;
936 VTAction::None
937 }
938 c if is_final(c) => {
939 self.st = DcsPassthrough;
940 self.dcs_start(c)
941 }
942 _ => {
943 self.st = DcsIgnore;
944 VTAction::None
945 }
946 }
947 }
948
949 fn on_dcs_int(&mut self, b: u8) -> VTAction {
950 use State::*;
951 match b {
952 CAN | SUB => {
953 self.st = Ground;
954 VTAction::None
955 }
956 DEL => VTAction::None,
957 ESC => {
958 self.st = Escape;
959 VTAction::None
960 }
961 c if is_intermediate(c) => {
962 if self.ints.push(c) {
963 self.st = DcsInt;
964 } else {
965 self.st = Ground;
966 }
967 VTAction::None
968 }
969 c if is_final(c) || is_digit(c) || c == b':' || c == b';' => {
970 self.st = DcsPassthrough;
971 self.dcs_start(c)
972 }
973 _ => {
974 self.st = DcsIgnore;
975 VTAction::None
976 }
977 }
978 }
979
980 fn on_dcs_ignore(&mut self, b: u8) -> VTAction {
981 use State::*;
982 match b {
983 CAN | SUB => {
984 self.st = Ground;
985 VTAction::None
986 }
987 DEL => VTAction::None,
988 ESC => {
989 self.st = DcsIgnoreEsc;
990 VTAction::None
991 }
992 _ => VTAction::None,
993 }
994 }
995
996 fn on_dcs_ignore_esc(&mut self, b: u8) -> VTAction {
997 use State::*;
998 match b {
999 CAN | SUB => {
1000 self.st = Ground;
1001 VTAction::None
1002 }
1003 ST_FINAL => {
1004 self.st = Ground;
1005 VTAction::None
1006 }
1007 DEL => VTAction::None,
1008 ESC => VTAction::None,
1009 _ => {
1010 self.st = DcsIgnore;
1011 VTAction::None
1012 }
1013 }
1014 }
1015
1016 fn on_dcs_pass(&mut self, b: u8) -> VTAction {
1017 use State::*;
1018 match b {
1019 CAN | SUB => {
1020 self.st = Ground;
1021 VTAction::Cancel(VTEmit::Dcs)
1022 }
1023 DEL => VTAction::None,
1024 ESC => {
1025 self.st = DcsEsc;
1026 VTAction::Hold(VTEmit::Dcs)
1027 }
1028 _ => VTAction::Buffer(VTEmit::Dcs),
1029 }
1030 }
1031
1032 fn on_dcs_esc(&mut self, b: u8) -> VTAction {
1033 use State::*;
1034 match b {
1035 ST_FINAL => {
1036 self.st = Ground;
1037 VTAction::End(VTEnd::Dcs)
1038 }
1039 DEL => VTAction::None,
1040 ESC => {
1041 VTAction::Hold(VTEmit::Dcs)
1043 }
1044 _ => {
1045 self.st = DcsPassthrough;
1047 VTAction::Buffer(VTEmit::Dcs)
1048 }
1049 }
1050 }
1051
1052 fn on_osc_string(&mut self, b: u8) -> VTAction {
1054 use State::*;
1055 match b {
1056 CAN | SUB => {
1057 self.st = Ground;
1058 VTAction::Cancel(VTEmit::Osc)
1059 }
1060 DEL => VTAction::None,
1061 BEL => {
1062 self.st = Ground;
1063 VTAction::End(VTEnd::Osc { used_bel: true })
1064 }
1065 ESC => {
1066 self.st = OscEsc;
1067 VTAction::Hold(VTEmit::Osc)
1068 }
1069 p if is_printable(p) => VTAction::Buffer(VTEmit::Osc),
1070 _ => VTAction::None, }
1072 }
1073
1074 fn on_osc_esc(&mut self, b: u8) -> VTAction {
1075 use State::*;
1076 match b {
1077 ST_FINAL => {
1078 self.st = Ground;
1079 VTAction::End(VTEnd::Osc { used_bel: false })
1080 } ESC => VTAction::Hold(VTEmit::Osc),
1082 DEL => VTAction::None,
1083 _ => {
1084 self.st = OscString;
1085 VTAction::Buffer(VTEmit::Osc)
1086 }
1087 }
1088 }
1089
1090 fn on_spa_string(&mut self, b: u8) -> VTAction {
1092 use State::*;
1093 match b {
1094 CAN | SUB => {
1095 self.st = Ground;
1096 VTAction::None
1097 }
1098 DEL => VTAction::None,
1099 ESC => {
1100 self.st = SpaEsc;
1101 VTAction::None
1102 }
1103 _ => VTAction::None,
1104 }
1105 }
1106
1107 fn on_spa_esc(&mut self, b: u8) -> VTAction {
1108 use State::*;
1109 match b {
1110 ST_FINAL => {
1111 self.st = Ground;
1112 VTAction::None
1113 }
1114 DEL => VTAction::None,
1115 ESC => {
1116 VTAction::None
1118 }
1119 _ => {
1120 self.st = State::SosPmApcString;
1121 VTAction::None
1122 }
1123 }
1124 }
1125}
1126
1127#[cfg(test)]
1128mod tests {
1129 use pretty_assertions::assert_eq;
1130
1131 use super::*;
1132
1133 #[test]
1134 fn test_edge_cases() {
1135 let mut result = String::new();
1137 VTPushParser::decode_buffer(&[], |e| result.push_str(&format!("{:?}\n", e)));
1138 assert_eq!(result.trim(), "");
1139
1140 let mut result = String::new();
1142 VTPushParser::decode_buffer(b"\x1b", |e| result.push_str(&format!("{:?}\n", e)));
1143 assert_eq!(result.trim(), "");
1144
1145 let mut result = String::new();
1147 VTPushParser::decode_buffer(b"\x1b[", |e| result.push_str(&format!("{:?}\n", e)));
1148 assert_eq!(result.trim(), "");
1149
1150 let mut result = String::new();
1152 VTPushParser::decode_buffer(b"\x1bP", |e| result.push_str(&format!("{:?}\n", e)));
1153 assert_eq!(result.trim(), "");
1154
1155 let mut result = String::new();
1157 VTPushParser::decode_buffer(b"\x1b]", |e| result.push_str(&format!("{:?}\n", e)));
1158 assert_eq!(result.trim(), "OscStart");
1159 }
1160
1161 #[test]
1162 fn test_streaming_behavior() {
1163 let mut parser = VTPushParser::new(); let mut result = String::new();
1166 let mut callback = |vt_input: VTEvent<'_>| {
1167 result.push_str(&format!("{:?}\n", vt_input));
1168 };
1169
1170 parser.feed_with(b"\x1bP1;2;3 |", &mut callback);
1172 parser.feed_with(b"data", &mut callback);
1173 parser.feed_with(b" more", &mut callback);
1174 parser.feed_with(b"\x1b\\", &mut callback);
1175
1176 assert_eq!(
1177 result.trim(),
1178 "DcsStart(, '1', '2', '3', ' ', |)\nDcsData('data')\nDcsData(' more')\nDcsEnd('')"
1179 );
1180 }
1181
1182 #[test]
1183 fn test_finish_method() {
1184 let mut parser = VTPushParser::new();
1185 let mut result = String::new();
1186 let mut callback = |vt_input: VTEvent<'_>| {
1187 result.push_str(&format!("{:?}\n", vt_input));
1188 };
1189
1190 parser.feed_with(b"\x1b[1;2;3", &mut callback);
1192
1193 parser.finish(&mut callback);
1195
1196 assert_eq!(result.trim(), "");
1197 }
1198
1199 fn collect_events(input: &[u8]) -> Vec<String> {
1279 let mut out = Vec::new();
1280 let mut p = VTPushParser::new();
1281 p.feed_with(input, &mut |ev| out.push(format!("{:?}", ev)));
1282 out
1283 }
1284
1285 #[test]
1286 fn dcs_esc_esc_del() {
1287 let ev = collect_events(b"\x1bP1;2;3|\x1b\x1b\x7fdata\x1b\\");
1289 eprintln!("{ev:?}");
1291 }
1292
1293 #[test]
1294 fn dcs_header_with_colon_is_ignored_case1() {
1295 let ev = collect_events(b"\x1bP1:2qHELLO\x1b\\");
1297 assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1299 }
1300
1301 #[test]
1302 fn dcs_header_with_colon_is_ignored_case2() {
1303 let ev = collect_events(b"\x1bP:1qDATA\x1b\\");
1305 assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1306 }
1307
1308 #[test]
1309 fn dcs_header_with_colon_is_ignored_case3() {
1310 let ev = collect_events(b"\x1bP12:34!qPAYLOAD\x1b\\");
1312 assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1313 }
1314
1315 #[test]
1316 fn osc_aborted_by_can_mid_body() {
1317 let mut s = Vec::new();
1319 s.extend_from_slice(b"\x1b]0;Title");
1320 s.push(CAN);
1321 s.extend_from_slice(b"more\x07");
1322
1323 let ev = collect_debug(&s);
1324
1325 assert!(ev.iter().any(|e| e.starts_with("OscStart")), "{ev:#?}");
1330 assert!(!ev.iter().any(|e| e.starts_with("OscData")), "{ev:#?}");
1331 assert!(!ev.iter().any(|e| e.starts_with("OscEnd")), "{ev:#?}");
1332 }
1333
1334 #[test]
1335 fn osc_aborted_by_sub_before_terminator() {
1336 let mut s = Vec::new();
1337 s.extend_from_slice(b"\x1b]52;c;YWJjZA==");
1338 s.push(SUB); s.extend_from_slice(b"\x1b\\"); let ev = collect_debug(&s);
1342 assert!(ev.iter().any(|e| e.starts_with("OscStart")), "{ev:#?}");
1346 assert!(!ev.iter().any(|e| e.starts_with("OscData")), "{ev:#?}");
1347 assert!(!ev.iter().any(|e| e.starts_with("OscEnd")), "{ev:#?}");
1348 }
1349
1350 fn collect_debug(input: &[u8]) -> Vec<String> {
1352 let mut out = Vec::new();
1353 let mut p = VTPushParser::new();
1354 p.feed_with(input, &mut |ev| out.push(format!("{:?}", ev)));
1355 out
1356 }
1357
1358 #[test]
1359 fn dcs_aborted_by_can_before_body() {
1360 let mut s = Vec::new();
1362 s.extend_from_slice(b"\x1bPq"); s.push(CAN);
1364 s.extend_from_slice(b"IGNORED\x1b\\"); let ev = collect_debug(&s);
1367
1368 assert_eq!(ev.len(), 4, "{ev:#?}");
1369 assert_eq!(ev[0], "DcsStart(, '', q)");
1370 assert_eq!(ev[1], "DcsCancel");
1371 assert_eq!(ev[2], "Raw('IGNORED')");
1372 assert_eq!(ev[3], "Esc('', \\)");
1373 }
1374
1375 #[test]
1376 fn dcs_aborted_by_can_mid_body() {
1377 let mut s = Vec::new();
1379 s.extend_from_slice(b"\x1bPqABC");
1380 s.push(CAN);
1381 s.extend_from_slice(b"MORE\x1b\\"); let ev = collect_debug(&s);
1384
1385 assert_eq!(ev.len(), 4, "{ev:#?}");
1386 assert_eq!(ev[0], "DcsStart(, '', q)");
1387 assert_eq!(ev[1], "DcsCancel");
1388 assert_eq!(ev[2], "Raw('MORE')");
1389 assert_eq!(ev[3], "Esc('', \\)");
1390 }
1391
1392 #[test]
1395 fn spa_aborted_by_can_is_ignored() {
1396 let mut s = Vec::new();
1398 s.extend_from_slice(b"\x1b_hello");
1399 s.push(CAN);
1400 s.extend_from_slice(b"world\x1b\\");
1401
1402 let ev = collect_debug(&s);
1403 assert_eq!(ev.len(), 2, "{ev:#?}");
1404 assert_eq!(ev[0], "Raw('world')");
1405 assert_eq!(ev[1], "Esc('', \\)");
1406 }
1407
1408 #[test]
1409 fn spa_sub_aborts_too() {
1410 let mut s = Vec::new();
1411 s.extend_from_slice(b"\x1bXhello");
1412 s.push(SUB);
1413 s.extend_from_slice(b"world\x1b\\");
1414 let ev = collect_debug(&s);
1415 assert_eq!(ev.len(), 2, "{ev:#?}");
1416 assert_eq!(ev[0], "Raw('world')");
1417 assert_eq!(ev[1], "Esc('', \\)");
1418 }
1419
1420 #[test]
1423 fn can_in_ground_is_c0() {
1424 let mut s = Vec::new();
1425 s.extend_from_slice(b"abc");
1426 s.push(CAN);
1427 s.extend_from_slice(b"def");
1428 let ev = collect_debug(&s);
1429 assert_eq!(ev.len(), 3, "{ev:#?}");
1431 assert_eq!(ev[0], "Raw('abc')");
1432 assert_eq!(ev[1], "C0(18)");
1433 assert_eq!(ev[2], "Raw('def')");
1434 }
1435}