1use std::ops::Range;
2
3use smallvec::SmallVec;
4
5const ESC: u8 = 0x1B;
6const BEL: u8 = 0x07;
7const DEL: u8 = 0x7F;
8const CAN: u8 = 0x18;
9const SUB: u8 = 0x1A;
10const CSI: u8 = b'[';
11const OSC: u8 = b']';
12const SS3: u8 = b'O';
13const DCS: u8 = b'P';
14const ST_FINAL: u8 = b'\\';
15
16#[derive(Default, Clone, Copy, PartialEq, Eq)]
17pub struct VTIntermediate {
18 data: [u8; 2],
19}
20
21impl VTIntermediate {
22 pub const fn empty() -> Self {
23 Self { data: [0, 0] }
24 }
25
26 pub const fn one(c: u8) -> Self {
27 assert!(c >= 0x20 && c <= 0x2F);
28 Self { data: [c, 0] }
29 }
30
31 pub const fn two(c1: u8, c2: u8) -> Self {
32 assert!(c1 >= 0x20 && c1 <= 0x2F);
33 assert!(c2 >= 0x20 && c2 <= 0x2F);
34 Self { data: [c1, c2] }
35 }
36
37 pub fn has(&self, c: u8) -> bool {
38 self.data[0] == c || self.data[1] == c
39 }
40
41 pub fn clear(&mut self) {
42 self.data[0] = 0;
43 self.data[1] = 0;
44 }
45
46 pub fn is_empty(&self) -> bool {
47 self.data[0] == 0 && self.data[1] == 0
48 }
49
50 pub fn len(&self) -> usize {
51 self.data.iter().filter(|&&c| c != 0).count()
52 }
53
54 #[must_use]
55 pub fn push(&mut self, c: u8) -> bool {
56 if c < 0x20 || c > 0x2F {
57 return false;
58 }
59
60 if self.data[0] == 0 {
61 self.data[0] = c;
62 true
63 } else if self.data[1] == 0 {
64 self.data[1] = c;
65 true
66 } else {
67 false
68 }
69 }
70
71 const fn const_eq(&self, other: &Self) -> bool {
72 self.data[0] == other.data[0] && self.data[1] == other.data[1]
73 }
74}
75
76impl std::fmt::Debug for VTIntermediate {
77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78 write!(f, "'")?;
80 for c in self.data.iter() {
81 if *c == 0 {
82 break;
83 }
84 write!(f, "{}", *c as char)?;
85 }
86 write!(f, "'")?;
87 Ok(())
88 }
89}
90
91pub struct VTEscapeSignature {
93 pub prefix: u8,
94 pub private: Option<u8>,
95 pub intermediates: VTIntermediate,
96 pub final_byte: u8,
97 pub param_count: Range<u8>,
98}
99
100impl VTEscapeSignature {
101 pub const fn csi(
102 private: Option<u8>,
103 param_count: Range<u8>,
104 intermediates: VTIntermediate,
105 final_byte: u8,
106 ) -> Self {
107 Self {
108 prefix: CSI,
109 private,
110 intermediates,
111 final_byte,
112 param_count,
113 }
114 }
115
116 pub const fn ss3(intermediates: VTIntermediate, final_byte: u8) -> Self {
117 Self {
118 prefix: SS3,
119 private: None,
120 intermediates,
121 final_byte,
122 param_count: u8::MIN..u8::MAX,
123 }
124 }
125
126 pub const fn dcs(
127 priv_prefix: Option<u8>,
128 param_count: Range<u8>,
129 intermediates: VTIntermediate,
130 final_byte: u8,
131 ) -> Self {
132 Self {
133 prefix: DCS,
134 private: priv_prefix,
135 intermediates,
136 final_byte,
137 param_count,
138 }
139 }
140
141 pub const fn osc(intermediates: VTIntermediate, final_byte: u8) -> Self {
142 Self {
143 prefix: OSC,
144 private: None,
145 intermediates,
146 final_byte,
147 param_count: u8::MIN..u8::MAX,
148 }
149 }
150
151 pub fn matches(&self, entry: &VTEvent) -> bool {
152 match entry {
154 VTEvent::Esc {
155 intermediates,
156 final_byte,
157 } => self.final_byte == *final_byte && self.intermediates.const_eq(intermediates),
158 VTEvent::Csi {
159 private,
160 params,
161 intermediates,
162 final_byte,
163 } => {
164 self.prefix == CSI
165 && self.final_byte == *final_byte
166 && self.intermediates.const_eq(intermediates)
167 && self.const_private_eq(private)
168 && self.const_contains(params.len())
169 }
170 VTEvent::Ss3 {
171 intermediates,
172 final_byte,
173 } => {
174 self.prefix == SS3
175 && self.final_byte == *final_byte
176 && self.intermediates.const_eq(intermediates)
177 }
178 VTEvent::DcsStart {
179 priv_prefix,
180 params,
181 intermediates,
182 final_byte,
183 } => {
184 self.prefix == DCS
185 && self.final_byte == *final_byte
186 && self.intermediates.const_eq(intermediates)
187 && self.private == *priv_prefix
188 && self.const_contains(params.len())
189 }
190 _ => false,
191 }
192 }
193
194 const fn const_private_eq(&self, other: &Option<u8>) -> bool {
195 match (self.private, other) {
196 (Some(a), Some(b)) => a == *b,
197 (None, None) => true,
198 _ => false,
199 }
200 }
201
202 fn const_contains(&self, len: usize) -> bool {
203 self.param_count.contains(&(len as u8))
205 }
206}
207
208pub enum VTEvent<'a> {
209 Raw(&'a [u8]),
211
212 C0(u8),
214
215 Esc {
217 intermediates: VTIntermediate,
218 final_byte: u8,
219 },
220
221 Csi {
223 private: Option<u8>,
224 params: smallvec::SmallVec<[&'a [u8]; 4]>,
225 intermediates: VTIntermediate,
226 final_byte: u8,
227 },
228
229 Ss3 {
231 intermediates: VTIntermediate,
232 final_byte: u8,
233 },
234
235 DcsStart {
237 priv_prefix: Option<u8>,
238 params: smallvec::SmallVec<[&'a [u8]; 4]>,
239 intermediates: VTIntermediate,
240 final_byte: u8,
241 },
242 DcsData(&'a [u8]),
243 DcsEnd,
244 DcsCancel,
245
246 OscStart,
248 OscData(&'a [u8]),
249 OscEnd {
250 used_bel: bool,
251 },
252 OscCancel,
253}
254
255impl<'a> std::fmt::Debug for VTEvent<'a> {
256 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
257 match self {
258 VTEvent::Raw(s) => {
259 write!(f, "Raw('")?;
260 for chunk in s.utf8_chunks() {
261 write!(f, "{}", chunk.valid())?;
262 if !chunk.invalid().is_empty() {
263 write!(f, "<{}>", hex::encode(chunk.invalid()))?;
264 }
265 }
266 write!(f, "')")?;
267 Ok(())
268 }
269 VTEvent::C0(b) => write!(f, "C0({:02x})", b),
270 VTEvent::Esc {
271 intermediates,
272 final_byte,
273 } => {
274 write!(f, "Esc({:?}", intermediates)?;
275 write!(f, ", {})", *final_byte as char)?;
276 Ok(())
277 }
278 VTEvent::Csi {
279 private,
280 params,
281 intermediates,
282 final_byte,
283 } => {
284 write!(f, "Csi(")?;
285 if let Some(p) = private {
286 write!(f, "{:?}", *p as char)?;
287 }
288 for param in params {
289 write!(f, ", '")?;
290 for chunk in param.utf8_chunks() {
291 write!(f, "{}", chunk.valid())?;
292 if !chunk.invalid().is_empty() {
293 write!(f, "<{}>", hex::encode(chunk.invalid()))?;
294 }
295 }
296 write!(f, "'")?;
297 }
298 write!(f, ", {:?}", intermediates)?;
299 write!(f, ", {:?})", *final_byte as char)?;
300 Ok(())
301 }
302 VTEvent::Ss3 {
303 intermediates,
304 final_byte,
305 } => {
306 write!(f, "Ss3(")?;
307 write!(f, "{:?}", intermediates)?;
308 write!(f, ", {})", *final_byte as char)?;
309 Ok(())
310 }
311 VTEvent::DcsStart {
312 priv_prefix,
313 params,
314 intermediates,
315 final_byte,
316 } => {
317 write!(f, "DcsStart(")?;
318 if let Some(p) = priv_prefix {
319 write!(f, "{:?}", *p as char)?;
320 }
321 for param in params {
322 write!(f, ", '")?;
323 for chunk in param.utf8_chunks() {
324 write!(f, "{}", chunk.valid())?;
325 if !chunk.invalid().is_empty() {
326 write!(f, "<{}>", hex::encode(chunk.invalid()))?;
327 }
328 }
329 write!(f, "'")?;
330 }
331 write!(f, ", {:?}", intermediates)?;
332 write!(f, ", {})", *final_byte as char)?;
333 Ok(())
334 }
335 VTEvent::DcsData(s) => {
336 write!(f, "DcsData('")?;
337 for chunk in s.utf8_chunks() {
338 write!(f, "{}", chunk.valid())?;
339 if !chunk.invalid().is_empty() {
340 write!(f, "<{}>", hex::encode(chunk.invalid()))?;
341 }
342 }
343 write!(f, "')")?;
344 Ok(())
345 }
346 VTEvent::DcsEnd => write!(f, "DcsEnd"),
347 VTEvent::DcsCancel => write!(f, "DcsCancel"),
348 VTEvent::OscStart => write!(f, "OscStart"),
349 VTEvent::OscData(s) => {
350 write!(f, "OscData('")?;
351 for chunk in s.utf8_chunks() {
352 write!(f, "{}", chunk.valid())?;
353 if !chunk.invalid().is_empty() {
354 write!(f, "<{}>", hex::encode(chunk.invalid()))?;
355 }
356 }
357 write!(f, "')")?;
358 Ok(())
359 }
360 VTEvent::OscEnd { .. } => {
361 write!(f, "OscEnd")?;
362 Ok(())
363 }
364 VTEvent::OscCancel => write!(f, "OscCancel"),
365 }
366 }
367}
368
369#[inline]
370fn is_c0(b: u8) -> bool {
371 b <= 0x1F
372}
373#[inline]
374fn is_printable(b: u8) -> bool {
375 (0x20..=0x7E).contains(&b)
376}
377#[inline]
378fn is_intermediate(b: u8) -> bool {
379 (0x20..=0x2F).contains(&b)
380}
381#[inline]
382fn is_final(b: u8) -> bool {
383 (0x30..=0x7E).contains(&b)
384}
385#[inline]
386fn is_digit(b: u8) -> bool {
387 (b'0'..=b'9').contains(&b)
388}
389#[inline]
390fn is_priv(b: u8) -> bool {
391 matches!(b, b'<' | b'=' | b'>' | b'?')
392}
393
394#[derive(Debug, Copy, Clone, PartialEq, Eq)]
395enum State {
396 Ground,
397 Escape,
398 EscInt,
399 CsiEntry,
400 CsiParam,
401 CsiInt,
402 CsiIgnore,
403 DcsEntry,
404 DcsParam,
405 DcsInt,
406 DcsIgnore,
407 DcsIgnoreEsc,
408 DcsPassthrough,
409 DcsEsc,
410 OscString,
411 OscEsc,
412 SosPmApcString,
413 SpaEsc,
414}
415
416pub struct VTPushParser {
417 st: State,
418
419 raw_buf: Vec<u8>,
421
422 ints: VTIntermediate,
424 params: Vec<Vec<u8>>,
425 cur_param: Vec<u8>,
426 priv_prefix: Option<u8>,
427
428 stream_buf: Vec<u8>,
430 used_bel: bool,
431
432 stream_flush: usize,
434}
435
436impl VTPushParser {
437 pub fn new() -> Self {
438 Self {
439 st: State::Ground,
440 raw_buf: Vec::with_capacity(256),
441 ints: VTIntermediate::default(),
442 params: Vec::with_capacity(8),
443 cur_param: Vec::with_capacity(8),
444 priv_prefix: None,
445 stream_buf: Vec::with_capacity(8192),
446 used_bel: false,
447 stream_flush: 8192,
448 }
449 }
450
451 pub fn with_stream_flush_max(mut self, stream_flush: usize) -> Self {
452 self.stream_flush = stream_flush.max(1);
453 self
454 }
455
456 pub fn feed_with<F: FnMut(VTEvent)>(&mut self, input: &[u8], mut cb: F) {
461 for &b in input {
462 self.push_with(b, &mut cb);
463 }
464 self.flush_raw_if_any(&mut cb);
465 }
466
467 pub fn push_with<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
468 use State::*;
469 match self.st {
470 Ground => self.on_ground(b, cb),
471 Escape => self.on_escape(b, cb),
472 EscInt => self.on_esc_int(b, cb),
473
474 CsiEntry => self.on_csi_entry(b, cb),
475 CsiParam => self.on_csi_param(b, cb),
476 CsiInt => self.on_csi_int(b, cb),
477 CsiIgnore => self.on_csi_ignore(b, cb),
478
479 DcsEntry => self.on_dcs_entry(b, cb),
480 DcsParam => self.on_dcs_param(b, cb),
481 DcsInt => self.on_dcs_int(b, cb),
482 DcsIgnore => self.on_dcs_ignore(b, cb),
483 DcsIgnoreEsc => self.on_dcs_ignore_esc(b, cb),
484 DcsPassthrough => self.on_dcs_pass(b, cb),
485 DcsEsc => self.on_dcs_esc(b, cb),
486
487 OscString => self.on_osc_string(b, cb),
488 OscEsc => self.on_osc_esc(b, cb),
489
490 SosPmApcString => self.on_spa_string(b, cb),
491 SpaEsc => self.on_spa_esc(b, cb),
492 }
493 }
494
495 pub fn finish<F: FnMut(VTEvent)>(&mut self, cb: &mut F) {
496 self.reset_collectors();
498 self.st = State::Ground;
499 self.flush_raw_if_any(cb);
500 }
501
502 fn flush_raw_if_any<F: FnMut(VTEvent)>(&mut self, cb: &mut F) {
507 if !self.raw_buf.is_empty() {
508 let slice = &self.raw_buf[..];
509 cb(VTEvent::Raw(slice));
510 self.raw_buf.clear(); }
512 }
513
514 fn clear_hdr_collectors(&mut self) {
515 self.ints.clear();
516 self.params.clear();
517 self.cur_param.clear();
518 self.priv_prefix = None;
519 }
520
521 fn reset_collectors(&mut self) {
522 self.clear_hdr_collectors();
523 self.stream_buf.clear();
524 self.used_bel = false;
525 }
526
527 fn next_param(&mut self) {
528 self.params.push(std::mem::take(&mut self.cur_param));
529 }
530
531 fn finish_params_if_any(&mut self) {
532 if !self.cur_param.is_empty() || !self.params.is_empty() {
533 self.next_param();
534 }
535 }
536
537 fn emit_esc<F: FnMut(VTEvent)>(&mut self, final_byte: u8, cb: &mut F) {
538 self.flush_raw_if_any(cb);
539 cb(VTEvent::Esc {
540 intermediates: self.ints,
541 final_byte,
542 });
543 self.clear_hdr_collectors();
544 }
545
546 fn emit_csi<F: FnMut(VTEvent)>(&mut self, final_byte: u8, cb: &mut F) {
547 self.flush_raw_if_any(cb);
548 self.finish_params_if_any();
549
550 let mut borrowed: SmallVec<[&[u8]; 4]> = SmallVec::new();
552 borrowed.extend(self.params.iter().map(|v| v.as_slice()));
553
554 let privp = self.priv_prefix.take();
555 cb(VTEvent::Csi {
556 private: privp,
557 params: borrowed,
558 intermediates: self.ints,
559 final_byte,
560 });
561 self.clear_hdr_collectors();
562 }
563
564 fn dcs_start<F: FnMut(VTEvent)>(&mut self, final_byte: u8, cb: &mut F) {
565 self.flush_raw_if_any(cb);
566 self.finish_params_if_any();
567
568 let mut borrowed: SmallVec<[&[u8]; 4]> = SmallVec::new();
569 borrowed.extend(self.params.iter().map(|v| v.as_slice()));
570
571 let privp = self.priv_prefix.take();
572 cb(VTEvent::DcsStart {
573 priv_prefix: privp,
574 params: borrowed,
575 intermediates: self.ints,
576 final_byte,
577 });
578 self.stream_buf.clear();
579 self.clear_hdr_collectors();
581 }
582
583 fn dcs_put<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
584 self.stream_buf.push(b);
585 if self.stream_buf.len() >= self.stream_flush {
586 let slice = &self.stream_buf[..];
587 cb(VTEvent::DcsData(slice));
588 self.stream_buf.clear();
589 }
590 }
591
592 fn dcs_end<F: FnMut(VTEvent)>(&mut self, cb: &mut F) {
593 if !self.stream_buf.is_empty() {
594 let slice = &self.stream_buf[..];
595 cb(VTEvent::DcsData(slice));
596 self.stream_buf.clear();
597 }
598 cb(VTEvent::DcsEnd);
599 self.reset_collectors();
600 }
601
602 fn osc_start<F: FnMut(VTEvent)>(&mut self, cb: &mut F) {
603 self.flush_raw_if_any(cb);
604 self.used_bel = false;
605 self.stream_buf.clear();
606 cb(VTEvent::OscStart);
607 }
608
609 fn osc_put<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
610 self.stream_buf.push(b);
611 if self.stream_buf.len() >= self.stream_flush {
612 let slice = &self.stream_buf[..];
613 cb(VTEvent::OscData(slice));
614 self.stream_buf.clear();
615 }
616 }
617
618 fn osc_end<F: FnMut(VTEvent)>(&mut self, cb: &mut F) {
619 if !self.stream_buf.is_empty() {
620 let slice = &self.stream_buf[..];
621 cb(VTEvent::OscData(slice));
622 self.stream_buf.clear();
623 }
624 let used_bel = self.used_bel;
625 cb(VTEvent::OscEnd { used_bel });
626 self.reset_collectors();
627 }
628
629 fn on_ground<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
634 match b {
635 ESC => {
636 self.clear_hdr_collectors();
637 self.flush_raw_if_any(cb);
638 self.st = State::Escape;
639 }
640 DEL => {}
641 c if is_c0(c) => {
642 self.flush_raw_if_any(cb);
643 cb(VTEvent::C0(c));
644 }
645 p if is_printable(p) => {
646 self.raw_buf.push(p);
647 }
648 _ => {
649 self.raw_buf.push(b);
650 } }
652 }
653
654 fn on_escape<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
655 use State::*;
656 match b {
657 CAN | SUB => {
658 self.st = Ground;
659 }
660 DEL => {}
661 c if is_intermediate(c) => {
662 self.ints.push(c);
663 self.st = EscInt;
664 }
665 CSI => {
666 self.clear_hdr_collectors();
667 self.st = CsiEntry;
668 }
669 DCS => {
670 self.clear_hdr_collectors();
671 self.st = DcsEntry;
672 }
673 OSC => {
674 self.clear_hdr_collectors();
675 self.osc_start(cb);
676 self.st = OscString;
677 }
678 b'X' | b'^' | b'_' => {
679 self.clear_hdr_collectors();
680 self.st = State::SosPmApcString;
681 }
682 c if is_final(c) => {
683 self.emit_esc(c, cb);
684 self.st = Ground;
685 }
686 ESC => { }
687 _ => {
688 self.st = Ground;
689 }
690 }
691 }
692 fn on_esc_int<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
693 use State::*;
694 match b {
695 CAN | SUB => {
696 self.st = Ground;
697 }
698 DEL => {}
699 c if is_intermediate(c) => {
700 self.ints.push(c);
701 }
702 c if is_final(c) => {
703 self.emit_esc(c, cb);
704 self.st = Ground;
705 }
706 _ => {
707 self.st = Ground;
708 }
709 }
710 }
711
712 fn on_csi_entry<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
714 use State::*;
715 match b {
716 CAN | SUB => {
717 self.st = Ground;
718 }
719 DEL => {}
720 ESC => {
721 self.st = Escape;
722 }
723 c if is_priv(c) => {
724 self.priv_prefix = Some(c);
725 self.st = CsiParam;
726 }
727 d if is_digit(d) => {
728 self.cur_param.push(d);
729 self.st = CsiParam;
730 }
731 b';' => {
732 self.next_param();
733 self.st = CsiParam;
734 }
735 b':' => {
736 self.cur_param.push(b':');
737 self.st = CsiParam;
738 }
739 c if is_intermediate(c) => {
740 self.ints.push(c);
741 self.st = CsiInt;
742 }
743 c if is_final(c) => {
744 self.emit_csi(c, cb);
745 self.st = Ground;
746 }
747 _ => {
748 self.st = CsiIgnore;
749 }
750 }
751 }
752 fn on_csi_param<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
753 use State::*;
754 match b {
755 CAN | SUB => {
756 self.st = Ground;
757 }
758 DEL => {}
759 ESC => {
760 self.st = Escape;
761 }
762 d if is_digit(d) => {
763 self.cur_param.push(d);
764 }
765 b';' => {
766 self.next_param();
767 }
768 b':' => {
769 self.cur_param.push(b':');
770 }
771 c if is_intermediate(c) => {
772 self.ints.push(c);
773 self.st = CsiInt;
774 }
775 c if is_final(c) => {
776 self.emit_csi(c, cb);
777 self.st = Ground;
778 }
779 _ => {
780 self.st = CsiIgnore;
781 }
782 }
783 }
784 fn on_csi_int<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
785 use State::*;
786 match b {
787 CAN | SUB => {
788 self.st = Ground;
789 }
790 DEL => {}
791 ESC => {
792 self.st = Escape;
793 }
794 c if is_intermediate(c) => {
795 self.ints.push(c);
796 }
797 c if is_final(c) => {
798 self.emit_csi(c, cb);
799 self.st = Ground;
800 }
801 _ => {
802 self.st = CsiIgnore;
803 }
804 }
805 }
806 fn on_csi_ignore<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
807 use State::*;
808 match b {
809 CAN | SUB => {
810 self.st = Ground;
811 }
812 DEL => {}
813 ESC => {
814 self.st = Escape;
815 }
816 c if is_final(c) => {
817 self.st = Ground;
818 }
819 _ => {}
820 }
821 }
822
823 fn on_dcs_entry<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
825 use State::*;
826 match b {
827 CAN | SUB => {
828 self.st = Ground;
829 }
830 DEL => {}
831 ESC => {
832 self.st = Escape;
833 }
834 c if is_priv(c) => {
835 self.priv_prefix = Some(c);
836 self.st = DcsParam;
837 }
838 d if is_digit(d) => {
839 self.cur_param.push(d);
840 self.st = DcsParam;
841 }
842 b';' => {
843 self.next_param();
844 self.st = DcsParam;
845 }
846 b':' => {
847 self.st = DcsIgnore;
848 }
849 c if is_intermediate(c) => {
850 self.ints.push(c);
851 self.st = DcsInt;
852 }
853 c if is_final(c) => {
854 self.dcs_start(c, cb);
855 self.st = DcsPassthrough;
856 }
857 _ => {
858 self.st = DcsIgnore;
859 }
860 }
861 }
862 fn on_dcs_param<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
863 use State::*;
864 match b {
865 CAN | SUB => {
866 self.st = Ground;
867 }
868 DEL => {}
869 ESC => {
870 self.st = Escape;
871 }
872 d if is_digit(d) => {
873 self.cur_param.push(d);
874 }
875 b';' => {
876 self.next_param();
877 }
878 b':' => {
879 self.st = DcsIgnore;
880 }
881 c if is_intermediate(c) => {
882 self.ints.push(c);
883 self.st = DcsInt;
884 }
885 c if is_final(c) => {
886 self.dcs_start(c, cb);
887 self.st = DcsPassthrough;
888 }
889 _ => {
890 self.st = DcsIgnore;
891 }
892 }
893 }
894 fn on_dcs_int<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
895 use State::*;
896 match b {
897 CAN | SUB => {
898 self.st = Ground;
899 }
900 DEL => {}
901 ESC => {
902 self.st = Escape;
903 }
904 c if is_intermediate(c) => {
905 self.ints.push(c);
906 }
907 c if is_final(c) => {
908 self.dcs_start(c, cb);
909 self.st = DcsPassthrough;
910 }
911 _ => {
912 self.st = DcsIgnore;
913 }
914 }
915 }
916 fn on_dcs_ignore<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
917 use State::*;
918 match b {
919 CAN | SUB => {
920 self.st = Ground;
921 }
922 DEL => {}
923 ESC => {
924 self.st = DcsIgnoreEsc;
925 }
926 _ => {}
927 }
928 }
929 fn on_dcs_ignore_esc<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
930 use State::*;
931 match b {
932 CAN | SUB => {
933 self.st = Ground;
934 }
935 ST_FINAL => {
936 self.st = Ground;
937 }
938 DEL => {}
939 _ => {
940 self.st = DcsIgnore;
941 }
942 }
943 }
944 fn on_dcs_pass<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
945 use State::*;
946 match b {
947 CAN | SUB => {
948 cb(VTEvent::DcsCancel);
949 self.st = Ground;
950 }
951 DEL => {}
952 ESC => {
953 self.st = DcsEsc;
954 }
955 _ => {
956 self.dcs_put(b, cb);
957 }
958 }
959 }
960 fn on_dcs_esc<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
961 use State::*;
962 match b {
963 ST_FINAL => {
964 self.dcs_end(cb);
965 self.st = Ground;
966 } ESC => {
968 self.dcs_put(ESC, cb);
969 self.st = DcsPassthrough;
970 }
971 _ => {
972 self.dcs_put(ESC, cb);
973 self.dcs_put(b, cb);
974 self.st = DcsPassthrough;
975 }
976 }
977 }
978
979 fn on_osc_string<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
981 use State::*;
982 match b {
983 CAN | SUB => {
984 self.reset_collectors();
985 cb(VTEvent::OscCancel);
986 self.st = Ground;
987 }
988 DEL => {}
989 BEL => {
990 self.used_bel = true;
991 self.osc_end(cb);
992 self.st = Ground;
993 }
994 ESC => {
995 self.st = State::OscEsc;
996 }
997 p if is_printable(p) => {
998 self.osc_put(p, cb);
999 }
1000 _ => {} }
1002 }
1003 fn on_osc_esc<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
1004 use State::*;
1005 match b {
1006 ST_FINAL => {
1007 self.used_bel = false;
1008 self.osc_end(cb);
1009 self.st = Ground;
1010 } ESC => {
1012 self.osc_put(ESC, cb); }
1014 _ => {
1015 self.osc_put(ESC, cb);
1016 self.osc_put(b, cb);
1017 self.st = OscString;
1018 }
1019 }
1020 }
1021
1022 fn on_spa_string<F: FnMut(VTEvent)>(&mut self, b: u8, _cb: &mut F) {
1024 match b {
1025 CAN | SUB => {
1026 self.reset_collectors();
1027 self.st = State::Ground;
1028 }
1029 DEL => {}
1030 ESC => {
1031 self.st = State::SpaEsc;
1032 }
1033 _ => {}
1034 }
1035 }
1036 fn on_spa_esc<F: FnMut(VTEvent)>(&mut self, b: u8, _cb: &mut F) {
1037 match b {
1038 ST_FINAL => {
1039 self.reset_collectors();
1040 self.st = State::Ground;
1041 }
1042 ESC => { }
1043 _ => {
1044 self.st = State::SosPmApcString;
1045 }
1046 }
1047 }
1048}
1049
1050#[cfg(test)]
1051mod tests {
1052 use pretty_assertions::assert_eq;
1053
1054 use super::*;
1055
1056 fn decode_stream(input: &[u8]) -> String {
1057 println!("Input:");
1058 let mut s = Vec::new();
1059 _ = hxdmp::hexdump(input, &mut s);
1060 println!("{}", String::from_utf8_lossy(&s));
1061 let mut parser = VTPushParser::new();
1062 let mut result = String::new();
1063 let mut callback = |vt_input: VTEvent<'_>| {
1064 result.push_str(&format!("{:?}\n", vt_input));
1065 };
1066 parser.feed_with(input, &mut callback);
1067 println!("Result:");
1068 println!("{}", result);
1069 result
1070 }
1071
1072 #[test]
1073 fn test_large_escape2() {
1074 let result = decode_stream(
1075 &hex::decode(
1076 r#"
1077 1b5b495445524d3220332e352e31346e1b5d31303b7267623a646361612f6
1078 46361622f646361611b5c1b5d31313b7267623a313538652f313933612f31
1079 6537351b5c1b5b3f36343b313b323b343b363b31373b31383b32313b32326
1080 31b5b3e36343b323530303b30631b50217c36393534373236441b5c1b503e
1081 7c695465726d3220332e352e31341b5c1b5b383b33343b31343874"#
1082 .replace(char::is_whitespace, ""),
1083 )
1084 .unwrap(),
1085 );
1086 assert_eq!(
1087 result.trim(),
1088 r#"
1089Csi(, '', 'I')
1090Raw('TERM2 3.5.14n')
1091OscStart
1092OscData('10;rgb:dcaa/dcab/dcaa')
1093OscEnd
1094OscStart
1095OscData('11;rgb:158e/193a/1e75')
1096OscEnd
1097Csi('?', '64', '1', '2', '4', '6', '17', '18', '21', '22', '', 'c')
1098Csi('>', '64', '2500', '0', '', 'c')
1099DcsStart(, '!', |)
1100DcsData('6954726D')
1101DcsEnd
1102DcsStart('>', '', |)
1103DcsData('iTerm2 3.5.14')
1104DcsEnd
1105Csi(, '8', '34', '148', '', 't')
1106 "#
1107 .trim()
1108 );
1109 }
1110
1111 #[test]
1112 fn test_basic_escape_sequences() {
1113 let result = decode_stream(b"\x1b[1;2;3d");
1115 assert_eq!(result.trim(), "Csi(, '1', '2', '3', '', 'd')");
1116
1117 let result = decode_stream(b"\x1b M");
1119 assert_eq!(result.trim(), "Esc(' ', M)");
1120
1121 let result = decode_stream(b"\x1bOA");
1123 assert_eq!(result.trim(), "Esc('', O)\nRaw('A')");
1124 }
1125
1126 #[test]
1127 fn test_csi_sequences() {
1128 let result = decode_stream(b"\x1b[?25h");
1130 assert_eq!(result.trim(), "Csi('?', '25', '', 'h')");
1131
1132 let result = decode_stream(b"\x1b[1;2;3;4;5m");
1134 assert_eq!(result.trim(), "Csi(, '1', '2', '3', '4', '5', '', 'm')");
1135
1136 let result = decode_stream(b"\x1b[3:1;2;3;4;5m");
1138 assert_eq!(result.trim(), "Csi(, '3:1', '2', '3', '4', '5', '', 'm')");
1139
1140 let result = decode_stream(b"\x1b[ M");
1142 assert_eq!(result.trim(), "Csi(, ' ', 'M')");
1143 }
1144
1145 #[test]
1146 fn test_dcs_sequences() {
1147 let result = decode_stream(b"\x1bP 1;2;3|test data\x1b\\");
1149 assert_eq!(
1150 result.trim(),
1151 "DcsStart(, ' ', 1)\nDcsData(';2;3|test data')\nDcsEnd"
1152 );
1153
1154 let result = decode_stream(b"\x1bP>1;2;3|more data\x1b\\");
1156 assert_eq!(
1157 result.trim(),
1158 "DcsStart('>', '1', '2', '3', '', |)\nDcsData('more data')\nDcsEnd"
1159 );
1160
1161 let result = decode_stream(b"\x1bP 1;2;3 |data\x1b\\");
1163 assert_eq!(
1164 result.trim(),
1165 "DcsStart(, ' ', 1)\nDcsData(';2;3 |data')\nDcsEnd"
1166 );
1167
1168 let result = decode_stream(b"\x1bP1$r\x1b\\");
1169 assert_eq!(result.trim(), "DcsStart(, '1', '$', r)\nDcsEnd");
1170 }
1171
1172 #[test]
1173 fn test_osc_sequences() {
1174 let result = decode_stream(b"\x1b]10;rgb:fff/000/000\x07");
1176 assert_eq!(
1177 result.trim(),
1178 "OscStart\nOscData('10;rgb:fff/000/000')\nOscEnd"
1179 );
1180
1181 let result = decode_stream(b"\x1b]11;rgb:000/fff/000\x1b\\");
1183 assert_eq!(
1184 result.trim(),
1185 "OscStart\nOscData('11;rgb:000/fff/000')\nOscEnd"
1186 );
1187
1188 let result = decode_stream(b"\x1b]12;test [data\x1b\\");
1190 assert_eq!(result.trim(), "OscStart\nOscData('12;test [data')\nOscEnd");
1191 }
1192
1193 #[test]
1194 fn test_cancellation_and_invalid_sequences() {
1195 let result = decode_stream(b"x\x1b[1;2;3\x18y");
1197 assert_eq!(result.trim(), "Raw('x')\nRaw('y')");
1198
1199 let result = decode_stream(b"x\x1b[1;2;3\x1ay");
1201 assert_eq!(result.trim(), "Raw('x')\nRaw('y')");
1202
1203 let result = decode_stream(b"x\x1bP 1;2;3|data\x18y");
1205 assert_eq!(
1206 result.trim(),
1207 "Raw('x')\nDcsStart(, ' ', 1)\nDcsCancel\nRaw('y')"
1208 );
1209
1210 let result = decode_stream(b"x\x1b]10;data\x1ay");
1212 assert_eq!(result.trim(), "Raw('x')\nOscStart\nOscCancel\nRaw('y')");
1213
1214 let result = decode_stream(b"x\x1b[1;2;3gy");
1216 assert_eq!(
1217 result.trim(),
1218 "Raw('x')\nCsi(, '1', '2', '3', '', 'g')\nRaw('y')"
1219 );
1220
1221 let result = decode_stream(b"x\x1b[:1;2;3gy");
1223 assert_eq!(
1224 result.trim(),
1225 "Raw('x')\nCsi(, ':1', '2', '3', '', 'g')\nRaw('y')"
1226 );
1227 }
1228
1229 #[test]
1230 fn test_escape_sequences_with_raw_text() {
1231 let result = decode_stream(b"Hello\x1b[1;2;3dWorld");
1233 assert_eq!(
1234 result.trim(),
1235 "Raw('Hello')\nCsi(, '1', '2', '3', '', 'd')\nRaw('World')"
1236 );
1237
1238 let result = decode_stream(b"\x1b[1;2;3d\xe4\xb8\xad\xe6\x96\x87");
1240 assert_eq!(result.trim(), "Csi(, '1', '2', '3', '', 'd')\nRaw('中文')");
1241 }
1242
1243 #[test]
1244 fn test_c0_control_characters() {
1245 let result = decode_stream(b"\x0a\x0d\x09\x08\x0c\x0b");
1247 assert_eq!(
1248 result.trim(),
1249 "C0(0a)\nC0(0d)\nC0(09)\nC0(08)\nC0(0c)\nC0(0b)"
1250 );
1251
1252 let result = decode_stream(b"Hello\x0aWorld");
1254 assert_eq!(result.trim(), "Raw('Hello')\nC0(0a)\nRaw('World')");
1255 }
1256
1257 #[test]
1258 fn test_complex_escape_sequences() {
1259 let result = decode_stream(&hex::decode("1b5b3f32353b313b323b333a343b353b363b373b383b393b31303b31313b31323b31333b31343b31353b31363b31373b31383b31393b32303b32313b32323b32333b32343b32353b32363b32373b32383b32393b33303b33313b33323b33333b33343b33353b33363b33373b33383b33393b34303b34313b34323b34333b34343b34353b34363b34373b34383b34393b35303b35313b35323b35333b35343b35353b35363b35373b35383b35393b36303b36313b36323b36333b36343b36353b36363b36373b36383b36393b37303b37313b37323b37333b37343b37353b37363b37373b37383b37393b38303b38313b38323b38333b38343b38353b38363b38373b38383b38393b39303b39313b39323b39333b39343b39353b39363b39373b39383b39393b3130306d").unwrap());
1261 assert_eq!(
1262 result.trim(),
1263 "Csi('?', '25', '1', '2', '3:4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99', '100', '', 'm')"
1264 );
1265 }
1266
1267 #[test]
1268 fn test_escape_sequences_with_del() {
1269 let result = decode_stream(b"\x1b[1;2;3\x7fm");
1271 assert_eq!(result.trim(), "Csi(, '1', '2', '3', '', 'm')");
1272
1273 let result = decode_stream(b"Hello\x7fWorld");
1275 assert_eq!(result.trim(), "Raw('HelloWorld')");
1276 }
1277
1278 #[test]
1279 fn test_escape_sequences_with_esc_esc() {
1280 let result = decode_stream(b"\x1b\x1b[1;2;3d");
1282 assert_eq!(result.trim(), "Csi(, '1', '2', '3', '', 'd')");
1283
1284 let result = decode_stream(b"\x1bP 1;2;3|\x1b\x1bdata\x1b\\");
1285 assert_eq!(
1287 result.trim().replace("\x1b", "<ESC>"),
1288 "DcsStart(, ' ', 1)\nDcsData(';2;3|<ESC>data')\nDcsEnd"
1289 );
1290 }
1291
1292 #[test]
1293 fn test_sos_pm_apc_sequences() {
1294 let result = decode_stream(b"\x1bXtest data\x1b\\");
1296 assert_eq!(result.trim(), "");
1297
1298 let result = decode_stream(b"\x1b^test data\x1b\\");
1300 assert_eq!(result.trim(), "");
1301
1302 let result = decode_stream(b"\x1b_test data\x1b\\");
1304 assert_eq!(result.trim(), "");
1305 }
1306
1307 #[test]
1308 fn test_edge_cases() {
1309 let result = decode_stream(&[]);
1311 assert_eq!(result.trim(), "");
1312
1313 let result = decode_stream(b"\x1b");
1315 assert_eq!(result.trim(), "");
1316
1317 let result = decode_stream(b"\x1b[");
1319 assert_eq!(result.trim(), "");
1320
1321 let result = decode_stream(b"\x1bP");
1323 assert_eq!(result.trim(), "");
1324
1325 let result = decode_stream(b"\x1b]");
1327 assert_eq!(result.trim(), "OscStart");
1328 }
1329
1330 #[test]
1331 fn test_unicode_and_special_characters() {
1332 let result = decode_stream(b"\x1b[1;2;3d\xe4\xb8\xad\xe6\x96\x87");
1334 assert_eq!(result.trim(), "Csi(, '1', '2', '3', '', 'd')\nRaw('中文')");
1335
1336 let result = decode_stream(b"\x1b[1;2;3d~!@#$%^&*()_+|\\{}[]:;\"|<>,.?/");
1338 assert_eq!(
1339 result.trim(),
1340 "Csi(, '1', '2', '3', '', 'd')\nRaw('~!@#$%^&*()_+|\\{}[]:;\"|<>,.?/')"
1341 );
1342 }
1343
1344 #[test]
1345 fn test_streaming_behavior() {
1346 let mut parser = VTPushParser::new().with_stream_flush_max(4); let mut result = String::new();
1349 let mut callback = |vt_input: VTEvent<'_>| {
1350 result.push_str(&format!("{:?}\n", vt_input));
1351 };
1352
1353 parser.feed_with(b"\x1bP1;2;3 |", &mut callback);
1355 parser.feed_with(b"data", &mut callback);
1356 parser.feed_with(b" more", &mut callback);
1357 parser.feed_with(b"\x1b\\", &mut callback);
1358
1359 assert_eq!(
1360 result.trim(),
1361 "DcsStart(, '1', '2', '3', ' ', |)\nDcsData('data')\nDcsData(' mor')\nDcsData('e')\nDcsEnd"
1362 );
1363 }
1364
1365 #[test]
1366 fn test_finish_method() {
1367 let mut parser = VTPushParser::new();
1368 let mut result = String::new();
1369 let mut callback = |vt_input: VTEvent<'_>| {
1370 result.push_str(&format!("{:?}\n", vt_input));
1371 };
1372
1373 parser.feed_with(b"\x1b[1;2;3", &mut callback);
1375
1376 parser.finish(&mut callback);
1378
1379 assert_eq!(result.trim(), "");
1380 }
1381
1382 #[test]
1383 fn test_csi_colons() {
1384 let test_cases: Vec<(&'static [u8], &[&str], u8)> = vec![
1385 (b"\x1b[38:2:255:128:64m", &["38:2:255:128:64"], b'm'),
1387 (b"\x1b[48:2:0:0:0m", &["48:2:0:0:0"], b'm'),
1389 (b"\x1b[38:5:208m", &["38:5:208"], b'm'),
1391 (b"\x1b[48:5:123m", &["48:5:123"], b'm'),
1393 (
1395 b"\x1b[1;38:5:208;48:2:30:30:30m",
1396 &["1", "38:5:208", "48:2:30:30:30"],
1397 b'm',
1398 ),
1399 (b"\x1b[0;38:2:12:34:56m", &["0", "38:2:12:34:56"], b'm'),
1401 (b"\x1b[58:2::186:93:0m", &["58:2::186:93:0"], b'm'),
1403 (
1405 b"\x1b[38:2:10:20:30;48:5:17;58:2::200:100:0m",
1406 &["38:2:10:20:30", "48:5:17", "58:2::200:100:0"],
1407 b'm',
1408 ),
1409 (b"\x1b[38:2:000:007:042m", &["38:2:000:007:042"], b'm'),
1411 (b"\x1b[38:2:300:300:300m", &["38:2:300:300:300"], b'm'),
1413 (b"\x1b[38:5:15;m", &["38:5:15", ""], b'm'),
1415 (b"\x1b[38:2:1:2:3m", &["38:2:1:2:3"], b'm'),
1417 ];
1418
1419 for (input, expected_params, expected_final) in test_cases {
1420 let mut parser = VTPushParser::new();
1421 parser.feed_with(input, |event| match event {
1422 VTEvent::Csi {
1423 private,
1424 params,
1425 intermediates,
1426 final_byte,
1427 } => {
1428 assert_eq!(
1429 private, None,
1430 "Expected no private prefix for input {:?}",
1431 input
1432 );
1433 assert_eq!(
1434 intermediates.is_empty(),
1435 true,
1436 "Expected no intermediates for input {:?}",
1437 input
1438 );
1439 assert_eq!(
1440 final_byte, expected_final,
1441 "Expected final byte '{}' for input {:?}",
1442 expected_final, input
1443 );
1444
1445 let param_strings: Vec<String> = params
1446 .iter()
1447 .map(|p| String::from_utf8_lossy(p).to_string())
1448 .collect();
1449 assert_eq!(
1450 param_strings, expected_params,
1451 "Parameter mismatch for input {:?}",
1452 input
1453 );
1454 }
1455 _ => panic!("Expected CSI event for input {:?}, got {:?}", input, event),
1456 });
1457 }
1458 }
1459
1460 #[test]
1461 fn test_dcs_ignore_st_handling() {
1462 let result = decode_stream(b"Hello\x1bP:1;2;3|data\x1b\\World");
1469
1470 assert_eq!(result.trim(), "Raw('Hello')\nRaw('World')");
1474
1475 let result = decode_stream(b"\x1bP:1;2;3|data\x1b\\Hello");
1477
1478 assert_eq!(result.trim(), "Raw('Hello')");
1481
1482 let result = decode_stream(b"\x1bP1;2;3|data\x1b\\Hello");
1484 assert_eq!(
1485 result.trim(),
1486 "DcsStart(, '1', '2', '3', '', |)\nDcsData('data')\nDcsEnd\nRaw('Hello')"
1487 );
1488 }
1489
1490 #[test]
1491 fn test_dcs_ignore_cancellation() {
1492 let result = decode_stream(b"\x1bP:1;2;3|data\x18Hello"); assert_eq!(result.trim(), "Raw('Hello')");
1495
1496 let result = decode_stream(b"\x1bP:1;2;3|data\x1aHello"); assert_eq!(result.trim(), "Raw('Hello')");
1498 }
1499
1500 #[test]
1501 fn test_dcs_payload_passthrough() {
1502 let dcs_cases: &[(&[u8], &str)] = &[
1510 (b"\x1bPq\x1b[38:2:12:34:56m\x1b\\", "<ESC>[38:2:12:34:56m"),
1512 (b"\x1bPq\x1b[48:2:0:0:0m;xyz\x1b\\", "<ESC>[48:2:0:0:0m;xyz"),
1514 (
1516 b"\x1bP1$r\x1b[38:2:10:20:30;58:2::200:100:0m\x1b\\",
1517 "<ESC>[38:2:10:20:30;58:2::200:100:0m",
1518 ),
1519 (b"\x1bPqABC\x1b\x1bDEF\x1bXG\x1b\\", "ABC<ESC>DEF<ESC>XG"),
1521 (b"\x1bPqDATA\x07MORE\x1b\\", "DATA<BEL>MORE"),
1523 (b"\x1bP!|\x1b[38:5:208m\x1b\\", "<ESC>[38:5:208m"),
1525 (b"\x1bP>|Hello world\x1b\\", "Hello world"),
1527 (
1529 b"\x1bPq\x1b[38:2:1:2:3m\x1b[48:5:17m\x1b\\",
1530 "<ESC>[38:2:1:2:3m<ESC>[48:5:17m",
1531 ),
1532 (
1534 b"\x1bPq\x1b[58:2::000:007:042m\x1b\\",
1535 "<ESC>[58:2::000:007:042m",
1536 ),
1537 (
1540 b"\x1bPqKEEP \x1b\x1b\\ LITERAL ST\x1b\\",
1541 "KEEP <ESC>\\ LITERAL ST",
1542 ),
1543 ];
1544
1545 for (input, expected_body) in dcs_cases {
1546 let events = collect_events(input);
1547
1548 let mut actual_body = String::new();
1550 for event in &events {
1551 if let Some(data_part) = event
1552 .strip_prefix("DcsData('")
1553 .and_then(|s| s.strip_suffix("')"))
1554 {
1555 actual_body
1556 .push_str(&data_part.replace("\x1b", "<ESC>").replace("\x07", "<BEL>"));
1557 }
1558 }
1559
1560 assert_eq!(
1561 actual_body, *expected_body,
1562 "DCS payload mismatch for input {:?}. Full events: {:#?}",
1563 input, events
1564 );
1565
1566 assert!(
1568 events.iter().any(|e| e.starts_with("DcsStart")),
1569 "Missing DcsStart for input {:?}. Events: {:#?}",
1570 input,
1571 events
1572 );
1573 assert!(
1574 events.iter().any(|e| e == "DcsEnd"),
1575 "Missing DcsEnd for input {:?}. Events: {:#?}",
1576 input,
1577 events
1578 );
1579 }
1580 }
1581
1582 fn collect_events(input: &[u8]) -> Vec<String> {
1583 let mut out = Vec::new();
1584 let mut p = VTPushParser::new();
1585 p.feed_with(input, |ev| out.push(format!("{:?}", ev)));
1586 out
1587 }
1588
1589 #[test]
1590 fn dcs_header_with_colon_is_ignored_case1() {
1591 let ev = collect_events(b"\x1bP1:2qHELLO\x1b\\");
1593 assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1595 }
1596
1597 #[test]
1598 fn dcs_header_with_colon_is_ignored_case2() {
1599 let ev = collect_events(b"\x1bP:1qDATA\x1b\\");
1601 assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1602 }
1603
1604 #[test]
1605 fn dcs_header_with_colon_is_ignored_case3() {
1606 let ev = collect_events(b"\x1bP12:34!qPAYLOAD\x1b\\");
1608 assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1609 }
1610
1611 #[test]
1612 fn osc_aborted_by_can_mid_body() {
1613 let mut s = Vec::new();
1615 s.extend_from_slice(b"\x1b]0;Title");
1616 s.push(CAN);
1617 s.extend_from_slice(b"more\x07");
1618
1619 let ev = collect_debug(&s);
1620
1621 assert!(ev.iter().any(|e| e.starts_with("OscStart")), "{ev:#?}");
1626 assert!(!ev.iter().any(|e| e.starts_with("OscData")), "{ev:#?}");
1627 assert!(!ev.iter().any(|e| e.starts_with("OscEnd")), "{ev:#?}");
1628 }
1629
1630 #[test]
1631 fn osc_aborted_by_sub_before_terminator() {
1632 let mut s = Vec::new();
1633 s.extend_from_slice(b"\x1b]52;c;YWJjZA==");
1634 s.push(SUB); s.extend_from_slice(b"\x1b\\"); let ev = collect_debug(&s);
1638 assert!(ev.iter().any(|e| e.starts_with("OscStart")), "{ev:#?}");
1642 assert!(!ev.iter().any(|e| e.starts_with("OscData")), "{ev:#?}");
1643 assert!(!ev.iter().any(|e| e.starts_with("OscEnd")), "{ev:#?}");
1644 }
1645
1646 fn collect_debug(input: &[u8]) -> Vec<String> {
1648 let mut out = Vec::new();
1649 let mut p = VTPushParser::new();
1650 p.feed_with(input, |ev| out.push(format!("{:?}", ev)));
1651 out
1652 }
1653
1654 const CAN: u8 = 0x18;
1655 const SUB: u8 = 0x1A;
1656
1657 #[test]
1658 fn dcs_aborted_by_can_before_body() {
1659 let mut s = Vec::new();
1661 s.extend_from_slice(b"\x1bPq"); s.push(CAN);
1663 s.extend_from_slice(b"IGNORED\x1b\\"); let ev = collect_debug(&s);
1666
1667 assert_eq!(ev.len(), 4, "{ev:#?}");
1668 assert_eq!(ev[0], "DcsStart(, '', q)");
1669 assert_eq!(ev[1], "DcsCancel");
1670 assert_eq!(ev[2], "Raw('IGNORED')");
1671 assert_eq!(ev[3], "Esc('', \\)");
1672 }
1673
1674 #[test]
1675 fn dcs_aborted_by_can_mid_body() {
1676 let mut s = Vec::new();
1678 s.extend_from_slice(b"\x1bPqABC");
1679 s.push(CAN);
1680 s.extend_from_slice(b"MORE\x1b\\"); let ev = collect_debug(&s);
1683
1684 assert_eq!(ev.len(), 4, "{ev:#?}");
1685 assert_eq!(ev[0], "DcsStart(, '', q)");
1686 assert_eq!(ev[1], "DcsCancel");
1687 assert_eq!(ev[2], "Raw('MORE')");
1688 assert_eq!(ev[3], "Esc('', \\)");
1689 }
1690
1691 #[test]
1694 fn spa_aborted_by_can_is_ignored() {
1695 let mut s = Vec::new();
1697 s.extend_from_slice(b"\x1b_hello");
1698 s.push(CAN);
1699 s.extend_from_slice(b"world\x1b\\");
1700
1701 let ev = collect_debug(&s);
1702 assert_eq!(ev.len(), 2, "{ev:#?}");
1703 assert_eq!(ev[0], "Raw('world')");
1704 assert_eq!(ev[1], "Esc('', \\)");
1705 }
1706
1707 #[test]
1708 fn spa_sub_aborts_too() {
1709 let mut s = Vec::new();
1710 s.extend_from_slice(b"\x1bXhello");
1711 s.push(SUB);
1712 s.extend_from_slice(b"world\x1b\\");
1713 let ev = collect_debug(&s);
1714 assert_eq!(ev.len(), 2, "{ev:#?}");
1715 assert_eq!(ev[0], "Raw('world')");
1716 assert_eq!(ev[1], "Esc('', \\)");
1717 }
1718
1719 #[test]
1722 fn can_in_ground_is_c0() {
1723 let mut s = Vec::new();
1724 s.extend_from_slice(b"abc");
1725 s.push(CAN);
1726 s.extend_from_slice(b"def");
1727 let ev = collect_debug(&s);
1728 assert_eq!(ev.len(), 3, "{ev:#?}");
1730 assert_eq!(ev[0], "Raw('abc')");
1731 assert_eq!(ev[1], "C0(18)");
1732 assert_eq!(ev[2], "Raw('def')");
1733 }
1734}