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 Buffer(VTEmit),
39 Hold(VTEmit),
42 Cancel(VTEmit),
44}
45
46#[derive(Debug, Copy, Clone, PartialEq, Eq)]
47pub enum VTEmit {
48 Ground,
50 Dcs,
52 Osc,
54}
55
56#[inline]
57const fn is_c0(b: u8) -> bool {
58 b <= 0x1F && b != b'\r' && b != b'\n' && b != b'\t'
60}
61#[inline]
62fn is_printable(b: u8) -> bool {
63 (0x20..=0x7E).contains(&b)
64}
65#[inline]
66fn is_intermediate(b: u8) -> bool {
67 (0x20..=0x2F).contains(&b)
68}
69#[inline]
70const fn is_final(b: u8) -> bool {
71 b >= 0x40 && b <= 0x7E
72}
73#[inline]
74fn is_digit(b: u8) -> bool {
75 (b'0'..=b'9').contains(&b)
76}
77#[inline]
78fn is_priv(b: u8) -> bool {
79 matches!(b, b'<' | b'=' | b'>' | b'?')
80}
81
82macro_rules! byte_predicate {
83 (|$p:ident| $body:block) => {{
84 let mut out: [bool; 256] = [false; 256];
85 let mut i = 0;
86 while i < 256 {
87 let $p: u8 = i as u8;
88 out[i] = $body;
89 i += 1;
90 }
91 out
92 }};
93}
94
95const ENDS_CSI: [bool; 256] =
96 byte_predicate!(|b| { is_final(b) || b == ESC || b == CAN || b == SUB });
97
98const ENDS_GROUND: [bool; 256] = byte_predicate!(|b| { is_c0(b) || b == DEL });
99
100#[derive(Debug, Copy, Clone, PartialEq, Eq)]
101enum State {
102 Ground,
103 Escape,
104 EscInt,
105 EscSs2,
106 EscSs3,
107 CsiEntry,
108 CsiParam,
109 CsiInt,
110 CsiIgnore,
111 DcsEntry,
112 DcsParam,
113 DcsInt,
114 DcsIgnore,
115 DcsIgnoreEsc,
116 DcsPassthrough,
117 DcsEsc,
118 OscString,
119 OscEsc,
120 SosPmApcString,
121 SpaEsc,
122}
123
124pub const VT_PARSER_INTEREST_NONE: u8 = 0;
125pub const VT_PARSER_INTEREST_CSI: u8 = 1 << 0;
127pub const VT_PARSER_INTEREST_DCS: u8 = 1 << 1;
129pub const VT_PARSER_INTEREST_OSC: u8 = 1 << 2;
131pub const VT_PARSER_INTEREST_OTHER: u8 = 1 << 3;
133pub const VT_PARSER_INTEREST_ALL: u8 = VT_PARSER_INTEREST_CSI
134 | VT_PARSER_INTEREST_DCS
135 | VT_PARSER_INTEREST_OSC
136 | VT_PARSER_INTEREST_OTHER;
137
138pub struct VTPushParser<const INTEREST: u8 = VT_PARSER_INTEREST_ALL> {
139 st: State,
140
141 ints: VTIntermediate,
143 params: Params,
144 cur_param: Param,
145 priv_prefix: Option<u8>,
146 held_byte: Option<u8>,
147 held_emit: Option<VTEmit>,
148}
149
150impl VTPushParser {
151 pub const fn new() -> Self {
152 VTPushParser::new_with()
153 }
154
155 pub fn decode_buffer<'a>(input: &'a [u8], mut cb: impl for<'b> FnMut(VTEvent<'b>)) {
157 let mut parser = VTPushParser::new();
158 parser.feed_with(input, &mut cb);
159 parser.finish(&mut cb);
160 }
161
162 pub const fn new_with_interest<const INTEREST: u8>() -> VTPushParser<INTEREST> {
163 VTPushParser::new_with()
164 }
165}
166
167impl<const INTEREST: u8> VTPushParser<INTEREST> {
168 const fn new_with() -> Self {
169 Self {
170 st: State::Ground,
171 ints: VTIntermediate::empty(),
172 params: SmallVec::new_const(),
173 cur_param: SmallVec::new_const(),
174 priv_prefix: None,
175 held_byte: None,
176 held_emit: None,
177 }
178 }
179
180 #[inline]
192 pub fn feed_with<'this: 'input, 'input, F: for<'any> FnMut(VTEvent<'any>)>(
193 &'this mut self,
194 mut input: &'input [u8],
195 cb: &mut F,
196 ) {
197 if input.is_empty() {
198 return;
199 }
200
201 struct FeedState {
202 buffer_idx: usize,
203 current_emit: Option<VTEmit>,
204 hold: bool,
205 }
206
207 let mut state = FeedState {
208 buffer_idx: 0,
209 current_emit: self.held_emit.take(),
210 hold: self.held_byte.is_some(),
211 };
212
213 macro_rules! emit {
214 ($state:ident, $i:expr, $cb:expr) => {
215 let hold = std::mem::take(&mut $state.hold);
216 if let Some(emit) = $state.current_emit.take() {
217 let mut i = $i;
218 if hold {
219 i = i - 1;
220 }
221
222 let range = $state.buffer_idx..i;
223 if range.len() > 0 {
224 match emit {
225 VTEmit::Ground => $cb(VTEvent::Raw(&input[range])),
226 VTEmit::Dcs => $cb(VTEvent::DcsData(&input[range])),
227 VTEmit::Osc => $cb(VTEvent::OscData(&input[range])),
228 }
229 }
230 }
231 };
232 }
233
234 let mut held_byte = self.held_byte.take();
235 let mut i = 0;
236
237 while i < input.len() {
238 if self.st == State::Ground {
240 let start = i;
241 loop {
242 if i >= input.len() {
243 cb(VTEvent::Raw(&input[start..]));
244 return;
245 }
246 if ENDS_GROUND[input[i] as usize] {
247 break;
248 }
249 i += 1;
250 }
251
252 if start != i {
253 cb(VTEvent::Raw(&input[start..i]));
254 }
255
256 if input[i] == ESC {
257 self.clear_hdr_collectors();
258 self.st = State::Escape;
259 i = i + 1;
260 continue;
261 }
262 }
263
264 if self.st == State::CsiIgnore {
266 loop {
267 if i >= input.len() {
268 return;
269 }
270 if ENDS_CSI[input[i] as usize] {
271 break;
272 }
273 i += 1;
274 }
275
276 if input[i] == ESC {
277 self.st = State::Escape;
278 } else {
279 self.st = State::Ground;
280 }
281 i += 1;
282 continue;
283 }
284
285 let action = self.push_with(input[i]);
286
287 match action {
288 VTAction::None => {
289 emit!(state, i, cb);
290 }
291 VTAction::Event(e) => {
292 emit!(state, i, cb);
293 cb(e);
294 }
295 VTAction::Buffer(emit) | VTAction::Hold(emit) => {
296 if i == 0 {
297 if let Some(h) = held_byte.take() {
298 match emit {
299 VTEmit::Ground => cb(VTEvent::Raw(&[h])),
300 VTEmit::Dcs => cb(VTEvent::DcsData(&[h])),
301 VTEmit::Osc => cb(VTEvent::OscData(&[h])),
302 }
303 }
304 }
305
306 debug_assert!(state.current_emit.is_none() || state.current_emit == Some(emit));
307
308 state.hold = matches!(action, VTAction::Hold(_));
309 if state.current_emit.is_none() {
310 state.buffer_idx = i;
311 state.current_emit = Some(emit);
312 }
313 }
314 VTAction::Cancel(emit) => {
315 state.current_emit = None;
316 state.hold = false;
317 match emit {
318 VTEmit::Ground => unreachable!(),
319 VTEmit::Dcs => cb(VTEvent::DcsCancel),
320 VTEmit::Osc => cb(VTEvent::OscCancel),
321 }
322 }
323 };
324 i += 1;
325 }
326
327 let hold = state.hold;
329 emit!(state, input.len(), cb);
330
331 if hold {
332 self.held_byte = Some(input[input.len() - 1]);
333 }
334 }
335
336 pub fn idle(&mut self) -> Option<VTEvent<'static>> {
340 match self.st {
341 State::Escape => {
342 self.st = State::Ground;
343 Some(VTEvent::C0(ESC))
344 }
345 State::EscInt | State::EscSs2 | State::EscSs3 => {
346 self.st = State::Ground;
347 None
348 }
349 _ => None,
350 }
351 }
352
353 fn push_with<'this, 'input>(&'this mut self, b: u8) -> VTAction<'this> {
354 use State::*;
355 match self.st {
356 Ground => self.on_ground(b),
357 Escape => self.on_escape(b),
358 EscInt => self.on_esc_int(b),
359 EscSs2 => self.on_esc_ss2(b),
360 EscSs3 => self.on_esc_ss3(b),
361
362 CsiEntry => self.on_csi_entry(b),
363 CsiParam => self.on_csi_param(b),
364 CsiInt => self.on_csi_int(b),
365 CsiIgnore => self.on_csi_ignore(b),
366
367 DcsEntry => self.on_dcs_entry(b),
368 DcsParam => self.on_dcs_param(b),
369 DcsInt => self.on_dcs_int(b),
370 DcsIgnore => self.on_dcs_ignore(b),
371 DcsIgnoreEsc => self.on_dcs_ignore_esc(b),
372 DcsPassthrough => self.on_dcs_pass(b),
373 DcsEsc => self.on_dcs_esc(b),
374
375 OscString => self.on_osc_string(b),
376 OscEsc => self.on_osc_esc(b),
377
378 SosPmApcString => self.on_spa_string(b),
379 SpaEsc => self.on_spa_esc(b),
380 }
381 }
382
383 pub fn finish<F: FnMut(VTEvent)>(&mut self, cb: &mut F) {
384 self.reset_collectors();
385 self.st = State::Ground;
386
387 }
389
390 fn clear_hdr_collectors(&mut self) {
395 self.ints.clear();
396 self.params.clear();
397 self.cur_param.clear();
398 self.priv_prefix = None;
399 }
400
401 fn reset_collectors(&mut self) {
402 self.clear_hdr_collectors();
403 }
404
405 fn next_param(&mut self) {
406 self.params.push(std::mem::take(&mut self.cur_param));
407 }
408
409 fn finish_params_if_any(&mut self) {
410 if !self.cur_param.is_empty() || !self.params.is_empty() {
411 self.next_param();
412 }
413 }
414
415 fn emit_csi(&mut self, final_byte: u8) -> VTAction {
416 self.finish_params_if_any();
417
418 let mut borrowed: SmallVec<[&[u8]; 4]> = SmallVec::new();
420 borrowed.extend(self.params.iter().map(|v| v.as_slice()));
421
422 let privp = self.priv_prefix.take();
423 VTAction::Event(VTEvent::Csi {
424 private: privp,
425 params: ParamBuf {
426 params: &self.params,
427 },
428 intermediates: self.ints,
429 final_byte,
430 })
431 }
432
433 fn dcs_start(&mut self, final_byte: u8) -> VTAction {
434 self.finish_params_if_any();
435
436 let privp = self.priv_prefix.take();
437 VTAction::Event(VTEvent::DcsStart {
438 private: privp,
439 params: ParamBuf {
440 params: &self.params,
441 },
442 intermediates: self.ints,
443 final_byte,
444 })
445 }
446
447 fn on_ground(&mut self, b: u8) -> VTAction {
452 match b {
453 ESC => {
454 self.clear_hdr_collectors();
455 self.st = State::Escape;
456 VTAction::None
457 }
458 DEL => VTAction::Event(VTEvent::C0(DEL)),
459 c if is_c0(c) => VTAction::Event(VTEvent::C0(c)),
460 p if is_printable(p) => VTAction::Buffer(VTEmit::Ground),
461 _ => VTAction::Buffer(VTEmit::Ground), }
463 }
464
465 fn on_escape(&mut self, b: u8) -> VTAction {
466 use State::*;
467 match b {
468 CAN | SUB => {
469 self.st = Ground;
470 VTAction::None
471 }
472 DEL => VTAction::None,
473 c if is_intermediate(c) => {
474 if self.ints.push(c) {
475 self.st = EscInt;
476 } else {
477 self.st = Ground;
478 }
479 VTAction::None
480 }
481 CSI => {
482 if INTEREST & VT_PARSER_INTEREST_CSI == 0 {
483 self.st = CsiIgnore;
484 } else {
485 self.st = CsiEntry;
486 }
487 VTAction::None
488 }
489 DCS => {
490 if INTEREST & VT_PARSER_INTEREST_DCS == 0 {
491 self.st = DcsIgnore;
492 } else {
493 self.st = DcsEntry;
494 }
495 VTAction::None
496 }
497 OSC => {
498 self.st = OscString;
499 VTAction::Event(VTEvent::OscStart)
500 }
501 SS2 => {
502 self.st = EscSs2;
503 VTAction::None
504 }
505 SS3 => {
506 self.st = EscSs3;
507 VTAction::None
508 }
509 b'X' | b'^' | b'_' => {
510 self.st = State::SosPmApcString;
511 VTAction::None
512 }
513 c if is_final(c) || is_digit(c) => {
514 self.st = Ground;
515 VTAction::Event(VTEvent::Esc {
516 intermediates: self.ints,
517 final_byte: c,
518 })
519 }
520 ESC => {
521 VTAction::Event(VTEvent::C0(ESC))
523 }
524 _ => {
525 self.st = Ground;
526 VTAction::None
527 }
528 }
529 }
530
531 fn on_esc_int(&mut self, b: u8) -> VTAction {
532 use State::*;
533 match b {
534 CAN | SUB => {
535 self.st = Ground;
536 VTAction::None
537 }
538 DEL => VTAction::None,
539 c if is_intermediate(c) => {
540 if !self.ints.push(c) {
541 self.st = Ground;
542 }
543 VTAction::None
544 }
545 c if is_final(c) || is_digit(c) => {
546 self.st = Ground;
547 VTAction::Event(VTEvent::Esc {
548 intermediates: self.ints,
549 final_byte: c,
550 })
551 }
552 _ => {
553 self.st = Ground;
554 VTAction::None
555 }
556 }
557 }
558
559 fn on_esc_ss2(&mut self, b: u8) -> VTAction {
560 use State::*;
561 self.st = Ground;
562 match b {
563 CAN | SUB => VTAction::None,
564 c => VTAction::Event(VTEvent::Ss2 { char: c }),
565 }
566 }
567
568 fn on_esc_ss3(&mut self, b: u8) -> VTAction {
569 use State::*;
570 self.st = Ground;
571 match b {
572 CAN | SUB => VTAction::None,
573 c => VTAction::Event(VTEvent::Ss3 { char: c }),
574 }
575 }
576
577 fn on_csi_entry(&mut self, b: u8) -> VTAction {
579 use State::*;
580 match b {
581 CAN | SUB => {
582 self.st = Ground;
583 VTAction::None
584 }
585 DEL => VTAction::None,
586 ESC => {
587 self.st = Escape;
588 VTAction::None
589 }
590 c if is_priv(c) => {
591 self.priv_prefix = Some(c);
592 self.st = CsiParam;
593 VTAction::None
594 }
595 d if is_digit(d) => {
596 self.cur_param.push(d);
597 self.st = CsiParam;
598 VTAction::None
599 }
600 b';' => {
601 self.next_param();
602 self.st = CsiParam;
603 VTAction::None
604 }
605 b':' => {
606 self.cur_param.push(b':');
607 self.st = CsiParam;
608 VTAction::None
609 }
610 c if is_intermediate(c) => {
611 if self.ints.push(c) {
612 self.st = CsiInt;
613 } else {
614 self.st = Ground;
615 }
616 VTAction::None
617 }
618 c if is_final(c) => {
619 self.st = Ground;
620 self.emit_csi(c)
621 }
622 _ => {
623 self.st = CsiIgnore;
624 VTAction::None
625 }
626 }
627 }
628
629 fn on_csi_param(&mut self, b: u8) -> VTAction {
630 use State::*;
631 match b {
632 CAN | SUB => {
633 self.st = Ground;
634 VTAction::None
635 }
636 DEL => VTAction::None,
637 ESC => {
638 self.st = Escape;
639 VTAction::None
640 }
641 d if is_digit(d) => {
642 self.cur_param.push(d);
643 VTAction::None
644 }
645 b';' => {
646 self.next_param();
647 VTAction::None
648 }
649 b':' => {
650 self.cur_param.push(b':');
651 VTAction::None
652 }
653 c if is_intermediate(c) => {
654 if self.ints.push(c) {
655 self.st = CsiInt;
656 } else {
657 self.st = Ground;
658 }
659 VTAction::None
660 }
661 c if is_final(c) => {
662 self.st = Ground;
663 self.emit_csi(c)
664 }
665 _ => {
666 self.st = CsiIgnore;
667 VTAction::None
668 }
669 }
670 }
671
672 fn on_csi_int(&mut self, b: u8) -> VTAction {
673 use State::*;
674 match b {
675 CAN | SUB => {
676 self.st = Ground;
677 VTAction::None
678 }
679 DEL => VTAction::None,
680 ESC => {
681 self.st = Escape;
682 VTAction::None
683 }
684 c if is_intermediate(c) => {
685 if self.ints.push(c) {
686 self.st = CsiInt;
687 } else {
688 self.st = Ground;
689 }
690 VTAction::None
691 }
692 c if is_final(c) => {
693 self.st = Ground;
694 self.emit_csi(c)
695 }
696 _ => {
697 self.st = CsiIgnore;
698 VTAction::None
699 }
700 }
701 }
702
703 fn on_csi_ignore(&mut self, b: u8) -> VTAction {
704 use State::*;
705 match b {
706 CAN | SUB => {
707 self.st = Ground;
708 VTAction::None
709 }
710 DEL => VTAction::None,
711 ESC => {
712 self.st = Escape;
713 VTAction::None
714 }
715 c if is_final(c) => {
716 self.st = Ground;
717 VTAction::None
718 }
719 _ => VTAction::None,
720 }
721 }
722
723 fn on_dcs_entry(&mut self, b: u8) -> VTAction {
725 use State::*;
726 match b {
727 CAN | SUB => {
728 self.st = Ground;
729 VTAction::None
730 }
731 DEL => VTAction::None,
732 ESC => {
733 self.st = Escape;
734 VTAction::None
735 }
736 c if is_priv(c) => {
737 self.priv_prefix = Some(c);
738 self.st = DcsParam;
739 VTAction::None
740 }
741 d if is_digit(d) => {
742 self.cur_param.push(d);
743 self.st = DcsParam;
744 VTAction::None
745 }
746 b';' => {
747 self.next_param();
748 self.st = DcsParam;
749 VTAction::None
750 }
751 b':' => {
752 self.st = DcsIgnore;
753 VTAction::None
754 }
755 c if is_intermediate(c) => {
756 if self.ints.push(c) {
757 self.st = DcsInt;
758 } else {
759 self.st = Ground;
760 }
761 VTAction::None
762 }
763 c if is_final(c) => {
764 self.st = DcsPassthrough;
765 self.dcs_start(c)
766 }
767 _ => {
768 self.st = DcsIgnore;
769 VTAction::None
770 }
771 }
772 }
773
774 fn on_dcs_param(&mut self, b: u8) -> VTAction {
775 use State::*;
776 match b {
777 CAN | SUB => {
778 self.st = Ground;
779 VTAction::None
780 }
781 DEL => VTAction::None,
782 ESC => {
783 self.st = Escape;
784 VTAction::None
785 }
786 d if is_digit(d) => {
787 self.cur_param.push(d);
788 VTAction::None
789 }
790 b';' => {
791 self.next_param();
792 VTAction::None
793 }
794 b':' => {
795 self.st = DcsIgnore;
796 VTAction::None
797 }
798 c if is_intermediate(c) => {
799 if self.ints.push(c) {
800 self.st = DcsInt;
801 } else {
802 self.st = Ground;
803 }
804 self.st = DcsInt;
805 VTAction::None
806 }
807 c if is_final(c) => {
808 self.st = DcsPassthrough;
809 self.dcs_start(c)
810 }
811 _ => {
812 self.st = DcsIgnore;
813 VTAction::None
814 }
815 }
816 }
817
818 fn on_dcs_int(&mut self, b: u8) -> VTAction {
819 use State::*;
820 match b {
821 CAN | SUB => {
822 self.st = Ground;
823 VTAction::None
824 }
825 DEL => VTAction::None,
826 ESC => {
827 self.st = Escape;
828 VTAction::None
829 }
830 c if is_intermediate(c) => {
831 if self.ints.push(c) {
832 self.st = DcsInt;
833 } else {
834 self.st = Ground;
835 }
836 VTAction::None
837 }
838 c if is_final(c) || is_digit(c) || c == b':' || c == b';' => {
839 self.st = DcsPassthrough;
840 self.dcs_start(c)
841 }
842 _ => {
843 self.st = DcsIgnore;
844 VTAction::None
845 }
846 }
847 }
848
849 fn on_dcs_ignore(&mut self, b: u8) -> VTAction {
850 use State::*;
851 match b {
852 CAN | SUB => {
853 self.st = Ground;
854 VTAction::None
855 }
856 DEL => VTAction::None,
857 ESC => {
858 self.st = DcsIgnoreEsc;
859 VTAction::None
860 }
861 _ => VTAction::None,
862 }
863 }
864
865 fn on_dcs_ignore_esc(&mut self, b: u8) -> VTAction {
866 use State::*;
867 match b {
868 CAN | SUB => {
869 self.st = Ground;
870 VTAction::None
871 }
872 ST_FINAL => {
873 self.st = Ground;
874 VTAction::None
875 }
876 DEL => VTAction::None,
877 ESC => VTAction::None,
878 _ => {
879 self.st = DcsIgnore;
880 VTAction::None
881 }
882 }
883 }
884
885 fn on_dcs_pass(&mut self, b: u8) -> VTAction {
886 use State::*;
887 match b {
888 CAN | SUB => {
889 self.st = Ground;
890 VTAction::Cancel(VTEmit::Dcs)
891 }
892 DEL => VTAction::None,
893 ESC => {
894 self.st = DcsEsc;
895 VTAction::Hold(VTEmit::Dcs)
896 }
897 _ => VTAction::Buffer(VTEmit::Dcs),
898 }
899 }
900
901 fn on_dcs_esc(&mut self, b: u8) -> VTAction {
902 use State::*;
903 match b {
904 ST_FINAL => {
905 self.st = Ground;
906 VTAction::Event(VTEvent::DcsEnd)
907 }
908 ESC => {
909 VTAction::Hold(VTEmit::Dcs)
911 }
912 _ => {
913 self.st = DcsPassthrough;
915 VTAction::Buffer(VTEmit::Dcs)
916 }
917 }
918 }
919
920 fn on_osc_string(&mut self, b: u8) -> VTAction {
922 use State::*;
923 match b {
924 CAN | SUB => {
925 self.st = Ground;
926 VTAction::Cancel(VTEmit::Osc)
927 }
928 DEL => VTAction::None,
929 BEL => {
930 self.st = Ground;
931 VTAction::Event(VTEvent::OscEnd { used_bel: true })
932 }
933 ESC => {
934 self.st = OscEsc;
935 VTAction::Hold(VTEmit::Osc)
936 }
937 p if is_printable(p) => VTAction::Buffer(VTEmit::Osc),
938 _ => VTAction::None, }
940 }
941
942 fn on_osc_esc(&mut self, b: u8) -> VTAction {
943 use State::*;
944 match b {
945 ST_FINAL => {
946 self.st = Ground;
947 VTAction::Event(VTEvent::OscEnd { used_bel: false })
948 } ESC => VTAction::Hold(VTEmit::Osc),
950 _ => {
951 self.st = OscString;
952 VTAction::Buffer(VTEmit::Osc)
953 }
954 }
955 }
956
957 fn on_spa_string(&mut self, b: u8) -> VTAction {
959 use State::*;
960 match b {
961 CAN | SUB => {
962 self.st = Ground;
963 VTAction::None
964 }
965 DEL => VTAction::None,
966 ESC => {
967 self.st = SpaEsc;
968 VTAction::None
969 }
970 _ => VTAction::None,
971 }
972 }
973
974 fn on_spa_esc(&mut self, b: u8) -> VTAction {
975 use State::*;
976 match b {
977 ST_FINAL => {
978 self.st = Ground;
979 VTAction::None
980 }
981 ESC => {
982 VTAction::None
984 }
985 _ => {
986 self.st = State::SosPmApcString;
987 VTAction::None
988 }
989 }
990 }
991}
992
993#[cfg(test)]
994mod tests {
995 use pretty_assertions::assert_eq;
996
997 use super::*;
998
999 #[test]
1000 fn test_edge_cases() {
1001 let mut result = String::new();
1003 VTPushParser::decode_buffer(&[], |e| result.push_str(&format!("{:?}\n", e)));
1004 assert_eq!(result.trim(), "");
1005
1006 let mut result = String::new();
1008 VTPushParser::decode_buffer(b"\x1b", |e| result.push_str(&format!("{:?}\n", e)));
1009 assert_eq!(result.trim(), "");
1010
1011 let mut result = String::new();
1013 VTPushParser::decode_buffer(b"\x1b[", |e| result.push_str(&format!("{:?}\n", e)));
1014 assert_eq!(result.trim(), "");
1015
1016 let mut result = String::new();
1018 VTPushParser::decode_buffer(b"\x1bP", |e| result.push_str(&format!("{:?}\n", e)));
1019 assert_eq!(result.trim(), "");
1020
1021 let mut result = String::new();
1023 VTPushParser::decode_buffer(b"\x1b]", |e| result.push_str(&format!("{:?}\n", e)));
1024 assert_eq!(result.trim(), "OscStart");
1025 }
1026
1027 #[test]
1028 fn test_streaming_behavior() {
1029 let mut parser = VTPushParser::new(); let mut result = String::new();
1032 let mut callback = |vt_input: VTEvent<'_>| {
1033 result.push_str(&format!("{:?}\n", vt_input));
1034 };
1035
1036 parser.feed_with(b"\x1bP1;2;3 |", &mut callback);
1038 parser.feed_with(b"data", &mut callback);
1039 parser.feed_with(b" more", &mut callback);
1040 parser.feed_with(b"\x1b\\", &mut callback);
1041
1042 assert_eq!(
1043 result.trim(),
1044 "DcsStart(, '1', '2', '3', ' ', |)\nDcsData('data')\nDcsData(' more')\nDcsEnd"
1045 );
1046 }
1047
1048 #[test]
1049 fn test_finish_method() {
1050 let mut parser = VTPushParser::new();
1051 let mut result = String::new();
1052 let mut callback = |vt_input: VTEvent<'_>| {
1053 result.push_str(&format!("{:?}\n", vt_input));
1054 };
1055
1056 parser.feed_with(b"\x1b[1;2;3", &mut callback);
1058
1059 parser.finish(&mut callback);
1061
1062 assert_eq!(result.trim(), "");
1063 }
1064
1065 #[test]
1066 fn test_dcs_payload_passthrough() {
1067 let dcs_cases: &[(&[u8], &str)] = &[
1075 (b"\x1bPq\x1b[38:2:12:34:56m\x1b\\", "<ESC>[38:2:12:34:56m"),
1077 (b"\x1bPq\x1b[48:2:0:0:0m;xyz\x1b\\", "<ESC>[48:2:0:0:0m;xyz"),
1079 (
1081 b"\x1bP1$r\x1b[38:2:10:20:30;58:2::200:100:0m\x1b\\",
1082 "<ESC>[38:2:10:20:30;58:2::200:100:0m",
1083 ),
1084 (
1086 b"\x1bPqABC\x1b\x1bDEF\x1bXG\x1b\\",
1087 "ABC<ESC><ESC>DEF<ESC>XG",
1088 ),
1089 (b"\x1bPqDATA\x07MORE\x1b\\", "DATA<BEL>MORE"),
1091 (b"\x1bP!|\x1b[38:5:208m\x1b\\", "<ESC>[38:5:208m"),
1093 (b"\x1bP>|Hello world\x1b\\", "Hello world"),
1095 (
1097 b"\x1bPq\x1b[38:2:1:2:3m\x1b[48:5:17m\x1b\\",
1098 "<ESC>[38:2:1:2:3m<ESC>[48:5:17m",
1099 ),
1100 (
1102 b"\x1bPq\x1b[58:2::000:007:042m\x1b\\",
1103 "<ESC>[58:2::000:007:042m",
1104 ),
1105 ];
1106
1107 for (input, expected_body) in dcs_cases {
1108 let events = collect_events(input);
1109
1110 let mut actual_body = String::new();
1112 for event in &events {
1113 if let Some(data_part) = event
1114 .strip_prefix("DcsData('")
1115 .and_then(|s| s.strip_suffix("')"))
1116 {
1117 actual_body
1118 .push_str(&data_part.replace("\x1b", "<ESC>").replace("\x07", "<BEL>"));
1119 }
1120 }
1121
1122 assert_eq!(
1123 actual_body, *expected_body,
1124 "DCS payload mismatch for input {:?}. Full events: {:#?}",
1125 input, events
1126 );
1127
1128 assert!(
1130 events.iter().any(|e| e.starts_with("DcsStart")),
1131 "Missing DcsStart for input {:?}. Events: {:#?}",
1132 input,
1133 events
1134 );
1135 assert!(
1136 events.iter().any(|e| e == "DcsEnd"),
1137 "Missing DcsEnd for input {:?}. Events: {:#?}",
1138 input,
1139 events
1140 );
1141 }
1142 }
1143
1144 fn collect_events(input: &[u8]) -> Vec<String> {
1145 let mut out = Vec::new();
1146 let mut p = VTPushParser::new();
1147 p.feed_with(input, &mut |ev| out.push(format!("{:?}", ev)));
1148 out
1149 }
1150
1151 #[test]
1152 fn dcs_header_with_colon_is_ignored_case1() {
1153 let ev = collect_events(b"\x1bP1:2qHELLO\x1b\\");
1155 assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1157 }
1158
1159 #[test]
1160 fn dcs_header_with_colon_is_ignored_case2() {
1161 let ev = collect_events(b"\x1bP:1qDATA\x1b\\");
1163 assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1164 }
1165
1166 #[test]
1167 fn dcs_header_with_colon_is_ignored_case3() {
1168 let ev = collect_events(b"\x1bP12:34!qPAYLOAD\x1b\\");
1170 assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1171 }
1172
1173 #[test]
1174 fn osc_aborted_by_can_mid_body() {
1175 let mut s = Vec::new();
1177 s.extend_from_slice(b"\x1b]0;Title");
1178 s.push(CAN);
1179 s.extend_from_slice(b"more\x07");
1180
1181 let ev = collect_debug(&s);
1182
1183 assert!(ev.iter().any(|e| e.starts_with("OscStart")), "{ev:#?}");
1188 assert!(!ev.iter().any(|e| e.starts_with("OscData")), "{ev:#?}");
1189 assert!(!ev.iter().any(|e| e.starts_with("OscEnd")), "{ev:#?}");
1190 }
1191
1192 #[test]
1193 fn osc_aborted_by_sub_before_terminator() {
1194 let mut s = Vec::new();
1195 s.extend_from_slice(b"\x1b]52;c;YWJjZA==");
1196 s.push(SUB); s.extend_from_slice(b"\x1b\\"); let ev = collect_debug(&s);
1200 assert!(ev.iter().any(|e| e.starts_with("OscStart")), "{ev:#?}");
1204 assert!(!ev.iter().any(|e| e.starts_with("OscData")), "{ev:#?}");
1205 assert!(!ev.iter().any(|e| e.starts_with("OscEnd")), "{ev:#?}");
1206 }
1207
1208 fn collect_debug(input: &[u8]) -> Vec<String> {
1210 let mut out = Vec::new();
1211 let mut p = VTPushParser::new();
1212 p.feed_with(input, &mut |ev| out.push(format!("{:?}", ev)));
1213 out
1214 }
1215
1216 #[test]
1217 fn dcs_aborted_by_can_before_body() {
1218 let mut s = Vec::new();
1220 s.extend_from_slice(b"\x1bPq"); s.push(CAN);
1222 s.extend_from_slice(b"IGNORED\x1b\\"); let ev = collect_debug(&s);
1225
1226 assert_eq!(ev.len(), 4, "{ev:#?}");
1227 assert_eq!(ev[0], "DcsStart(, '', q)");
1228 assert_eq!(ev[1], "DcsCancel");
1229 assert_eq!(ev[2], "Raw('IGNORED')");
1230 assert_eq!(ev[3], "Esc('', \\)");
1231 }
1232
1233 #[test]
1234 fn dcs_aborted_by_can_mid_body() {
1235 let mut s = Vec::new();
1237 s.extend_from_slice(b"\x1bPqABC");
1238 s.push(CAN);
1239 s.extend_from_slice(b"MORE\x1b\\"); let ev = collect_debug(&s);
1242
1243 assert_eq!(ev.len(), 4, "{ev:#?}");
1244 assert_eq!(ev[0], "DcsStart(, '', q)");
1245 assert_eq!(ev[1], "DcsCancel");
1246 assert_eq!(ev[2], "Raw('MORE')");
1247 assert_eq!(ev[3], "Esc('', \\)");
1248 }
1249
1250 #[test]
1253 fn spa_aborted_by_can_is_ignored() {
1254 let mut s = Vec::new();
1256 s.extend_from_slice(b"\x1b_hello");
1257 s.push(CAN);
1258 s.extend_from_slice(b"world\x1b\\");
1259
1260 let ev = collect_debug(&s);
1261 assert_eq!(ev.len(), 2, "{ev:#?}");
1262 assert_eq!(ev[0], "Raw('world')");
1263 assert_eq!(ev[1], "Esc('', \\)");
1264 }
1265
1266 #[test]
1267 fn spa_sub_aborts_too() {
1268 let mut s = Vec::new();
1269 s.extend_from_slice(b"\x1bXhello");
1270 s.push(SUB);
1271 s.extend_from_slice(b"world\x1b\\");
1272 let ev = collect_debug(&s);
1273 assert_eq!(ev.len(), 2, "{ev:#?}");
1274 assert_eq!(ev[0], "Raw('world')");
1275 assert_eq!(ev[1], "Esc('', \\)");
1276 }
1277
1278 #[test]
1281 fn can_in_ground_is_c0() {
1282 let mut s = Vec::new();
1283 s.extend_from_slice(b"abc");
1284 s.push(CAN);
1285 s.extend_from_slice(b"def");
1286 let ev = collect_debug(&s);
1287 assert_eq!(ev.len(), 3, "{ev:#?}");
1289 assert_eq!(ev[0], "Raw('abc')");
1290 assert_eq!(ev[1], "C0(18)");
1291 assert_eq!(ev[2], "Raw('def')");
1292 }
1293}