vt_push_parser/
lib.rs

1//! A streaming push parser for the VT/xterm protocol.
2//!
3//! Use [`VTPushParser::feed_with`] to feed bytes into the parser, handling the
4//! [`VTEvent`]s as they are emitted.
5//!
6//! ```rust
7//! use vt_push_parser::VTPushParser;
8//! use vt_push_parser::event::VTEvent;
9//!
10//! let mut parser = VTPushParser::new();
11//! let mut output = String::new();
12//! parser.feed_with(b"\x1b[32mHello, world!\x1b[0m", &mut |event: VTEvent| {
13//!     output.push_str(&format!("{:?}", event));
14//! });
15//! assert_eq!(output, "Csi('32', '', 'm')Raw('Hello, world!')Csi('0', '', 'm')");
16//! ```
17//!
18//! ## Interest
19//!
20//! The parser can be configured to only emit certain types of events by setting
21//! the `INTEREST` parameter. Other event types will be parsed and discarded.
22//!
23//! For example, to only emit CSI (and Raw) events:
24//!
25//! ```rust
26//! use vt_push_parser::{VTPushParser, VT_PARSER_INTEREST_CSI};
27//!
28//! let mut parser = VTPushParser::new_with_interest::<VT_PARSER_INTEREST_CSI>();
29//! ```
30//!
31//! ## Input parsing
32//!
33//! This crate is designed to be used for parsing terminal output, but it can
34//! also be used for parsing input. Input is not always well-formed, however and
35//! may contain mode-switching escapes that require the parser to turn off its
36//! normal parsing behaviours (ie: bracketed-paste mode, xterm mouse events,
37//! etc).
38//!
39//! The [`capture::VTCapturePushParser`] is useful for parsing input that may
40//! work in this way.
41pub mod ascii;
42pub mod capture;
43pub mod event;
44pub mod iter;
45pub mod signature;
46
47use smallvec::SmallVec;
48
49use ascii::AsciiControl;
50use event::{CSI, DCS, Esc, EscInvalid, SS2, SS3, VTEvent, VTIntermediate};
51
52const ESC: u8 = AsciiControl::Esc as _;
53const BEL: u8 = AsciiControl::Bel as _;
54const DEL: u8 = AsciiControl::Del as _;
55const CAN: u8 = AsciiControl::Can as _;
56const SUB: u8 = AsciiControl::Sub as _;
57const CSI: u8 = b'[';
58const OSC: u8 = b']';
59const SS2: u8 = b'N';
60const SS3: u8 = b'O';
61const DCS: u8 = b'P';
62const APC: u8 = b'_';
63const PM: u8 = b'^';
64const SOS: u8 = b'X';
65const ST_FINAL: u8 = b'\\';
66
67use crate::event::{Param, ParamBuf, Params};
68
69/// Receives a single [`VTEvent`].
70#[allow(private_bounds)]
71pub trait VTEventCallback: VTEventCallbackMaybeAbortable<()> {
72    fn event(&mut self, event: VTEvent<'_>) -> ();
73}
74
75impl<T: FnMut(VTEvent<'_>)> VTEventCallback for T {
76    #[inline(always)]
77    fn event(&mut self, event: VTEvent<'_>) {
78        self(event)
79    }
80}
81
82impl<T: VTEventCallback> VTEventCallbackMaybeAbortable<()> for T {
83    #[inline(always)]
84    fn event(&mut self, event: VTEvent<'_>) {
85        VTEventCallback::event(self, event)
86    }
87}
88
89/// Receives a single [`VTEvent`], returning a boolean indicating whether to
90/// continue parsing (`true`) or stop parsing (`false`).
91#[allow(private_bounds)]
92pub trait VTEventCallbackAbortable: VTEventCallbackMaybeAbortable<bool> {
93    fn event(&mut self, event: VTEvent<'_>) -> bool;
94}
95
96impl<T: VTEventCallbackAbortable> VTEventCallbackMaybeAbortable<bool> for T {
97    #[inline(always)]
98    fn event(&mut self, event: VTEvent<'_>) -> bool {
99        VTEventCallbackAbortable::event(self, event)
100    }
101}
102
103impl<T: FnMut(VTEvent<'_>) -> bool> VTEventCallbackAbortable for T {
104    #[inline(always)]
105    fn event(&mut self, event: VTEvent<'_>) -> bool {
106        self(event)
107    }
108}
109
110trait VTEventCallbackMaybeAbortable<R: MaybeAbortable> {
111    fn event<'e>(&mut self, event: VTEvent<'e>) -> R;
112}
113
114/// The action to take with the most recently accumulated byte.
115enum VTAction<'a> {
116    /// The parser will accumulate the byte and continue processing. If
117    /// currently buffered, emit the buffered bytes.
118    None,
119    /// The parser emitted an event. If currently buffered, emit the buffered
120    /// bytes.
121    Event(VTEvent<'a>),
122    /// The parser ended a region.
123    End(VTEnd),
124    /// Start or continue buffering bytes. Include the current byte in the
125    /// buffer.
126    Buffer(VTEmit),
127    /// Hold this byte until the next byte is received. If another byte is
128    /// already held, emit the previous byte.
129    Hold(VTEmit),
130    /// Cancel the current buffer.
131    Cancel(VTEmit),
132}
133
134#[derive(Debug, Copy, Clone, PartialEq, Eq)]
135enum VTEmit {
136    /// Emit this byte as a ground-state character.
137    Ground,
138    /// Emit this byte into the current DCS stream.
139    Dcs,
140    /// Emit this byte into the current OSC stream.
141    Osc,
142}
143
144#[derive(Debug, Copy, Clone, PartialEq, Eq)]
145enum VTEnd {
146    /// Emit this byte into the current DCS stream.
147    Dcs,
148    /// Emit this byte into the current OSC stream.
149    Osc { used_bel: bool },
150}
151
152macro_rules! def_pattern {
153    ($name:ident => $pattern:pat) => {
154        #[inline]
155        #[allow(unused)]
156        const fn $name(b: u8) -> bool {
157            matches!(b, $pattern)
158        }
159
160        #[allow(unused)]
161        macro_rules! $name {
162            () => {
163                $pattern
164            };
165        }
166    };
167}
168
169def_pattern!(is_c0 => 0x00..=0x08 | 0x0b..=0x0c | 0x0e..=0x1f);
170def_pattern!(is_any_c0 => 0x00..=0x1f);
171def_pattern!(is_printable => b' '..=b'~');
172def_pattern!(is_intermediate => b' '..=b'/');
173def_pattern!(is_final => 0x40..=0x7e);
174def_pattern!(is_priv => b'<' | b'=' | b'>' | b'?');
175def_pattern!(is_priv_no_q => b'<' | b'=' | b'>');
176def_pattern!(is_digit => b'0'..=b'9');
177
178macro_rules! byte_predicate {
179    (|$p:ident| $body:block) => {{
180        let mut out: [bool; 256] = [false; 256];
181        let mut i = 0;
182        while i < 256 {
183            let $p: u8 = i as u8;
184            out[i] = $body;
185            i += 1;
186        }
187        out
188    }};
189}
190
191const ENDS_CSI: [bool; 256] =
192    byte_predicate!(|b| { is_final(b) || b == ESC || b == CAN || b == SUB });
193
194const ENDS_GROUND: [bool; 256] = byte_predicate!(|b| { is_c0(b) || b == DEL });
195
196#[derive(Debug, Copy, Clone, PartialEq, Eq)]
197enum State {
198    Ground,
199    Escape,
200    EscInt,
201    EscSs2,
202    EscSs3,
203    CsiEntry,
204    CsiParam,
205    CsiInt,
206    CsiIgnore,
207    DcsEntry,
208    DcsParam,
209    DcsInt,
210    DcsIgnore,
211    DcsIgnoreEsc,
212    DcsPassthrough,
213    DcsEsc,
214    OscString,
215    OscEsc,
216    SosPmApcString,
217    SpaEsc,
218}
219
220/// No events from parser (ie, only emits [`VTEvent::Raw`] events)
221pub const VT_PARSER_INTEREST_NONE: u8 = 0;
222/// Request CSI events from parser.
223pub const VT_PARSER_INTEREST_CSI: u8 = 1 << 0;
224/// Request DCS events from parser.
225pub const VT_PARSER_INTEREST_DCS: u8 = 1 << 1;
226/// Request OSC events from parser.
227pub const VT_PARSER_INTEREST_OSC: u8 = 1 << 2;
228/// Request escape recovery events from parser.
229pub const VT_PARSER_INTEREST_ESCAPE_RECOVERY: u8 = 1 << 4;
230/// Request other events from parser.
231pub const VT_PARSER_INTEREST_OTHER: u8 = 1 << 5;
232
233/// Request all events from parser.
234pub const VT_PARSER_INTEREST_ALL: u8 = VT_PARSER_INTEREST_CSI
235    | VT_PARSER_INTEREST_DCS
236    | VT_PARSER_INTEREST_OSC
237    | VT_PARSER_INTEREST_ESCAPE_RECOVERY
238    | VT_PARSER_INTEREST_OTHER;
239
240/// Default interest level.
241pub const VT_PARSER_INTEREST_DEFAULT: u8 = VT_PARSER_INTEREST_CSI
242    | VT_PARSER_INTEREST_DCS
243    | VT_PARSER_INTEREST_OSC
244    | VT_PARSER_INTEREST_OTHER;
245
246#[must_use]
247trait MaybeAbortable {
248    fn abort(self) -> bool;
249}
250
251impl MaybeAbortable for bool {
252    #[inline(always)]
253    fn abort(self) -> bool {
254        !self
255    }
256}
257
258impl MaybeAbortable for () {
259    #[inline(always)]
260    fn abort(self) -> bool {
261        false
262    }
263}
264
265/// A push parser for the VT/xterm protocol.
266///
267/// The parser can be configured to only emit certain types of events by setting
268/// the `INTEREST` parameter.
269pub struct VTPushParser<const INTEREST: u8 = VT_PARSER_INTEREST_DEFAULT> {
270    st: State,
271
272    // Header collectors for short escapes (we borrow from these in callbacks)
273    ints: VTIntermediate,
274    params: Params,
275    cur_param: Param,
276    priv_prefix: Option<u8>,
277    held_byte: Option<u8>,
278}
279
280impl Default for VTPushParser {
281    fn default() -> Self {
282        Self::new()
283    }
284}
285
286impl VTPushParser {
287    pub const fn new() -> Self {
288        VTPushParser::new_with()
289    }
290
291    /// Decode a buffer of bytes into a series of events.
292    pub fn decode_buffer<'a>(input: &'a [u8], mut cb: impl for<'b> FnMut(VTEvent<'b>)) {
293        let mut parser = VTPushParser::new();
294        parser.feed_with(input, &mut cb);
295    }
296
297    pub const fn new_with_interest<const INTEREST: u8>() -> VTPushParser<INTEREST> {
298        VTPushParser::new_with()
299    }
300}
301
302/// Emit the EscInvalid event
303macro_rules! invalid {
304    ($self:ident .priv_prefix, $self_:ident .ints, $b:expr) => {
305        if let Some(p) = $self.priv_prefix {
306            if $self.ints.len() == 0 {
307                VTEvent::EscInvalid(EscInvalid::Two(p, $b))
308            } else if $self.ints.len() == 1 {
309                VTEvent::EscInvalid(EscInvalid::Three(p, $self.ints.data[0], $b))
310            } else {
311                VTEvent::EscInvalid(EscInvalid::Four(
312                    p,
313                    $self.ints.data[0],
314                    $self.ints.data[1],
315                    $b,
316                ))
317            }
318        } else {
319            if $self.ints.len() == 0 {
320                VTEvent::EscInvalid(EscInvalid::One($b))
321            } else if $self.ints.len() == 1 {
322                VTEvent::EscInvalid(EscInvalid::Two($self.ints.data[0], $b))
323            } else {
324                VTEvent::EscInvalid(EscInvalid::Three(
325                    $self.ints.data[0],
326                    $self.ints.data[1],
327                    $b,
328                ))
329            }
330        }
331    };
332    ($self:ident .priv_prefix, $self_:ident .ints) => {
333        if let Some(p) = $self.priv_prefix {
334            if $self.ints.len() == 0 {
335                VTEvent::EscInvalid(EscInvalid::One(p))
336            } else if $self.ints.len() == 1 {
337                VTEvent::EscInvalid(EscInvalid::Two(p, $self.ints.data[0]))
338            } else {
339                VTEvent::EscInvalid(EscInvalid::Three(p, $self.ints.data[0], $self.ints.data[1]))
340            }
341        } else {
342            if $self.ints.len() == 0 {
343                // I don't think this can happen
344                VTEvent::C0(0x1b)
345            } else if $self.ints.len() == 1 {
346                VTEvent::EscInvalid(EscInvalid::One($self.ints.data[0]))
347            } else {
348                VTEvent::EscInvalid(EscInvalid::Two($self.ints.data[0], $self.ints.data[1]))
349            }
350        }
351    };
352    ($a:expr) => {
353        VTEvent::EscInvalid(EscInvalid::One($a))
354    };
355    ($a:expr, $b:expr) => {
356        VTEvent::EscInvalid(EscInvalid::Two($a, $b))
357    };
358}
359
360impl<const INTEREST: u8> VTPushParser<INTEREST> {
361    const fn new_with() -> Self {
362        Self {
363            st: State::Ground,
364            ints: VTIntermediate::empty(),
365            params: SmallVec::new_const(),
366            cur_param: SmallVec::new_const(),
367            priv_prefix: None,
368            held_byte: None,
369        }
370    }
371
372    // =====================
373    // Callback-driven API
374    // =====================
375
376    /// Feed bytes into the parser. This is the main entry point for the parser.
377    /// It will call the callback with events as they are emitted.
378    ///
379    /// The [`VTEventCallback`] callback must be valid for the lifetime of the
380    /// `feed_with` call. The [`VTEventCallback`] trait is implemented for all
381    /// function pointers and closures, as well as mutable references to them.
382    ///
383    /// This function may emit any number of events (including zero), depending
384    /// on the state of the internal parser.
385    ///
386    /// ## With a closure
387    ///
388    /// ```rust
389    /// use vt_push_parser::{VTPushParser, event::VTEvent};
390    ///
391    /// let mut parser = VTPushParser::new();
392    /// parser.feed_with(b"\x1b[32mHello, world!\x1b[0m", |event: VTEvent| {
393    ///     println!("{:?}", event);
394    /// });
395    /// ```
396    ///
397    /// ## With a custom callback
398    ///
399    /// ```rust
400    /// use vt_push_parser::{VTPushParser, VTEventCallback, event::VTEvent};
401    ///
402    /// struct MyCallback {
403    ///     output: String,
404    /// }
405    ///
406    /// impl VTEventCallback for MyCallback {
407    ///     fn event(&mut self, event: VTEvent) {
408    ///         self.output.push_str(&format!("{:?}", event));
409    ///     }
410    /// }
411    ///
412    /// let mut parser = VTPushParser::new();
413    /// parser.feed_with(b"\x1b[32mHello, world!\x1b[0m", MyCallback { output: String::new() });
414    /// ```
415    #[inline]
416    pub fn feed_with<F: VTEventCallback>(&mut self, input: &[u8], mut cb: F) {
417        self.feed_with_internal(input, &mut cb);
418    }
419
420    /// Feed bytes into the parser. This is the main entry point for the parser.
421    /// It will call the callback with events as they are emitted.
422    ///
423    /// The callback must be valid for the lifetime of the `feed_with` call.
424    /// Returning `true` will continue parsing, while returning `false` will
425    /// stop.
426    ///
427    /// The callback may emit any number of events (including zero), depending
428    /// on the state of the internal parser.
429    ///
430    /// This function returns the number of bytes processed. Note that some
431    /// bytes may have been processed any not emitted.
432    #[inline]
433    pub fn feed_with_abortable<F: VTEventCallbackAbortable>(
434        &mut self,
435        input: &[u8],
436        mut cb: F,
437    ) -> usize {
438        self.feed_with_internal(input, &mut cb)
439    }
440
441    #[inline(always)]
442    fn feed_with_internal<R: MaybeAbortable, F: VTEventCallbackMaybeAbortable<R>>(
443        &mut self,
444        input: &[u8],
445        cb: &mut F,
446    ) -> usize {
447        if input.is_empty() {
448            return 0;
449        }
450
451        #[derive(Debug)]
452        struct FeedState {
453            buffer_idx: usize,
454            current_emit: Option<VTEmit>,
455            hold: bool,
456        }
457
458        let mut state = FeedState {
459            buffer_idx: 0,
460            current_emit: None,
461            hold: self.held_byte.is_some(),
462        };
463
464        let mut held_byte = self.held_byte.take();
465        let mut i = 0;
466
467        while i < input.len() {
468            // Fast path for the common case of no ANSI escape sequences.
469            if self.st == State::Ground {
470                let start = i;
471                loop {
472                    if i >= input.len() {
473                        cb.event(VTEvent::Raw(&input[start..]));
474                        return input.len();
475                    }
476                    if ENDS_GROUND[input[i] as usize] {
477                        break;
478                    }
479                    i += 1;
480                }
481
482                if start != i && cb.event(VTEvent::Raw(&input[start..i])).abort() {
483                    return i;
484                }
485
486                if input[i] == ESC {
487                    self.clear_hdr_collectors();
488                    self.st = State::Escape;
489                    i += 1;
490                    continue;
491                }
492            }
493
494            // Fast path: search for the CSI final
495            if self.st == State::CsiIgnore {
496                loop {
497                    if i >= input.len() {
498                        return input.len();
499                    }
500                    if ENDS_CSI[input[i] as usize] {
501                        break;
502                    }
503                    i += 1;
504                }
505
506                if input[i] == ESC {
507                    self.st = State::Escape;
508                } else {
509                    self.st = State::Ground;
510                }
511                i += 1;
512                continue;
513            }
514
515            let action = self.push_with(input[i]);
516
517            match action {
518                VTAction::None => {
519                    if let Some(emit) = state.current_emit {
520                        // We received a DEL during an emit, so we need to partially emit our buffer
521                        let range = state.buffer_idx..(i - state.hold as usize);
522                        if !range.is_empty()
523                            && match emit {
524                                VTEmit::Ground => cb.event(VTEvent::Raw(&input[range])),
525                                VTEmit::Dcs => cb.event(VTEvent::DcsData(&input[range])),
526                                VTEmit::Osc => cb.event(VTEvent::OscData(&input[range])),
527                            }
528                            .abort()
529                        {
530                            if state.hold {
531                                self.held_byte = Some(0x1b);
532                            }
533                            return i + 1;
534                        }
535                        if state.hold {
536                            held_byte = Some(0x1b);
537                        }
538                        state.current_emit = None;
539                    }
540                }
541                VTAction::Event(e) => {
542                    if cb.event(e).abort() {
543                        return i + 1;
544                    }
545                }
546                VTAction::End(VTEnd::Dcs) => {
547                    state.current_emit = None;
548                    let hold = std::mem::take(&mut state.hold);
549                    let range = state.buffer_idx..(i.saturating_sub(hold as usize));
550                    if cb.event(VTEvent::DcsEnd(&input[range])).abort() {
551                        return i + 1;
552                    }
553                    held_byte = None;
554                }
555                VTAction::End(VTEnd::Osc { used_bel }) => {
556                    state.current_emit = None;
557                    let hold = std::mem::take(&mut state.hold);
558                    let range = state.buffer_idx..(i.saturating_sub(hold as usize));
559                    if cb
560                        .event(VTEvent::OscEnd {
561                            data: &input[range],
562                            used_bel,
563                        })
564                        .abort()
565                    {
566                        return i + 1;
567                    }
568                    held_byte = None;
569                }
570                VTAction::Buffer(emit) | VTAction::Hold(emit) => {
571                    if state.current_emit.is_none()
572                        && let Some(h) = held_byte.take()
573                        && match emit {
574                            VTEmit::Ground => cb.event(VTEvent::Raw(&[h])),
575                            VTEmit::Dcs => cb.event(VTEvent::DcsData(&[h])),
576                            VTEmit::Osc => cb.event(VTEvent::OscData(&[h])),
577                        }
578                        .abort()
579                    {
580                        if matches!(action, VTAction::Hold(_)) {
581                            self.held_byte = Some(0x1b);
582                            return 1;
583                        }
584                        return 0;
585                    }
586
587                    debug_assert!(state.current_emit.is_none() || state.current_emit == Some(emit));
588
589                    state.hold = matches!(action, VTAction::Hold(_));
590                    if state.current_emit.is_none() {
591                        state.buffer_idx = i;
592                        state.current_emit = Some(emit);
593                    }
594                }
595                VTAction::Cancel(emit) => {
596                    state.current_emit = None;
597                    state.hold = false;
598                    if match emit {
599                        VTEmit::Ground => unreachable!(),
600                        VTEmit::Dcs => cb.event(VTEvent::DcsCancel),
601                        VTEmit::Osc => cb.event(VTEvent::OscCancel),
602                    }
603                    .abort()
604                    {
605                        return i + 1;
606                    }
607                }
608            };
609            i += 1;
610        }
611
612        // Is there more to emit?
613        if state.hold {
614            self.held_byte = Some(0x1b);
615        }
616
617        if let Some(emit) = state.current_emit.take() {
618            let range = &input[state.buffer_idx..input.len() - state.hold as usize];
619            if !range.is_empty() {
620                match emit {
621                    VTEmit::Ground => cb.event(VTEvent::Raw(range)),
622                    VTEmit::Dcs => cb.event(VTEvent::DcsData(range)),
623                    VTEmit::Osc => cb.event(VTEvent::OscData(range)),
624                };
625            }
626        };
627
628        // If we get this far, we processed the whole buffer
629        input.len()
630    }
631
632    /// Returns true if the parser is in the ground state.
633    pub fn is_ground(&self) -> bool {
634        self.st == State::Ground
635    }
636
637    /// Feed an idle event into the parser. This will emit a C0(ESC) event if
638    /// the parser is in the Escape state, and will silently cancel any EscInt
639    /// state.
640    pub fn idle(&mut self) -> Option<VTEvent<'static>> {
641        match self.st {
642            State::Escape => {
643                self.st = State::Ground;
644                Some(VTEvent::C0(ESC))
645            }
646            State::EscInt => {
647                self.st = State::Ground;
648                if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
649                    None
650                } else {
651                    Some(invalid!(self.priv_prefix, self.ints))
652                }
653            }
654            State::EscSs2 | State::EscSs3 => {
655                if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
656                    self.st = State::Ground;
657                    None
658                } else {
659                    let c = match self.st {
660                        State::EscSs2 => SS2,
661                        State::EscSs3 => SS3,
662                        _ => unreachable!(),
663                    };
664                    self.st = State::Ground;
665                    Some(invalid!(c))
666                }
667            }
668            _ => None,
669        }
670    }
671
672    pub fn finish<F: FnMut(VTEvent)>(&mut self, _cb: &mut F) {
673        self.reset_collectors();
674        self.st = State::Ground;
675
676        // TODO
677    }
678
679    // =====================
680    // Emit helpers (borrowed)
681    // =====================
682
683    fn clear_hdr_collectors(&mut self) {
684        self.ints.clear();
685        self.params.clear();
686        self.cur_param.clear();
687        self.priv_prefix = None;
688    }
689
690    fn reset_collectors(&mut self) {
691        self.clear_hdr_collectors();
692    }
693
694    fn next_param(&mut self) {
695        self.params.push(std::mem::take(&mut self.cur_param));
696    }
697
698    fn finish_params_if_any(&mut self) {
699        if !self.cur_param.is_empty() || !self.params.is_empty() {
700            self.next_param();
701        }
702    }
703
704    fn emit_csi(&mut self, final_byte: u8) -> VTAction {
705        self.finish_params_if_any();
706
707        // Build borrowed views into self.params
708        let mut borrowed: SmallVec<[&[u8]; 4]> = SmallVec::new();
709        borrowed.extend(self.params.iter().map(|v| v.as_slice()));
710
711        let privp = self.priv_prefix.take();
712        VTAction::Event(VTEvent::Csi(CSI {
713            private: privp,
714            params: ParamBuf {
715                params: &self.params,
716            },
717            intermediates: self.ints,
718            final_byte,
719        }))
720    }
721
722    fn dcs_start(&mut self, final_byte: u8) -> VTAction {
723        self.finish_params_if_any();
724
725        let privp = self.priv_prefix.take();
726        VTAction::Event(VTEvent::DcsStart(DCS {
727            private: privp,
728            params: ParamBuf {
729                params: &self.params,
730            },
731            intermediates: self.ints,
732            final_byte,
733        }))
734    }
735
736    // =====================
737    // State handlers
738    // =====================
739
740    fn push_with(&mut self, b: u8) -> VTAction {
741        use State::*;
742
743        macro_rules! state_machine {
744            (def $c:ident $(
745                $state:ident => { $( $p:pat
746                    $(if $( $if_ident:ident $( ($call_ident:ident) )? $( == $literal:literal)? $(||)? )+)?
747                    => $block:expr $(,)? )* }
748            )*) => {
749                #[allow(unused)]
750                match self.st {
751                    $(
752                        $state => {
753                            match b {
754                                $(
755                                    $c @ state_machine!(pattern $p $(if $( ($if_ident ($($call_ident)?) ($($literal)?)) )+ )?) => $block,
756                                )*
757                            }
758                        }
759                    )*
760                }
761            };
762            (pattern $pat:pat if $( ($if_ident:ident ($($call_ident:ident)?) ($($literal:literal)?)) )+ ) => {
763                $(
764                    state_machine!(pattern_if ($if_ident) ($($call_ident)?) ($($literal)?) )
765                )|+
766            };
767            (pattern $pat:pat) => {
768                $pat
769            };
770            (pattern_if ($any:ident) ($call_ident:ident) ()) => {
771                $any!()
772            };
773            (pattern_if ($if_ident:ident) () ($literal:literal)) => {
774                $literal
775            };
776        }
777
778        // This builds a mega state machine that is somewhat easier for LLVM to
779        // optimize than nesting functions. We parse the match-like patterns
780        // into true byte ranges for the best possible performance.
781
782        // Note that because of macro limitations, every binding is declared as
783        // "c".
784        state_machine! {
785            def c
786            Ground => {
787                ESC => {
788                    self.clear_hdr_collectors();
789                    self.st = State::Escape;
790                    VTAction::None
791                }
792                DEL => VTAction::Event(VTEvent::C0(DEL)),
793                c if is_c0(c) => VTAction::Event(VTEvent::C0(c)),
794                _ => VTAction::Buffer(VTEmit::Ground),
795            }
796            Escape => {
797                CAN | SUB => {
798                    self.st = Ground;
799                    if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
800                        VTAction::None
801                    } else {
802                        VTAction::Event(invalid!(b))
803                    }
804                }
805                // NOTE: DEL should be ignored normally, but for better recovery,
806                // we move to ground state here instead.
807                DEL => {
808                    self.st = Ground;
809                    if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
810                        VTAction::None
811                    } else {
812                        VTAction::Event(invalid!(b))
813                    }
814                }
815                c if is_intermediate(c) => {
816                    if self.ints.push(c) {
817                        self.st = EscInt;
818                    } else {
819                        self.st = Ground;
820                    }
821                    VTAction::None
822                }
823                // Not all private sequences are supported -- only ESC ? <final>
824                b'?' => {
825                    self.priv_prefix = Some(b);
826                    self.st = EscInt;
827                    VTAction::None
828                }
829                // The rest of the private sequences become ESC <final>
830                c if is_priv_no_q(c) => {
831                    self.st = Ground;
832                    VTAction::Event(VTEvent::Esc(Esc {
833                        intermediates: VTIntermediate::empty(),
834                        private: None,
835                        final_byte: b,
836                    }))
837                }
838                CSI => {
839                    if INTEREST & VT_PARSER_INTEREST_CSI == 0 {
840                        self.st = CsiIgnore;
841                    } else {
842                        self.st = CsiEntry;
843                    }
844                    VTAction::None
845                }
846                DCS => {
847                    if INTEREST & VT_PARSER_INTEREST_DCS == 0 {
848                        self.st = DcsIgnore;
849                    } else {
850                        self.st = DcsEntry;
851                    }
852                    VTAction::None
853                }
854                OSC => {
855                    self.st = OscString;
856                    VTAction::Event(VTEvent::OscStart)
857                }
858                SS2 => {
859                    self.st = EscSs2;
860                    VTAction::None
861                }
862                SS3 => {
863                    self.st = EscSs3;
864                    VTAction::None
865                }
866                SOS | PM | APC => {
867                    self.st = State::SosPmApcString;
868                    VTAction::None
869                }
870                c if is_final(c) || is_digit(c) => {
871                    self.st = Ground;
872                    VTAction::Event(VTEvent::Esc(Esc {
873                        intermediates: self.ints,
874                        private: self.priv_prefix.take(),
875                        final_byte: c,
876                    }))
877                }
878                ESC => {
879                    // ESC ESC allowed, but we stay in the current state
880                    VTAction::Event(VTEvent::C0(ESC))
881                }
882                _ => {
883                    self.st = Ground;
884                    if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
885                        VTAction::None
886                    } else {
887                        VTAction::Event(invalid!(b))
888                    }
889                }
890            }
891            EscInt => {
892                CAN | SUB => {
893                    self.st = Ground;
894                    if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
895                        VTAction::None
896                    } else {
897                        VTAction::Event(invalid!(self.priv_prefix, self.ints, b))
898                    }
899                }
900                // NOTE: DEL should be ignored normally, but for better recovery,
901                // we move to ground state here instead.
902                DEL => {
903                    self.st = Ground;
904                    if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
905                        VTAction::None
906                    } else {
907                        VTAction::Event(invalid!(self.priv_prefix, self.ints, b))
908                    }
909                }
910                c if is_intermediate(c) => {
911                    if !self.ints.push(c) {
912                        self.st = Ground;
913                        if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
914                            VTAction::None
915                        } else {
916                            VTAction::Event(invalid!(self.priv_prefix, self.ints, b))
917                        }
918                    } else {
919                        VTAction::None
920                    }
921                }
922                c if is_final(c) || is_digit(c) => {
923                    self.st = Ground;
924                    VTAction::Event(VTEvent::Esc(Esc {
925                        intermediates: self.ints,
926                        private: self.priv_prefix.take(),
927                        final_byte: c,
928                    }))
929                }
930                // NOTE: We assume that we want to stay in the escape state
931                // to recover from this state.
932                ESC => {
933                    self.st = Escape;
934                    if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
935                        VTAction::None
936                    } else {
937                        VTAction::Event(invalid!(self.priv_prefix, self.ints))
938                    }
939                }
940                _ => {
941                    self.st = Ground;
942                    if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
943                        VTAction::None
944                    } else {
945                        VTAction::Event(invalid!(self.priv_prefix, self.ints, c))
946                    }
947                }
948            }
949            EscSs2 => {
950                CAN | SUB => {
951                    self.st = Ground;
952                    if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
953                        VTAction::None
954                    } else {
955                        VTAction::Event(invalid!(SS2, b))
956                    }
957                }
958                // NOTE: We assume that we want to stay in the escape state
959                // to recover from this state.
960                ESC => {
961                    self.st = Escape;
962                    if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
963                        VTAction::None
964                    } else {
965                        VTAction::Event(invalid!(SS2))
966                    }
967                }
968                _ => {
969                    self.st = Ground;
970                    VTAction::Event(VTEvent::Ss2(SS2 { char: c }))
971                }
972            }
973            EscSs3 => {
974                CAN | SUB => {
975                    self.st = Ground;
976                    if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
977                        VTAction::None
978                    } else {
979                        VTAction::Event(invalid!(SS3, b))
980                    }
981                }
982                // NOTE: We assume that we want to stay in the escape state
983                // to recover from this state.
984                ESC => {
985                    self.st = Escape;
986                    if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
987                        VTAction::None
988                    } else {
989                        VTAction::Event(invalid!(SS3))
990                    }
991                }
992                _ => {
993                    self.st = Ground;
994                    VTAction::Event(VTEvent::Ss3(SS3 { char: c }))
995                }
996            }
997            CsiEntry => {
998                CAN | SUB => {
999                    self.st = Ground;
1000                    VTAction::None
1001                }
1002                DEL => VTAction::None,
1003                ESC => {
1004                    self.st = Escape;
1005                    VTAction::None
1006                }
1007                // Weird case: if we encounter C0 inside of CSI, emit it while we parse.
1008                c if is_any_c0(c) => VTAction::Event(VTEvent::C0(c)),
1009                c if is_priv(c) => {
1010                    self.priv_prefix = Some(c);
1011                    self.st = CsiParam;
1012                    VTAction::None
1013                }
1014                c if is_digit(c) => {
1015                    self.cur_param.push(c);
1016                    self.st = CsiParam;
1017                    VTAction::None
1018                }
1019                b';' => {
1020                    self.next_param();
1021                    self.st = CsiParam;
1022                    VTAction::None
1023                }
1024                b':' => {
1025                    self.cur_param.push(b':');
1026                    self.st = CsiParam;
1027                    VTAction::None
1028                }
1029                c if is_intermediate(c) => {
1030                    if self.ints.push(c) {
1031                        self.st = CsiInt;
1032                    } else {
1033                        self.st = Ground;
1034                    }
1035                    VTAction::None
1036                }
1037                c if is_final(c) => {
1038                    self.st = Ground;
1039                    self.emit_csi(c)
1040                }
1041                _ => {
1042                    self.st = CsiIgnore;
1043                    VTAction::None
1044                }
1045            }
1046            CsiParam => {
1047                CAN | SUB => {
1048                    self.st = Ground;
1049                    VTAction::None
1050                }
1051                DEL => VTAction::None,
1052                ESC => {
1053                    self.st = Escape;
1054                    VTAction::None
1055                }
1056                // Weird case: if we encounter C0 inside of CSI, emit it while we parse.
1057                c if is_any_c0(c) => VTAction::Event(VTEvent::C0(c)),
1058                c if is_digit(c) => {
1059                    self.cur_param.push(c);
1060                    VTAction::None
1061                }
1062                b';' => {
1063                    self.next_param();
1064                    VTAction::None
1065                }
1066                b':' => {
1067                    self.cur_param.push(b':');
1068                    VTAction::None
1069                }
1070                c if is_intermediate(c) => {
1071                    if self.ints.push(c) {
1072                        self.st = CsiInt;
1073                    } else {
1074                        self.st = Ground;
1075                    }
1076                    VTAction::None
1077                }
1078                c if is_final(c) => {
1079                    self.st = Ground;
1080                    self.emit_csi(c)
1081                }
1082                _ => {
1083                    self.st = CsiIgnore;
1084                    VTAction::None
1085                }
1086            }
1087            CsiInt => {
1088                CAN | SUB => {
1089                    self.st = Ground;
1090                    VTAction::None
1091                }
1092                DEL => VTAction::None,
1093                ESC => {
1094                    self.st = Escape;
1095                    VTAction::None
1096                }
1097                // Weird case: if we encounter C0 inside of CSI, emit it while we parse.
1098                c if is_any_c0(c) => VTAction::Event(VTEvent::C0(c)),
1099                c if is_intermediate(c) => {
1100                    if self.ints.push(c) {
1101                        self.st = CsiInt;
1102                    } else {
1103                        self.st = Ground;
1104                    }
1105                    VTAction::None
1106                }
1107                c if is_final(c) => {
1108                    self.st = Ground;
1109                    self.emit_csi(c)
1110                }
1111                _ => {
1112                    self.st = CsiIgnore;
1113                    VTAction::None
1114                }
1115            }
1116            CsiIgnore => {
1117                CAN | SUB => {
1118                    self.st = Ground;
1119                    VTAction::None
1120                }
1121                DEL => VTAction::None,
1122                ESC => {
1123                    self.st = Escape;
1124                    VTAction::None
1125                }
1126                c if is_final(c) => {
1127                    self.st = Ground;
1128                    VTAction::None
1129                }
1130                _ => VTAction::None,
1131            }
1132            DcsEntry => {
1133                CAN | SUB => {
1134                    self.st = Ground;
1135                    VTAction::None
1136                }
1137                DEL => VTAction::None,
1138                ESC => {
1139                    self.st = Escape;
1140                    VTAction::None
1141                }
1142                c if is_priv(c) => {
1143                    self.priv_prefix = Some(c);
1144                    self.st = DcsParam;
1145                    VTAction::None
1146                }
1147                c if is_digit(c) => {
1148                    self.cur_param.push(c);
1149                    self.st = DcsParam;
1150                    VTAction::None
1151                }
1152                b';' => {
1153                    self.next_param();
1154                    self.st = DcsParam;
1155                    VTAction::None
1156                }
1157                b':' => {
1158                    self.st = DcsIgnore;
1159                    VTAction::None
1160                }
1161                c if is_intermediate(c) => {
1162                    if self.ints.push(c) {
1163                        self.st = DcsInt;
1164                    } else {
1165                        self.st = Ground;
1166                    }
1167                    VTAction::None
1168                }
1169                c if is_final(c) => {
1170                    self.st = DcsPassthrough;
1171                    self.dcs_start(c)
1172                }
1173                _ => {
1174                    self.st = DcsIgnore;
1175                    VTAction::None
1176                }
1177            }
1178            DcsParam => {
1179                CAN | SUB => {
1180                    self.st = Ground;
1181                    VTAction::None
1182                }
1183                DEL => VTAction::None,
1184                ESC => {
1185                    self.st = Escape;
1186                    VTAction::None
1187                }
1188                c if is_digit(c) => {
1189                    self.cur_param.push(c);
1190                    VTAction::None
1191                }
1192                b';' => {
1193                    self.next_param();
1194                    VTAction::None
1195                }
1196                b':' => {
1197                    self.st = DcsIgnore;
1198                    VTAction::None
1199                }
1200                c if is_intermediate(c) => {
1201                    if self.ints.push(c) {
1202                        self.st = DcsInt;
1203                    } else {
1204                        self.st = Ground;
1205                    }
1206                    self.st = DcsInt;
1207                    VTAction::None
1208                }
1209                c if is_final(c) => {
1210                    self.st = DcsPassthrough;
1211                    self.dcs_start(c)
1212                }
1213                _ => {
1214                    self.st = DcsIgnore;
1215                    VTAction::None
1216                }
1217            }
1218            DcsInt => {
1219                CAN | SUB => {
1220                    self.st = Ground;
1221                    VTAction::None
1222                }
1223                DEL => VTAction::None,
1224                ESC => {
1225                    self.st = Escape;
1226                    VTAction::None
1227                }
1228                c if is_intermediate(c) => {
1229                    if self.ints.push(c) {
1230                        self.st = DcsInt;
1231                    } else {
1232                        self.st = Ground;
1233                    }
1234                    VTAction::None
1235                }
1236                c if is_final(c) || is_digit(c) || c == b':' || c == b';' => {
1237                    self.st = DcsPassthrough;
1238                    self.dcs_start(c)
1239                }
1240                _ => {
1241                    self.st = DcsIgnore;
1242                    VTAction::None
1243                }
1244            }
1245            DcsIgnore => {
1246                CAN | SUB => {
1247                    self.st = Ground;
1248                    VTAction::None
1249                }
1250                DEL => VTAction::None,
1251                ESC => {
1252                    self.st = DcsIgnoreEsc;
1253                    VTAction::None
1254                }
1255                _ => VTAction::None,
1256            }
1257            DcsIgnoreEsc => {
1258                CAN | SUB => {
1259                    self.st = Ground;
1260                    VTAction::None
1261                }
1262                ST_FINAL => {
1263                    self.st = Ground;
1264                    VTAction::None
1265                }
1266                DEL => VTAction::None,
1267                ESC => VTAction::None,
1268                _ => {
1269                    self.st = DcsIgnore;
1270                    VTAction::None
1271                }
1272            }
1273            DcsPassthrough => {
1274                CAN | SUB => {
1275                    self.st = Ground;
1276                    VTAction::Cancel(VTEmit::Dcs)
1277                }
1278                DEL => VTAction::None,
1279                ESC => {
1280                    self.st = DcsEsc;
1281                    VTAction::Hold(VTEmit::Dcs)
1282                }
1283                _ => VTAction::Buffer(VTEmit::Dcs),
1284            }
1285            DcsEsc => {
1286                ST_FINAL => {
1287                    self.st = Ground;
1288                    VTAction::End(VTEnd::Dcs)
1289                }
1290                DEL => VTAction::None,
1291                ESC => {
1292                    // If we get ESC ESC, we need to yield the previous ESC as well.
1293                    VTAction::Hold(VTEmit::Dcs)
1294                }
1295                _ => {
1296                    // If we get ESC !ST, we need to yield the previous ESC as well.
1297                    self.st = DcsPassthrough;
1298                    VTAction::Buffer(VTEmit::Dcs)
1299                }
1300            }
1301            OscString => {
1302                CAN | SUB => {
1303                    self.st = Ground;
1304                    VTAction::Cancel(VTEmit::Osc)
1305                }
1306                DEL => VTAction::None,
1307                BEL => {
1308                    self.st = Ground;
1309                    VTAction::End(VTEnd::Osc { used_bel: true })
1310                }
1311                ESC => {
1312                    self.st = OscEsc;
1313                    VTAction::Hold(VTEmit::Osc)
1314                }
1315                p if is_printable(p) => VTAction::Buffer(VTEmit::Osc),
1316                _ => VTAction::None, // ignore other C0
1317            }
1318            OscEsc => {
1319                ST_FINAL => {
1320                    self.st = Ground;
1321                    VTAction::End(VTEnd::Osc { used_bel: false })
1322                } // ST
1323                ESC => VTAction::Hold(VTEmit::Osc),
1324                DEL => VTAction::None,
1325                _ => {
1326                    self.st = OscString;
1327                    VTAction::Buffer(VTEmit::Osc)
1328                }
1329            }
1330            SosPmApcString => {
1331                CAN | SUB => {
1332                    self.st = Ground;
1333                    VTAction::None
1334                }
1335                DEL => VTAction::None,
1336                ESC => {
1337                    self.st = SpaEsc;
1338                    VTAction::None
1339                }
1340                _ => VTAction::None,
1341            }
1342            SpaEsc => {
1343                ST_FINAL => {
1344                    self.st = Ground;
1345                    VTAction::None
1346                }
1347                DEL => VTAction::None,
1348                ESC => {
1349                    /* remain */
1350                    VTAction::None
1351                }
1352                _ => {
1353                    self.st = State::SosPmApcString;
1354                    VTAction::None
1355                }
1356            }
1357        }
1358    }
1359}
1360
1361#[cfg(test)]
1362mod tests {
1363    use crate::event::VTOwnedEvent;
1364
1365    use super::*;
1366    use pretty_assertions::assert_eq;
1367
1368    #[test]
1369    fn test_edge_cases() {
1370        // Test empty input
1371        let mut result = String::new();
1372        VTPushParser::decode_buffer(&[], |e| result.push_str(&format!("{e:?}\n")));
1373        assert_eq!(result.trim(), "");
1374
1375        // Test single ESC
1376        let mut result = String::new();
1377        VTPushParser::decode_buffer(b"\x1b", |e| result.push_str(&format!("{e:?}\n")));
1378        assert_eq!(result.trim(), "");
1379
1380        // Test incomplete CSI
1381        let mut result = String::new();
1382        VTPushParser::decode_buffer(b"\x1b[", |e| result.push_str(&format!("{e:?}\n")));
1383        assert_eq!(result.trim(), "");
1384
1385        // Test incomplete DCS
1386        let mut result = String::new();
1387        VTPushParser::decode_buffer(b"\x1bP", |e| result.push_str(&format!("{e:?}\n")));
1388        assert_eq!(result.trim(), "");
1389
1390        // Test incomplete OSC
1391        let mut result = String::new();
1392        VTPushParser::decode_buffer(b"\x1b]", |e| result.push_str(&format!("{e:?}\n")));
1393        assert_eq!(result.trim(), "OscStart");
1394    }
1395
1396    #[test]
1397    fn test_streaming_behavior() {
1398        // Test streaming DCS data
1399        let mut parser = VTPushParser::new(); // Small flush size
1400        let mut result = String::new();
1401        let mut callback = |vt_input: VTEvent<'_>| {
1402            result.push_str(&format!("{vt_input:?}\n"));
1403        };
1404
1405        // Feed DCS data in chunks
1406        parser.feed_with(b"\x1bP1;2;3 |", &mut callback);
1407        parser.feed_with(b"data", &mut callback);
1408        parser.feed_with(b" more", &mut callback);
1409        parser.feed_with(b"\x1b\\", &mut callback);
1410
1411        assert_eq!(
1412            result.trim(),
1413            "DcsStart('1', '2', '3', ' ', |)\nDcsData('data')\nDcsData(' more')\nDcsEnd('')"
1414        );
1415    }
1416
1417    #[test]
1418    fn test_dcs_payload_passthrough() {
1419        // Test cases for DCS payload passthrough behavior
1420        // Notes: body must be passed through verbatim.
1421        // - ESC '\' (ST) ends the string.
1422        // - ESC ESC stays as two bytes in the body.
1423        // - ESC X (X!='\') is data: both ESC and the following byte are payload.
1424        // - BEL (0x07) is data in DCS (not a terminator).
1425
1426        let dcs_cases: &[(&[u8], &str)] = &[
1427            // 1) Minimal: embedded CSI SGR truecolor (colon params)
1428            (b"\x1bPq\x1b[38:2:12:34:56m\x1b\\", "<ESC>[38:2:12:34:56m"),
1429            // 2) Mixed payload: CSI + literal text
1430            (b"\x1bPq\x1b[48:2:0:0:0m;xyz\x1b\\", "<ESC>[48:2:0:0:0m;xyz"),
1431            // 3) DECRQSS-style reply payload (DCS 1$r ... ST) containing colon-CSI
1432            (
1433                b"\x1bP1$r\x1b[38:2:10:20:30;58:2::200:100:0m\x1b\\",
1434                "<ESC>[38:2:10:20:30;58:2::200:100:0m",
1435            ),
1436            // 4) ESC ESC and ESC X inside body (all data)
1437            (
1438                b"\x1bPqABC\x1b\x1bDEF\x1bXG\x1b\\",
1439                "ABC<ESC><ESC>DEF<ESC>XG",
1440            ),
1441            // 5) BEL in body (data, not a terminator)
1442            (b"\x1bPqDATA\x07MORE\x1b\\", "DATA<BEL>MORE"),
1443            // 6) iTerm2-style header (!|) with embedded CSI 256-color
1444            (b"\x1bP!|\x1b[38:5:208m\x1b\\", "<ESC>[38:5:208m"),
1445            // 7) Private prefix + final '|' (>|) with plain text payload
1446            (b"\x1bP>|Hello world\x1b\\", "Hello world"),
1447            // 8) Multiple embedded CSIs back-to-back
1448            (
1449                b"\x1bPq\x1b[38:2:1:2:3m\x1b[48:5:17m\x1b\\",
1450                "<ESC>[38:2:1:2:3m<ESC>[48:5:17m",
1451            ),
1452            // 9) Long colon param with leading zeros
1453            (
1454                b"\x1bPq\x1b[58:2::000:007:042m\x1b\\",
1455                "<ESC>[58:2::000:007:042m",
1456            ),
1457        ];
1458
1459        for (input, expected_body) in dcs_cases {
1460            let events = collect_owned_events(input);
1461
1462            // Find DcsData events and concatenate their payloads
1463            let mut actual_body = Vec::new();
1464            for event in &events {
1465                match event {
1466                    VTOwnedEvent::DcsData(data) | VTOwnedEvent::DcsEnd(data) => {
1467                        actual_body.extend(data);
1468                    }
1469                    _ => {}
1470                }
1471            }
1472
1473            let actual_body = String::from_utf8(actual_body).unwrap();
1474            let actual_body = actual_body
1475                .replace("\x1b", "<ESC>")
1476                .replace("\x07", "<BEL>");
1477
1478            assert_eq!(
1479                actual_body, *expected_body,
1480                "DCS payload mismatch for input {:?}. Full events: {:#?}",
1481                input, events
1482            );
1483        }
1484    }
1485
1486    fn collect_events(input: &[u8]) -> Vec<String> {
1487        let mut out = Vec::new();
1488        let mut p = VTPushParser::new();
1489        p.feed_with(input, |ev: VTEvent| out.push(format!("{ev:?}")));
1490        out
1491    }
1492
1493    fn collect_owned_events(input: &[u8]) -> Vec<VTOwnedEvent> {
1494        let mut out = Vec::new();
1495        let mut p = VTPushParser::new();
1496        p.feed_with(input, &mut |ev: VTEvent| out.push(ev.to_owned()));
1497        out
1498    }
1499
1500    #[test]
1501    fn dcs_esc_esc_del() {
1502        let ev = collect_events(b"\x1bP1;2;3|\x1b\x1b\x7fdata\x1b\\");
1503        eprintln!("{ev:?}");
1504    }
1505
1506    #[test]
1507    fn osc_bel() {
1508        let mut parser = VTPushParser::new();
1509        let mut output = String::new();
1510        for b in b"\x1b]X\x07" {
1511            parser.feed_with(&[*b], &mut |ev: VTEvent| {
1512                output.push_str(&format!("{ev:?}\n"));
1513            });
1514        }
1515        assert_eq!(output.trim(), "OscStart\nOscData('X')\nOscEnd('')");
1516    }
1517
1518    #[test]
1519    fn dcs_header_with_colon_is_ignored_case1() {
1520        // ESC P 1:2 q ... ST   -> colon inside header params (invalid)
1521        let ev = collect_events(b"\x1bP1:2qHELLO\x1b\\");
1522        // Expect: no DcsStart; the whole thing is ignored until ST
1523        assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1524    }
1525
1526    #[test]
1527    fn dcs_header_with_colon_is_ignored_case2() {
1528        // Colon immediately after ESC P, before any digit
1529        let ev = collect_events(b"\x1bP:1qDATA\x1b\\");
1530        assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1531    }
1532
1533    #[test]
1534    fn dcs_header_with_colon_is_ignored_case3() {
1535        // Mixed: digits;colon;digits then intermediates/final
1536        let ev = collect_events(b"\x1bP12:34!qPAYLOAD\x1b\\");
1537        assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1538    }
1539
1540    #[test]
1541    fn osc_aborted_by_can_mid_body() {
1542        // ESC ] 0;Title <CAN> more <BEL>
1543        let mut s = Vec::new();
1544        s.extend_from_slice(b"\x1b]0;Title");
1545        s.push(CAN);
1546        s.extend_from_slice(b"more\x07");
1547
1548        let ev = collect_debug(&s);
1549
1550        // EXPECT_SPEC_STRICT: no events at all (no Start/Data/End)
1551        // assert!(ev.is_empty(), "{ev:#?}");
1552
1553        // EXPECT_PUSH_PARSER: Start emitted, but NO Data, NO End
1554        assert!(ev.iter().any(|e| e.starts_with("OscStart")), "{ev:#?}");
1555        assert!(!ev.iter().any(|e| e.starts_with("OscData")), "{ev:#?}");
1556        assert!(!ev.iter().any(|e| e.starts_with("OscEnd")), "{ev:#?}");
1557    }
1558
1559    #[test]
1560    fn osc_aborted_by_sub_before_terminator() {
1561        let mut s = Vec::new();
1562        s.extend_from_slice(b"\x1b]52;c;YWJjZA==");
1563        s.push(SUB); // abort
1564        s.extend_from_slice(b"\x1b\\"); // would have been ST, but must be ignored after abort
1565
1566        let ev = collect_debug(&s);
1567        // SPEC-STRICT:
1568        // assert!(ev.is_empty(), "{ev:#?}");
1569        // PUSH-PARSER:
1570        assert!(ev.iter().any(|e| e.starts_with("OscStart")), "{ev:#?}");
1571        assert!(!ev.iter().any(|e| e.starts_with("OscData")), "{ev:#?}");
1572        assert!(!ev.iter().any(|e| e.starts_with("OscEnd")), "{ev:#?}");
1573    }
1574
1575    /// Collect raw VTEvent debug lines for quick assertions.
1576    fn collect_debug(input: &[u8]) -> Vec<String> {
1577        let mut out = Vec::new();
1578        let mut p = VTPushParser::new();
1579        p.feed_with(input, |ev: VTEvent| out.push(format!("{ev:?}")));
1580        out
1581    }
1582
1583    #[test]
1584    fn dcs_aborted_by_can_before_body() {
1585        // ESC P q <CAN> ... ST
1586        let mut s = Vec::new();
1587        s.extend_from_slice(b"\x1bPq"); // header (valid: final 'q')
1588        s.push(CAN);
1589        s.extend_from_slice(b"IGNORED\x1b\\"); // should be raw
1590
1591        let ev = collect_debug(&s);
1592
1593        assert_eq!(ev.len(), 4, "{ev:#?}");
1594        assert_eq!(ev[0], "DcsStart('', q)");
1595        assert_eq!(ev[1], "DcsCancel");
1596        assert_eq!(ev[2], "Raw('IGNORED')");
1597        assert_eq!(ev[3], "Esc('', \\)");
1598    }
1599
1600    #[test]
1601    fn dcs_aborted_by_can_mid_body() {
1602        // ESC P q ABC <CAN> more ST
1603        let mut s = Vec::new();
1604        s.extend_from_slice(b"\x1bPqABC");
1605        s.push(CAN);
1606        s.extend_from_slice(b"MORE\x1b\\"); // ignored after abort
1607
1608        let ev = collect_debug(&s);
1609
1610        assert_eq!(ev.len(), 4, "{ev:#?}");
1611        assert_eq!(ev[0], "DcsStart('', q)");
1612        assert_eq!(ev[1], "DcsCancel");
1613        assert_eq!(ev[2], "Raw('MORE')");
1614        assert_eq!(ev[3], "Esc('', \\)");
1615    }
1616
1617    /* ========= SOS / PM / APC (ESC X, ESC ^, ESC _) ========= */
1618
1619    #[test]
1620    fn spa_aborted_by_can_is_ignored() {
1621        // ESC _ data <CAN> more ST
1622        let mut s = Vec::new();
1623        s.extend_from_slice(b"\x1b_hello");
1624        s.push(CAN);
1625        s.extend_from_slice(b"world\x1b\\");
1626
1627        let ev = collect_debug(&s);
1628        assert_eq!(ev.len(), 2, "{ev:#?}");
1629        assert_eq!(ev[0], "Raw('world')");
1630        assert_eq!(ev[1], "Esc('', \\)");
1631    }
1632
1633    #[test]
1634    fn spa_sub_aborts_too() {
1635        let mut s = Vec::new();
1636        s.extend_from_slice(b"\x1bXhello");
1637        s.push(SUB);
1638        s.extend_from_slice(b"world\x1b\\");
1639        let ev = collect_debug(&s);
1640        assert_eq!(ev.len(), 2, "{ev:#?}");
1641        assert_eq!(ev[0], "Raw('world')");
1642        assert_eq!(ev[1], "Esc('', \\)");
1643    }
1644
1645    /* ========= Sanity: CAN outside strings is a C0 EXECUTE ========= */
1646
1647    #[test]
1648    fn can_in_ground_is_c0() {
1649        let mut s = Vec::new();
1650        s.extend_from_slice(b"abc");
1651        s.push(CAN);
1652        s.extend_from_slice(b"def");
1653        let ev = collect_debug(&s);
1654        // Expect Raw("abc"), C0(0x18), Raw("def")
1655        assert_eq!(ev.len(), 3, "{ev:#?}");
1656        assert_eq!(ev[0], "Raw('abc')");
1657        assert_eq!(ev[1], "C0(18)");
1658        assert_eq!(ev[2], "Raw('def')");
1659    }
1660
1661    /// Brute force sweep of all three-byte sequences to ensure we can recover
1662    /// from all invalid escape sequences (unless CSI/OSC/DCS/SOS/PM/APC).
1663    #[test]
1664    fn three_byte_sequences_capturable() {
1665        let mut bytes = vec![];
1666        for i in 0..=0xFFFFFF_u32 {
1667            bytes.clear();
1668            let test_bytes = i.to_le_bytes();
1669            let test_bytes = &test_bytes[..3];
1670            if test_bytes.iter().any(|b| b == &0) {
1671                continue;
1672            }
1673            if test_bytes[0] == 0x1b && matches!(test_bytes[1], CSI | DCS | OSC | APC | PM | SOS) {
1674                continue;
1675            }
1676            if test_bytes[1] == 0x1b && matches!(test_bytes[2], CSI | DCS | OSC | APC | PM | SOS) {
1677                continue;
1678            }
1679
1680            let mut parser = VTPushParser::<VT_PARSER_INTEREST_ALL>::new_with();
1681            parser.feed_with(test_bytes, |event: VTEvent| {
1682                let mut chunk = [0_u8; 3];
1683                let b = event.encode(&mut chunk).unwrap_or_else(|_| {
1684                    panic!("Failed to encode event {test_bytes:02X?} -> {event:?}")
1685                });
1686                bytes.extend_from_slice(&chunk[..b]);
1687            });
1688            if let Some(event) = parser.idle() {
1689                let mut chunk = [0_u8; 3];
1690                let b = event.encode(&mut chunk).unwrap_or_else(|_| {
1691                    panic!("Failed to encode event {test_bytes:02X?} -> {event:?}")
1692                });
1693                bytes.extend_from_slice(&chunk[..b]);
1694            }
1695
1696            if bytes.len() != 3 || bytes != test_bytes {
1697                eprintln!("Failed to parse:");
1698                parser.feed_with(test_bytes, |event: VTEvent| {
1699                    eprintln!("{event:?}");
1700                });
1701                if let Some(event) = parser.idle() {
1702                    eprintln!("{event:?}");
1703                }
1704                assert_eq!(bytes, test_bytes, "{test_bytes:02X?} -> {bytes:02X?}");
1705            }
1706        }
1707    }
1708}