vt_push_parser/
lib.rs

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        // Inefficient
79        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
91/// A signature for an escape sequence.
92pub 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        // TODO: const
153        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        // TODO: const
204        self.param_count.contains(&(len as u8))
205    }
206}
207
208pub enum VTEvent<'a> {
209    // Plain printable text from GROUND (coalesced)
210    Raw(&'a [u8]),
211
212    // C0 control (EXECUTE)
213    C0(u8),
214
215    // ESC final (with intermediates)
216    Esc {
217        intermediates: VTIntermediate,
218        final_byte: u8,
219    },
220
221    // CSI short escape
222    Csi {
223        private: Option<u8>,
224        params: smallvec::SmallVec<[&'a [u8]; 4]>,
225        intermediates: VTIntermediate,
226        final_byte: u8,
227    },
228
229    // SS3 (ESC O …)
230    Ss3 {
231        intermediates: VTIntermediate,
232        final_byte: u8,
233    },
234
235    // DCS stream
236    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
245    // OSC stream
246    OscStart,
247    OscData(&'a [u8]),
248    OscEnd {
249        used_bel: bool,
250    },
251}
252
253impl<'a> std::fmt::Debug for VTEvent<'a> {
254    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
255        match self {
256            VTEvent::Raw(s) => {
257                write!(f, "Raw('")?;
258                for chunk in s.utf8_chunks() {
259                    write!(f, "{}", chunk.valid())?;
260                    if !chunk.invalid().is_empty() {
261                        write!(f, "<{}>", hex::encode(chunk.invalid()))?;
262                    }
263                }
264                write!(f, "')")?;
265                Ok(())
266            }
267            VTEvent::C0(b) => write!(f, "C0({:02x})", b),
268            VTEvent::Esc {
269                intermediates,
270                final_byte,
271            } => {
272                write!(f, "Esc({:?}", intermediates)?;
273                write!(f, ", {})", *final_byte as char)?;
274                Ok(())
275            }
276            VTEvent::Csi {
277                private,
278                params,
279                intermediates,
280                final_byte,
281            } => {
282                write!(f, "Csi(")?;
283                if let Some(p) = private {
284                    write!(f, "{:?}", *p as char)?;
285                }
286                for param in params {
287                    write!(f, ", '")?;
288                    for chunk in param.utf8_chunks() {
289                        write!(f, "{}", chunk.valid())?;
290                        if !chunk.invalid().is_empty() {
291                            write!(f, "<{}>", hex::encode(chunk.invalid()))?;
292                        }
293                    }
294                    write!(f, "'")?;
295                }
296                write!(f, ", {:?}", intermediates)?;
297                write!(f, ", {:?})", *final_byte as char)?;
298                Ok(())
299            }
300            VTEvent::Ss3 {
301                intermediates,
302                final_byte,
303            } => {
304                write!(f, "Ss3(")?;
305                write!(f, "{:?}", intermediates)?;
306                write!(f, ", {})", *final_byte as char)?;
307                Ok(())
308            }
309            VTEvent::DcsStart {
310                priv_prefix,
311                params,
312                intermediates,
313                final_byte,
314            } => {
315                write!(f, "DcsStart(")?;
316                if let Some(p) = priv_prefix {
317                    write!(f, "{:?}", *p as char)?;
318                }
319                for param in params {
320                    write!(f, ", {:02x?}", param)?;
321                }
322                write!(f, ", {:?}", intermediates)?;
323                write!(f, ", {})", *final_byte as char)?;
324                Ok(())
325            }
326            VTEvent::DcsData(s) => {
327                write!(f, "DcsData('")?;
328                for chunk in s.utf8_chunks() {
329                    write!(f, "{}", chunk.valid())?;
330                    if !chunk.invalid().is_empty() {
331                        write!(f, "<{}>", hex::encode(chunk.invalid()))?;
332                    }
333                }
334                write!(f, "')")?;
335                Ok(())
336            }
337            VTEvent::DcsEnd => write!(f, "DcsEnd"),
338            VTEvent::OscStart => write!(f, "OscStart"),
339            VTEvent::OscData(s) => {
340                write!(f, "OscData('")?;
341                for chunk in s.utf8_chunks() {
342                    write!(f, "{}", chunk.valid())?;
343                    if !chunk.invalid().is_empty() {
344                        write!(f, "<{}>", hex::encode(chunk.invalid()))?;
345                    }
346                }
347                write!(f, "')")?;
348                Ok(())
349            }
350            VTEvent::OscEnd { .. } => {
351                write!(f, "OscEnd")?;
352                Ok(())
353            }
354        }
355    }
356}
357
358#[inline]
359fn is_c0(b: u8) -> bool {
360    b <= 0x1F
361}
362#[inline]
363fn is_printable(b: u8) -> bool {
364    (0x20..=0x7E).contains(&b)
365}
366#[inline]
367fn is_intermediate(b: u8) -> bool {
368    (0x20..=0x2F).contains(&b)
369}
370#[inline]
371fn is_final(b: u8) -> bool {
372    (0x30..=0x7E).contains(&b)
373}
374#[inline]
375fn is_digit(b: u8) -> bool {
376    (b'0'..=b'9').contains(&b)
377}
378#[inline]
379fn is_priv(b: u8) -> bool {
380    matches!(b, b'<' | b'=' | b'>' | b'?')
381}
382
383#[derive(Debug, Copy, Clone, PartialEq, Eq)]
384enum State {
385    Ground,
386    Escape,
387    EscInt,
388    CsiEntry,
389    CsiParam,
390    CsiInt,
391    CsiIgnore,
392    DcsEntry,
393    DcsParam,
394    DcsInt,
395    DcsIgnore,
396    DcsPassthrough,
397    DcsEsc,
398    OscString,
399    OscEsc,
400    SosPmApcString,
401    SpaEsc,
402}
403
404pub struct VTPushParser {
405    st: State,
406
407    // GROUND raw coalescing
408    raw_buf: Vec<u8>,
409
410    // Header collectors for short escapes (we borrow from these in callbacks)
411    ints: VTIntermediate,
412    params: Vec<Vec<u8>>,
413    cur_param: Vec<u8>,
414    priv_prefix: Option<u8>,
415
416    // Streaming buffer (DCS/OSC bodies)
417    stream_buf: Vec<u8>,
418    used_bel: bool,
419
420    // Limits
421    max_short_hdr: usize,
422    stream_flush: usize,
423}
424
425impl VTPushParser {
426    pub fn new() -> Self {
427        Self {
428            st: State::Ground,
429            raw_buf: Vec::with_capacity(256),
430            ints: VTIntermediate::default(),
431            params: Vec::with_capacity(8),
432            cur_param: Vec::with_capacity(8),
433            priv_prefix: None,
434            stream_buf: Vec::with_capacity(8192),
435            used_bel: false,
436            max_short_hdr: 4096,
437            stream_flush: 8192,
438        }
439    }
440
441    pub fn with_limits(mut self, max_short_hdr: usize, stream_flush: usize) -> Self {
442        self.max_short_hdr = max_short_hdr;
443        self.stream_flush = stream_flush.max(1);
444        self
445    }
446
447    /* =====================
448    Callback-driven API
449    ===================== */
450
451    pub fn feed_with<F: FnMut(VTEvent)>(&mut self, input: &[u8], mut cb: F) {
452        for &b in input {
453            self.push_with(b, &mut cb);
454        }
455        // Emitting pending raw at end-of-chunk helps coalesce; you can remove if undesired.
456        self.flush_raw_if_any(&mut cb);
457    }
458
459    pub fn push_with<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
460        use State::*;
461        match self.st {
462            Ground => self.on_ground(b, cb),
463            Escape => self.on_escape(b, cb),
464            EscInt => self.on_esc_int(b, cb),
465
466            CsiEntry => self.on_csi_entry(b, cb),
467            CsiParam => self.on_csi_param(b, cb),
468            CsiInt => self.on_csi_int(b, cb),
469            CsiIgnore => self.on_csi_ignore(b, cb),
470
471            DcsEntry => self.on_dcs_entry(b, cb),
472            DcsParam => self.on_dcs_param(b, cb),
473            DcsInt => self.on_dcs_int(b, cb),
474            DcsIgnore => self.on_dcs_ignore(b, cb),
475            DcsPassthrough => self.on_dcs_pass(b, cb),
476            DcsEsc => self.on_dcs_esc(b, cb),
477
478            OscString => self.on_osc_string(b, cb),
479            OscEsc => self.on_osc_esc(b, cb),
480
481            SosPmApcString => self.on_spa_string(b, cb),
482            SpaEsc => self.on_spa_esc(b, cb),
483        }
484    }
485
486    pub fn finish<F: FnMut(VTEvent)>(&mut self, cb: &mut F) {
487        // Abort unterminated strings and flush raw.
488        self.reset_collectors();
489        self.st = State::Ground;
490        self.flush_raw_if_any(cb);
491    }
492
493    /* =====================
494    Emit helpers (borrowed)
495    ===================== */
496
497    fn flush_raw_if_any<F: FnMut(VTEvent)>(&mut self, cb: &mut F) {
498        if !self.raw_buf.is_empty() {
499            let slice = &self.raw_buf[..];
500            cb(VTEvent::Raw(slice));
501            self.raw_buf.clear(); // borrow ended with callback return
502        }
503    }
504
505    fn clear_hdr_collectors(&mut self) {
506        self.ints.clear();
507        self.params.clear();
508        self.cur_param.clear();
509        self.priv_prefix = None;
510    }
511
512    fn reset_collectors(&mut self) {
513        self.clear_hdr_collectors();
514        self.stream_buf.clear();
515        self.used_bel = false;
516    }
517
518    fn next_param(&mut self) {
519        self.params.push(std::mem::take(&mut self.cur_param));
520    }
521
522    fn finish_params_if_any(&mut self) {
523        if !self.cur_param.is_empty() || !self.params.is_empty() {
524            self.next_param();
525        }
526    }
527
528    fn emit_esc<F: FnMut(VTEvent)>(&mut self, final_byte: u8, cb: &mut F) {
529        self.flush_raw_if_any(cb);
530        cb(VTEvent::Esc {
531            intermediates: self.ints,
532            final_byte,
533        });
534        self.clear_hdr_collectors();
535    }
536
537    fn emit_csi<F: FnMut(VTEvent)>(&mut self, final_byte: u8, cb: &mut F) {
538        self.flush_raw_if_any(cb);
539        self.finish_params_if_any();
540
541        // Build borrowed views into self.params
542        let mut borrowed: SmallVec<[&[u8]; 4]> = SmallVec::new();
543        borrowed.extend(self.params.iter().map(|v| v.as_slice()));
544
545        let privp = self.priv_prefix.take();
546        cb(VTEvent::Csi {
547            private: privp,
548            params: borrowed,
549            intermediates: self.ints,
550            final_byte,
551        });
552        self.clear_hdr_collectors();
553    }
554
555    fn dcs_start<F: FnMut(VTEvent)>(&mut self, final_byte: u8, cb: &mut F) {
556        self.flush_raw_if_any(cb);
557        self.finish_params_if_any();
558
559        let mut borrowed: SmallVec<[&[u8]; 4]> = SmallVec::new();
560        borrowed.extend(self.params.iter().map(|v| v.as_slice()));
561
562        let privp = self.priv_prefix.take();
563        cb(VTEvent::DcsStart {
564            priv_prefix: privp,
565            params: borrowed,
566            intermediates: self.ints,
567            final_byte,
568        });
569        self.stream_buf.clear();
570        // keep header buffers intact until after callback; already done
571        self.clear_hdr_collectors();
572    }
573
574    fn dcs_put<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
575        self.stream_buf.push(b);
576        if self.stream_buf.len() >= self.stream_flush {
577            let slice = &self.stream_buf[..];
578            cb(VTEvent::DcsData(slice));
579            self.stream_buf.clear();
580        }
581    }
582
583    fn dcs_end<F: FnMut(VTEvent)>(&mut self, cb: &mut F) {
584        if !self.stream_buf.is_empty() {
585            let slice = &self.stream_buf[..];
586            cb(VTEvent::DcsData(slice));
587            self.stream_buf.clear();
588        }
589        cb(VTEvent::DcsEnd);
590        self.reset_collectors();
591    }
592
593    fn osc_start<F: FnMut(VTEvent)>(&mut self, cb: &mut F) {
594        self.flush_raw_if_any(cb);
595        self.used_bel = false;
596        self.stream_buf.clear();
597        cb(VTEvent::OscStart);
598    }
599
600    fn osc_put<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
601        self.stream_buf.push(b);
602        if self.stream_buf.len() >= self.stream_flush {
603            let slice = &self.stream_buf[..];
604            cb(VTEvent::OscData(slice));
605            self.stream_buf.clear();
606        }
607    }
608
609    fn osc_end<F: FnMut(VTEvent)>(&mut self, cb: &mut F) {
610        if !self.stream_buf.is_empty() {
611            let slice = &self.stream_buf[..];
612            cb(VTEvent::OscData(slice));
613            self.stream_buf.clear();
614        }
615        let used_bel = self.used_bel;
616        cb(VTEvent::OscEnd { used_bel });
617        self.reset_collectors();
618    }
619
620    /* =====================
621    State handlers
622    ===================== */
623
624    fn on_ground<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
625        match b {
626            ESC => {
627                self.clear_hdr_collectors();
628                self.flush_raw_if_any(cb);
629                self.st = State::Escape;
630            }
631            DEL => {}
632            c if is_c0(c) => {
633                self.flush_raw_if_any(cb);
634                cb(VTEvent::C0(c));
635            }
636            p if is_printable(p) => {
637                self.raw_buf.push(p);
638            }
639            _ => {
640                self.raw_buf.push(b);
641            } // safe fallback
642        }
643    }
644
645    fn on_escape<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
646        use State::*;
647        match b {
648            CAN | SUB => {
649                self.st = Ground;
650            }
651            DEL => {}
652            c if is_intermediate(c) => {
653                self.ints.push(c);
654                self.st = EscInt;
655            }
656            CSI => {
657                self.clear_hdr_collectors();
658                self.st = CsiEntry;
659            }
660            DCS => {
661                self.clear_hdr_collectors();
662                self.st = DcsEntry;
663            }
664            OSC => {
665                self.clear_hdr_collectors();
666                self.osc_start(cb);
667                self.st = OscString;
668            }
669            b'X' | b'^' | b'_' => {
670                self.clear_hdr_collectors();
671                self.st = State::SosPmApcString;
672            }
673            c if is_final(c) => {
674                self.emit_esc(c, cb);
675                self.st = Ground;
676            }
677            ESC => { /* ESC ESC allowed */ }
678            _ => {
679                self.st = Ground;
680            }
681        }
682    }
683    fn on_esc_int<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
684        use State::*;
685        match b {
686            CAN | SUB => {
687                self.st = Ground;
688            }
689            DEL => {}
690            c if is_intermediate(c) => {
691                self.ints.push(c);
692            }
693            c if is_final(c) => {
694                self.emit_esc(c, cb);
695                self.st = Ground;
696            }
697            _ => {
698                self.st = Ground;
699            }
700        }
701    }
702
703    // ---- CSI
704    fn on_csi_entry<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
705        use State::*;
706        match b {
707            CAN | SUB => {
708                self.st = Ground;
709            }
710            DEL => {}
711            ESC => {
712                self.st = Escape;
713            }
714            c if is_priv(c) => {
715                self.priv_prefix = Some(c);
716                self.st = CsiParam;
717            }
718            d if is_digit(d) => {
719                self.cur_param.push(d);
720                self.st = CsiParam;
721            }
722            b';' => {
723                self.next_param();
724                self.st = CsiParam;
725            }
726            b':' => {
727                self.cur_param.push(b':');
728                self.st = CsiParam;
729            }
730            c if is_intermediate(c) => {
731                self.ints.push(c);
732                self.st = CsiInt;
733            }
734            c if is_final(c) => {
735                self.emit_csi(c, cb);
736                self.st = Ground;
737            }
738            _ => {
739                self.st = CsiIgnore;
740            }
741        }
742    }
743    fn on_csi_param<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
744        use State::*;
745        match b {
746            CAN | SUB => {
747                self.st = Ground;
748            }
749            DEL => {}
750            ESC => {
751                self.st = Escape;
752            }
753            d if is_digit(d) => {
754                self.cur_param.push(d);
755            }
756            b';' => {
757                self.next_param();
758            }
759            b':' => {
760                self.cur_param.push(b':');
761            }
762            c if is_intermediate(c) => {
763                self.ints.push(c);
764                self.st = CsiInt;
765            }
766            c if is_final(c) => {
767                self.emit_csi(c, cb);
768                self.st = Ground;
769            }
770            _ => {
771                self.st = CsiIgnore;
772            }
773        }
774    }
775    fn on_csi_int<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
776        use State::*;
777        match b {
778            CAN | SUB => {
779                self.st = Ground;
780            }
781            DEL => {}
782            ESC => {
783                self.st = Escape;
784            }
785            c if is_intermediate(c) => {
786                self.ints.push(c);
787            }
788            c if is_final(c) => {
789                self.emit_csi(c, cb);
790                self.st = Ground;
791            }
792            _ => {
793                self.st = CsiIgnore;
794            }
795        }
796    }
797    fn on_csi_ignore<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
798        use State::*;
799        match b {
800            CAN | SUB => {
801                self.st = Ground;
802            }
803            DEL => {}
804            ESC => {
805                self.st = Escape;
806            }
807            c if is_final(c) => {
808                self.st = Ground;
809            }
810            _ => {}
811        }
812    }
813
814    // ---- DCS
815    fn on_dcs_entry<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
816        use State::*;
817        match b {
818            CAN | SUB => {
819                self.st = Ground;
820            }
821            DEL => {}
822            ESC => {
823                self.st = Escape;
824            }
825            c if is_priv(c) => {
826                self.priv_prefix = Some(c);
827                self.st = DcsParam;
828            }
829            d if is_digit(d) => {
830                self.cur_param.push(d);
831                self.st = DcsParam;
832            }
833            b';' => {
834                self.next_param();
835                self.st = DcsParam;
836            }
837            b':' => {
838                self.st = DcsIgnore;
839            }
840            c if is_intermediate(c) => {
841                self.ints.push(c);
842                self.st = DcsInt;
843            }
844            c if is_final(c) => {
845                self.dcs_start(c, cb);
846                self.st = DcsPassthrough;
847            }
848            _ => {
849                self.st = DcsIgnore;
850            }
851        }
852    }
853    fn on_dcs_param<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
854        use State::*;
855        match b {
856            CAN | SUB => {
857                self.st = Ground;
858            }
859            DEL => {}
860            ESC => {
861                self.st = Escape;
862            }
863            d if is_digit(d) => {
864                self.cur_param.push(d);
865            }
866            b';' => {
867                self.next_param();
868            }
869            b':' => {
870                self.st = DcsIgnore;
871            }
872            c if is_intermediate(c) => {
873                self.ints.push(c);
874                self.st = DcsInt;
875            }
876            c if is_final(c) => {
877                self.dcs_start(c, cb);
878                self.st = DcsPassthrough;
879            }
880            _ => {
881                self.st = DcsIgnore;
882            }
883        }
884    }
885    fn on_dcs_int<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
886        use State::*;
887        match b {
888            CAN | SUB => {
889                self.st = Ground;
890            }
891            DEL => {}
892            ESC => {
893                self.st = Escape;
894            }
895            c if is_intermediate(c) => {
896                self.ints.push(c);
897            }
898            c if is_final(c) => {
899                self.dcs_start(c, cb);
900                self.st = DcsPassthrough;
901            }
902            _ => {
903                self.st = DcsIgnore;
904            }
905        }
906    }
907    fn on_dcs_ignore<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
908        use State::*;
909        match b {
910            CAN | SUB => {
911                self.st = Ground;
912            }
913            DEL => {}
914            ESC => {
915                self.st = Escape;
916            }
917            // stay until ST (handled in DcsEsc path if you want); we just drop here.
918            _ => {}
919        }
920    }
921    fn on_dcs_pass<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
922        use State::*;
923        match b {
924            CAN | SUB => {
925                self.dcs_end(cb);
926                self.st = Ground;
927            }
928            DEL => {}
929            ESC => {
930                self.st = DcsEsc;
931            }
932            _ => {
933                self.dcs_put(b, cb);
934            }
935        }
936    }
937    fn on_dcs_esc<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
938        use State::*;
939        match b {
940            ST_FINAL => {
941                self.dcs_end(cb);
942                self.st = Ground;
943            } // ST
944            ESC => {
945                self.dcs_put(ESC, cb); /* remain in DcsEsc */
946            }
947            _ => {
948                self.dcs_put(ESC, cb);
949                self.dcs_put(b, cb);
950                self.st = DcsPassthrough;
951            }
952        }
953    }
954
955    // ---- OSC
956    fn on_osc_string<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
957        use State::*;
958        match b {
959            CAN | SUB => {
960                self.reset_collectors();
961                self.st = Ground;
962            }
963            DEL => {}
964            BEL => {
965                self.used_bel = true;
966                self.osc_end(cb);
967                self.st = Ground;
968            }
969            ESC => {
970                self.st = State::OscEsc;
971            }
972            p if is_printable(p) => {
973                self.osc_put(p, cb);
974            }
975            _ => {} // ignore other C0
976        }
977    }
978    fn on_osc_esc<F: FnMut(VTEvent)>(&mut self, b: u8, cb: &mut F) {
979        use State::*;
980        match b {
981            ST_FINAL => {
982                self.used_bel = false;
983                self.osc_end(cb);
984                self.st = Ground;
985            } // ST
986            ESC => {
987                self.osc_put(ESC, cb); /* remain in OscEsc */
988            }
989            _ => {
990                self.osc_put(ESC, cb);
991                self.osc_put(b, cb);
992                self.st = OscString;
993            }
994        }
995    }
996
997    // ---- SOS/PM/APC (ignored payload as per your machine)
998    fn on_spa_string<F: FnMut(VTEvent)>(&mut self, b: u8, _cb: &mut F) {
999        match b {
1000            CAN | SUB => {
1001                self.reset_collectors();
1002                self.st = State::Ground;
1003            }
1004            DEL => {}
1005            ESC => {
1006                self.st = State::SpaEsc;
1007            }
1008            _ => {}
1009        }
1010    }
1011    fn on_spa_esc<F: FnMut(VTEvent)>(&mut self, b: u8, _cb: &mut F) {
1012        match b {
1013            ST_FINAL => {
1014                self.reset_collectors();
1015                self.st = State::Ground;
1016            }
1017            ESC => { /* remain */ }
1018            _ => {
1019                self.st = State::SosPmApcString;
1020            }
1021        }
1022    }
1023}
1024
1025#[cfg(test)]
1026mod tests {
1027    use pretty_assertions::assert_eq;
1028
1029    use super::*;
1030
1031    fn decode_stream(input: &[u8]) -> String {
1032        println!("Input:");
1033        let mut s = Vec::new();
1034        _ = hxdmp::hexdump(input, &mut s);
1035        println!("{}", String::from_utf8_lossy(&s));
1036        let mut parser = VTPushParser::new();
1037        let mut result = String::new();
1038        let mut callback = |vt_input: VTEvent<'_>| {
1039            result.push_str(&format!("{:?}\n", vt_input));
1040        };
1041        parser.feed_with(input, &mut callback);
1042        println!("Result:");
1043        println!("{}", result);
1044        result
1045    }
1046
1047    #[test]
1048    fn test_large_escape2() {
1049        let result = decode_stream(
1050            &hex::decode(
1051                r#"
1052        1b5b495445524d3220332e352e31346e1b5d31303b7267623a646361612f6
1053        46361622f646361611b5c1b5d31313b7267623a313538652f313933612f31
1054        6537351b5c1b5b3f36343b313b323b343b363b31373b31383b32313b32326
1055        31b5b3e36343b323530303b30631b50217c36393534373236441b5c1b503e
1056        7c695465726d3220332e352e31341b5c1b5b383b33343b31343874"#
1057                    .replace(char::is_whitespace, ""),
1058            )
1059            .unwrap(),
1060        );
1061        assert_eq!(
1062            result.trim(),
1063            r#"
1064Csi(, '', 'I')
1065Raw('TERM2 3.5.14n')
1066OscStart
1067OscData('10;rgb:dcaa/dcab/dcaa')
1068OscEnd
1069OscStart
1070OscData('11;rgb:158e/193a/1e75')
1071OscEnd
1072Csi('?', '64', '1', '2', '4', '6', '17', '18', '21', '22', '', 'c')
1073Csi('>', '64', '2500', '0', '', 'c')
1074DcsStart(, '!', |)
1075DcsData('6954726D')
1076DcsEnd
1077DcsStart('>', '', |)
1078DcsData('iTerm2 3.5.14')
1079DcsEnd
1080Csi(, '8', '34', '148', '', 't')
1081        "#
1082            .trim()
1083        );
1084    }
1085}