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        let privp = self.priv_prefix.take();
708        VTAction::Event(VTEvent::Csi(CSI {
709            private: privp,
710            params: ParamBuf {
711                params: &self.params,
712            },
713            intermediates: self.ints,
714            final_byte,
715        }))
716    }
717
718    fn dcs_start(&mut self, final_byte: u8) -> VTAction<'_> {
719        self.finish_params_if_any();
720
721        let privp = self.priv_prefix.take();
722        VTAction::Event(VTEvent::DcsStart(DCS {
723            private: privp,
724            params: ParamBuf {
725                params: &self.params,
726            },
727            intermediates: self.ints,
728            final_byte,
729        }))
730    }
731
732    // =====================
733    // State handlers
734    // =====================
735
736    fn push_with(&mut self, b: u8) -> VTAction<'_> {
737        use State::*;
738
739        macro_rules! state_machine {
740            (def $c:ident $(
741                $state:ident => { $( $p:pat
742                    $(if $( $if_ident:ident $( ($call_ident:ident) )? $( == $literal:literal)? $(||)? )+)?
743                    => $block:expr $(,)? )* }
744            )*) => {
745                #[allow(unused, clippy::redundant_pattern)]
746                match self.st {
747                    $(
748                        $state => {
749                            match b {
750                                $(
751                                    $c @ state_machine!(pattern $p $(if $( ($if_ident ($($call_ident)?) ($($literal)?)) )+ )?) => $block,
752                                )*
753                            }
754                        }
755                    )*
756                }
757            };
758            (pattern $pat:pat if $( ($if_ident:ident ($($call_ident:ident)?) ($($literal:literal)?)) )+ ) => {
759                $(
760                    state_machine!(pattern_if ($if_ident) ($($call_ident)?) ($($literal)?) )
761                )|+
762            };
763            (pattern $pat:pat) => {
764                $pat
765            };
766            (pattern_if ($any:ident) ($call_ident:ident) ()) => {
767                $any!()
768            };
769            (pattern_if ($if_ident:ident) () ($literal:literal)) => {
770                $literal
771            };
772        }
773
774        // This builds a mega state machine that is somewhat easier for LLVM to
775        // optimize than nesting functions. We parse the match-like patterns
776        // into true byte ranges for the best possible performance.
777
778        // Note that because of macro limitations, every binding is declared as
779        // "c".
780        state_machine! {
781            def c
782            Ground => {
783                ESC => {
784                    self.clear_hdr_collectors();
785                    self.st = State::Escape;
786                    VTAction::None
787                }
788                DEL => VTAction::Event(VTEvent::C0(DEL)),
789                c if is_c0(c) => VTAction::Event(VTEvent::C0(c)),
790                _ => VTAction::Buffer(VTEmit::Ground),
791            }
792            Escape => {
793                CAN | SUB => {
794                    self.st = Ground;
795                    if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
796                        VTAction::None
797                    } else {
798                        VTAction::Event(invalid!(b))
799                    }
800                }
801                // NOTE: DEL should be ignored normally, but for better recovery,
802                // we move to ground state here instead.
803                DEL => {
804                    self.st = Ground;
805                    if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
806                        VTAction::None
807                    } else {
808                        VTAction::Event(invalid!(b))
809                    }
810                }
811                c if is_intermediate(c) => {
812                    if self.ints.push(c) {
813                        self.st = EscInt;
814                    } else {
815                        self.st = Ground;
816                    }
817                    VTAction::None
818                }
819                // Not all private sequences are supported -- only ESC ? <final>
820                b'?' => {
821                    self.priv_prefix = Some(b);
822                    self.st = EscInt;
823                    VTAction::None
824                }
825                // The rest of the private sequences become ESC <final>
826                c if is_priv_no_q(c) => {
827                    self.st = Ground;
828                    VTAction::Event(VTEvent::Esc(Esc {
829                        intermediates: VTIntermediate::empty(),
830                        private: None,
831                        final_byte: b,
832                    }))
833                }
834                CSI => {
835                    if INTEREST & VT_PARSER_INTEREST_CSI == 0 {
836                        self.st = CsiIgnore;
837                    } else {
838                        self.st = CsiEntry;
839                    }
840                    VTAction::None
841                }
842                DCS => {
843                    if INTEREST & VT_PARSER_INTEREST_DCS == 0 {
844                        self.st = DcsIgnore;
845                    } else {
846                        self.st = DcsEntry;
847                    }
848                    VTAction::None
849                }
850                OSC => {
851                    self.st = OscString;
852                    VTAction::Event(VTEvent::OscStart)
853                }
854                SS2 => {
855                    self.st = EscSs2;
856                    VTAction::None
857                }
858                SS3 => {
859                    self.st = EscSs3;
860                    VTAction::None
861                }
862                SOS | PM | APC => {
863                    self.st = State::SosPmApcString;
864                    VTAction::None
865                }
866                c if is_final(c) || is_digit(c) => {
867                    self.st = Ground;
868                    VTAction::Event(VTEvent::Esc(Esc {
869                        intermediates: self.ints,
870                        private: self.priv_prefix.take(),
871                        final_byte: c,
872                    }))
873                }
874                ESC => {
875                    // ESC ESC allowed, but we stay in the current state
876                    VTAction::Event(VTEvent::C0(ESC))
877                }
878                _ => {
879                    self.st = Ground;
880                    if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
881                        VTAction::None
882                    } else {
883                        VTAction::Event(invalid!(b))
884                    }
885                }
886            }
887            EscInt => {
888                CAN | SUB => {
889                    self.st = Ground;
890                    if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
891                        VTAction::None
892                    } else {
893                        VTAction::Event(invalid!(self.priv_prefix, self.ints, b))
894                    }
895                }
896                // NOTE: DEL should be ignored normally, but for better recovery,
897                // we move to ground state here instead.
898                DEL => {
899                    self.st = Ground;
900                    if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
901                        VTAction::None
902                    } else {
903                        VTAction::Event(invalid!(self.priv_prefix, self.ints, b))
904                    }
905                }
906                c if is_intermediate(c) => {
907                    if !self.ints.push(c) {
908                        self.st = Ground;
909                        if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
910                            VTAction::None
911                        } else {
912                            VTAction::Event(invalid!(self.priv_prefix, self.ints, b))
913                        }
914                    } else {
915                        VTAction::None
916                    }
917                }
918                c if is_final(c) || is_digit(c) => {
919                    self.st = Ground;
920                    VTAction::Event(VTEvent::Esc(Esc {
921                        intermediates: self.ints,
922                        private: self.priv_prefix.take(),
923                        final_byte: c,
924                    }))
925                }
926                // NOTE: We assume that we want to stay in the escape state
927                // to recover from this state.
928                ESC => {
929                    self.st = Escape;
930                    if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
931                        VTAction::None
932                    } else {
933                        VTAction::Event(invalid!(self.priv_prefix, self.ints))
934                    }
935                }
936                _ => {
937                    self.st = Ground;
938                    if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
939                        VTAction::None
940                    } else {
941                        VTAction::Event(invalid!(self.priv_prefix, self.ints, c))
942                    }
943                }
944            }
945            EscSs2 => {
946                CAN | SUB => {
947                    self.st = Ground;
948                    if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
949                        VTAction::None
950                    } else {
951                        VTAction::Event(invalid!(SS2, b))
952                    }
953                }
954                // NOTE: We assume that we want to stay in the escape state
955                // to recover from this state.
956                ESC => {
957                    self.st = Escape;
958                    if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
959                        VTAction::None
960                    } else {
961                        VTAction::Event(invalid!(SS2))
962                    }
963                }
964                _ => {
965                    self.st = Ground;
966                    VTAction::Event(VTEvent::Ss2(SS2 { char: c }))
967                }
968            }
969            EscSs3 => {
970                CAN | SUB => {
971                    self.st = Ground;
972                    if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
973                        VTAction::None
974                    } else {
975                        VTAction::Event(invalid!(SS3, b))
976                    }
977                }
978                // NOTE: We assume that we want to stay in the escape state
979                // to recover from this state.
980                ESC => {
981                    self.st = Escape;
982                    if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
983                        VTAction::None
984                    } else {
985                        VTAction::Event(invalid!(SS3))
986                    }
987                }
988                _ => {
989                    self.st = Ground;
990                    VTAction::Event(VTEvent::Ss3(SS3 { char: c }))
991                }
992            }
993            CsiEntry => {
994                CAN | SUB => {
995                    self.st = Ground;
996                    VTAction::None
997                }
998                DEL => VTAction::None,
999                ESC => {
1000                    self.st = Escape;
1001                    VTAction::None
1002                }
1003                // Weird case: if we encounter C0 inside of CSI, emit it while we parse.
1004                c if is_any_c0(c) => VTAction::Event(VTEvent::C0(c)),
1005                c if is_priv(c) => {
1006                    self.priv_prefix = Some(c);
1007                    self.st = CsiParam;
1008                    VTAction::None
1009                }
1010                c if is_digit(c) => {
1011                    self.cur_param.push(c);
1012                    self.st = CsiParam;
1013                    VTAction::None
1014                }
1015                b';' => {
1016                    self.next_param();
1017                    self.st = CsiParam;
1018                    VTAction::None
1019                }
1020                b':' => {
1021                    self.cur_param.push(b':');
1022                    self.st = CsiParam;
1023                    VTAction::None
1024                }
1025                c if is_intermediate(c) => {
1026                    if self.ints.push(c) {
1027                        self.st = CsiInt;
1028                    } else {
1029                        self.st = Ground;
1030                    }
1031                    VTAction::None
1032                }
1033                c if is_final(c) => {
1034                    self.st = Ground;
1035                    self.emit_csi(c)
1036                }
1037                _ => {
1038                    self.st = CsiIgnore;
1039                    VTAction::None
1040                }
1041            }
1042            CsiParam => {
1043                CAN | SUB => {
1044                    self.st = Ground;
1045                    VTAction::None
1046                }
1047                DEL => VTAction::None,
1048                ESC => {
1049                    self.st = Escape;
1050                    VTAction::None
1051                }
1052                // Weird case: if we encounter C0 inside of CSI, emit it while we parse.
1053                c if is_any_c0(c) => VTAction::Event(VTEvent::C0(c)),
1054                c if is_digit(c) => {
1055                    self.cur_param.push(c);
1056                    VTAction::None
1057                }
1058                b';' => {
1059                    self.next_param();
1060                    VTAction::None
1061                }
1062                b':' => {
1063                    self.cur_param.push(b':');
1064                    VTAction::None
1065                }
1066                c if is_intermediate(c) => {
1067                    if self.ints.push(c) {
1068                        self.st = CsiInt;
1069                    } else {
1070                        self.st = Ground;
1071                    }
1072                    VTAction::None
1073                }
1074                c if is_final(c) => {
1075                    self.st = Ground;
1076                    self.emit_csi(c)
1077                }
1078                _ => {
1079                    self.st = CsiIgnore;
1080                    VTAction::None
1081                }
1082            }
1083            CsiInt => {
1084                CAN | SUB => {
1085                    self.st = Ground;
1086                    VTAction::None
1087                }
1088                DEL => VTAction::None,
1089                ESC => {
1090                    self.st = Escape;
1091                    VTAction::None
1092                }
1093                // Weird case: if we encounter C0 inside of CSI, emit it while we parse.
1094                c if is_any_c0(c) => VTAction::Event(VTEvent::C0(c)),
1095                c if is_intermediate(c) => {
1096                    if self.ints.push(c) {
1097                        self.st = CsiInt;
1098                    } else {
1099                        self.st = Ground;
1100                    }
1101                    VTAction::None
1102                }
1103                c if is_final(c) => {
1104                    self.st = Ground;
1105                    self.emit_csi(c)
1106                }
1107                _ => {
1108                    self.st = CsiIgnore;
1109                    VTAction::None
1110                }
1111            }
1112            CsiIgnore => {
1113                CAN | SUB => {
1114                    self.st = Ground;
1115                    VTAction::None
1116                }
1117                DEL => VTAction::None,
1118                ESC => {
1119                    self.st = Escape;
1120                    VTAction::None
1121                }
1122                c if is_final(c) => {
1123                    self.st = Ground;
1124                    VTAction::None
1125                }
1126                _ => VTAction::None,
1127            }
1128            DcsEntry => {
1129                CAN | SUB => {
1130                    self.st = Ground;
1131                    VTAction::None
1132                }
1133                DEL => VTAction::None,
1134                ESC => {
1135                    self.st = Escape;
1136                    VTAction::None
1137                }
1138                c if is_priv(c) => {
1139                    self.priv_prefix = Some(c);
1140                    self.st = DcsParam;
1141                    VTAction::None
1142                }
1143                c if is_digit(c) => {
1144                    self.cur_param.push(c);
1145                    self.st = DcsParam;
1146                    VTAction::None
1147                }
1148                b';' => {
1149                    self.next_param();
1150                    self.st = DcsParam;
1151                    VTAction::None
1152                }
1153                b':' => {
1154                    self.st = DcsIgnore;
1155                    VTAction::None
1156                }
1157                c if is_intermediate(c) => {
1158                    if self.ints.push(c) {
1159                        self.st = DcsInt;
1160                    } else {
1161                        self.st = Ground;
1162                    }
1163                    VTAction::None
1164                }
1165                c if is_final(c) => {
1166                    self.st = DcsPassthrough;
1167                    self.dcs_start(c)
1168                }
1169                _ => {
1170                    self.st = DcsIgnore;
1171                    VTAction::None
1172                }
1173            }
1174            DcsParam => {
1175                CAN | SUB => {
1176                    self.st = Ground;
1177                    VTAction::None
1178                }
1179                DEL => VTAction::None,
1180                ESC => {
1181                    self.st = Escape;
1182                    VTAction::None
1183                }
1184                c if is_digit(c) => {
1185                    self.cur_param.push(c);
1186                    VTAction::None
1187                }
1188                b';' => {
1189                    self.next_param();
1190                    VTAction::None
1191                }
1192                b':' => {
1193                    self.st = DcsIgnore;
1194                    VTAction::None
1195                }
1196                c if is_intermediate(c) => {
1197                    if self.ints.push(c) {
1198                        self.st = DcsInt;
1199                    } else {
1200                        self.st = Ground;
1201                    }
1202                    self.st = DcsInt;
1203                    VTAction::None
1204                }
1205                c if is_final(c) => {
1206                    self.st = DcsPassthrough;
1207                    self.dcs_start(c)
1208                }
1209                _ => {
1210                    self.st = DcsIgnore;
1211                    VTAction::None
1212                }
1213            }
1214            DcsInt => {
1215                CAN | SUB => {
1216                    self.st = Ground;
1217                    VTAction::None
1218                }
1219                DEL => VTAction::None,
1220                ESC => {
1221                    self.st = Escape;
1222                    VTAction::None
1223                }
1224                c if is_intermediate(c) => {
1225                    if self.ints.push(c) {
1226                        self.st = DcsInt;
1227                    } else {
1228                        self.st = Ground;
1229                    }
1230                    VTAction::None
1231                }
1232                c if is_final(c) || is_digit(c) || c == b':' || c == b';' => {
1233                    self.st = DcsPassthrough;
1234                    self.dcs_start(c)
1235                }
1236                _ => {
1237                    self.st = DcsIgnore;
1238                    VTAction::None
1239                }
1240            }
1241            DcsIgnore => {
1242                CAN | SUB => {
1243                    self.st = Ground;
1244                    VTAction::None
1245                }
1246                DEL => VTAction::None,
1247                ESC => {
1248                    self.st = DcsIgnoreEsc;
1249                    VTAction::None
1250                }
1251                _ => VTAction::None,
1252            }
1253            DcsIgnoreEsc => {
1254                CAN | SUB => {
1255                    self.st = Ground;
1256                    VTAction::None
1257                }
1258                ST_FINAL => {
1259                    self.st = Ground;
1260                    VTAction::None
1261                }
1262                DEL => VTAction::None,
1263                ESC => VTAction::None,
1264                _ => {
1265                    self.st = DcsIgnore;
1266                    VTAction::None
1267                }
1268            }
1269            DcsPassthrough => {
1270                CAN | SUB => {
1271                    self.st = Ground;
1272                    VTAction::Cancel(VTEmit::Dcs)
1273                }
1274                DEL => VTAction::None,
1275                ESC => {
1276                    self.st = DcsEsc;
1277                    VTAction::Hold(VTEmit::Dcs)
1278                }
1279                _ => VTAction::Buffer(VTEmit::Dcs),
1280            }
1281            DcsEsc => {
1282                ST_FINAL => {
1283                    self.st = Ground;
1284                    VTAction::End(VTEnd::Dcs)
1285                }
1286                DEL => VTAction::None,
1287                ESC => {
1288                    // If we get ESC ESC, we need to yield the previous ESC as well.
1289                    VTAction::Hold(VTEmit::Dcs)
1290                }
1291                _ => {
1292                    // If we get ESC !ST, we need to yield the previous ESC as well.
1293                    self.st = DcsPassthrough;
1294                    VTAction::Buffer(VTEmit::Dcs)
1295                }
1296            }
1297            OscString => {
1298                CAN | SUB => {
1299                    self.st = Ground;
1300                    VTAction::Cancel(VTEmit::Osc)
1301                }
1302                DEL => VTAction::None,
1303                BEL => {
1304                    self.st = Ground;
1305                    VTAction::End(VTEnd::Osc { used_bel: true })
1306                }
1307                ESC => {
1308                    self.st = OscEsc;
1309                    VTAction::Hold(VTEmit::Osc)
1310                }
1311                p if is_printable(p) => VTAction::Buffer(VTEmit::Osc),
1312                _ => VTAction::None, // ignore other C0
1313            }
1314            OscEsc => {
1315                ST_FINAL => {
1316                    self.st = Ground;
1317                    VTAction::End(VTEnd::Osc { used_bel: false })
1318                } // ST
1319                ESC => VTAction::Hold(VTEmit::Osc),
1320                DEL => VTAction::None,
1321                _ => {
1322                    self.st = OscString;
1323                    VTAction::Buffer(VTEmit::Osc)
1324                }
1325            }
1326            SosPmApcString => {
1327                CAN | SUB => {
1328                    self.st = Ground;
1329                    VTAction::None
1330                }
1331                DEL => VTAction::None,
1332                ESC => {
1333                    self.st = SpaEsc;
1334                    VTAction::None
1335                }
1336                _ => VTAction::None,
1337            }
1338            SpaEsc => {
1339                ST_FINAL => {
1340                    self.st = Ground;
1341                    VTAction::None
1342                }
1343                DEL => VTAction::None,
1344                ESC => {
1345                    /* remain */
1346                    VTAction::None
1347                }
1348                _ => {
1349                    self.st = State::SosPmApcString;
1350                    VTAction::None
1351                }
1352            }
1353        }
1354    }
1355}
1356
1357#[cfg(test)]
1358mod tests {
1359    use crate::event::VTOwnedEvent;
1360
1361    use super::*;
1362    use pretty_assertions::assert_eq;
1363
1364    #[test]
1365    fn test_edge_cases() {
1366        // Test empty input
1367        let mut result = String::new();
1368        VTPushParser::decode_buffer(&[], |e| result.push_str(&format!("{e:?}\n")));
1369        assert_eq!(result.trim(), "");
1370
1371        // Test single ESC
1372        let mut result = String::new();
1373        VTPushParser::decode_buffer(b"\x1b", |e| result.push_str(&format!("{e:?}\n")));
1374        assert_eq!(result.trim(), "");
1375
1376        // Test incomplete CSI
1377        let mut result = String::new();
1378        VTPushParser::decode_buffer(b"\x1b[", |e| result.push_str(&format!("{e:?}\n")));
1379        assert_eq!(result.trim(), "");
1380
1381        // Test incomplete DCS
1382        let mut result = String::new();
1383        VTPushParser::decode_buffer(b"\x1bP", |e| result.push_str(&format!("{e:?}\n")));
1384        assert_eq!(result.trim(), "");
1385
1386        // Test incomplete OSC
1387        let mut result = String::new();
1388        VTPushParser::decode_buffer(b"\x1b]", |e| result.push_str(&format!("{e:?}\n")));
1389        assert_eq!(result.trim(), "OscStart");
1390    }
1391
1392    #[test]
1393    fn test_streaming_behavior() {
1394        // Test streaming DCS data
1395        let mut parser = VTPushParser::new(); // Small flush size
1396        let mut result = String::new();
1397        let mut callback = |vt_input: VTEvent<'_>| {
1398            result.push_str(&format!("{vt_input:?}\n"));
1399        };
1400
1401        // Feed DCS data in chunks
1402        parser.feed_with(b"\x1bP1;2;3 |", &mut callback);
1403        parser.feed_with(b"data", &mut callback);
1404        parser.feed_with(b" more", &mut callback);
1405        parser.feed_with(b"\x1b\\", &mut callback);
1406
1407        assert_eq!(
1408            result.trim(),
1409            "DcsStart('1', '2', '3', ' ', |)\nDcsData('data')\nDcsData(' more')\nDcsEnd('')"
1410        );
1411    }
1412
1413    #[test]
1414    fn test_dcs_payload_passthrough() {
1415        // Test cases for DCS payload passthrough behavior
1416        // Notes: body must be passed through verbatim.
1417        // - ESC '\' (ST) ends the string.
1418        // - ESC ESC stays as two bytes in the body.
1419        // - ESC X (X!='\') is data: both ESC and the following byte are payload.
1420        // - BEL (0x07) is data in DCS (not a terminator).
1421
1422        let dcs_cases: &[(&[u8], &str)] = &[
1423            // 1) Minimal: embedded CSI SGR truecolor (colon params)
1424            (b"\x1bPq\x1b[38:2:12:34:56m\x1b\\", "<ESC>[38:2:12:34:56m"),
1425            // 2) Mixed payload: CSI + literal text
1426            (b"\x1bPq\x1b[48:2:0:0:0m;xyz\x1b\\", "<ESC>[48:2:0:0:0m;xyz"),
1427            // 3) DECRQSS-style reply payload (DCS 1$r ... ST) containing colon-CSI
1428            (
1429                b"\x1bP1$r\x1b[38:2:10:20:30;58:2::200:100:0m\x1b\\",
1430                "<ESC>[38:2:10:20:30;58:2::200:100:0m",
1431            ),
1432            // 4) ESC ESC and ESC X inside body (all data)
1433            (
1434                b"\x1bPqABC\x1b\x1bDEF\x1bXG\x1b\\",
1435                "ABC<ESC><ESC>DEF<ESC>XG",
1436            ),
1437            // 5) BEL in body (data, not a terminator)
1438            (b"\x1bPqDATA\x07MORE\x1b\\", "DATA<BEL>MORE"),
1439            // 6) iTerm2-style header (!|) with embedded CSI 256-color
1440            (b"\x1bP!|\x1b[38:5:208m\x1b\\", "<ESC>[38:5:208m"),
1441            // 7) Private prefix + final '|' (>|) with plain text payload
1442            (b"\x1bP>|Hello world\x1b\\", "Hello world"),
1443            // 8) Multiple embedded CSIs back-to-back
1444            (
1445                b"\x1bPq\x1b[38:2:1:2:3m\x1b[48:5:17m\x1b\\",
1446                "<ESC>[38:2:1:2:3m<ESC>[48:5:17m",
1447            ),
1448            // 9) Long colon param with leading zeros
1449            (
1450                b"\x1bPq\x1b[58:2::000:007:042m\x1b\\",
1451                "<ESC>[58:2::000:007:042m",
1452            ),
1453        ];
1454
1455        for (input, expected_body) in dcs_cases {
1456            let events = collect_owned_events(input);
1457
1458            // Find DcsData events and concatenate their payloads
1459            let mut actual_body = Vec::new();
1460            for event in &events {
1461                match event {
1462                    VTOwnedEvent::DcsData(data) | VTOwnedEvent::DcsEnd(data) => {
1463                        actual_body.extend(data);
1464                    }
1465                    _ => {}
1466                }
1467            }
1468
1469            let actual_body = String::from_utf8(actual_body).unwrap();
1470            let actual_body = actual_body
1471                .replace("\x1b", "<ESC>")
1472                .replace("\x07", "<BEL>");
1473
1474            assert_eq!(
1475                actual_body, *expected_body,
1476                "DCS payload mismatch for input {:?}. Full events: {:#?}",
1477                input, events
1478            );
1479        }
1480    }
1481
1482    fn collect_events(input: &[u8]) -> Vec<String> {
1483        let mut out = Vec::new();
1484        let mut p = VTPushParser::new();
1485        p.feed_with(input, |ev: VTEvent| out.push(format!("{ev:?}")));
1486        out
1487    }
1488
1489    fn collect_owned_events(input: &[u8]) -> Vec<VTOwnedEvent> {
1490        let mut out = Vec::new();
1491        let mut p = VTPushParser::new();
1492        p.feed_with(input, &mut |ev: VTEvent| out.push(ev.to_owned()));
1493        out
1494    }
1495
1496    #[test]
1497    fn dcs_esc_esc_del() {
1498        let ev = collect_events(b"\x1bP1;2;3|\x1b\x1b\x7fdata\x1b\\");
1499        eprintln!("{ev:?}");
1500    }
1501
1502    #[test]
1503    fn osc_bel() {
1504        let mut parser = VTPushParser::new();
1505        let mut output = String::new();
1506        for b in b"\x1b]X\x07" {
1507            parser.feed_with(&[*b], &mut |ev: VTEvent| {
1508                output.push_str(&format!("{ev:?}\n"));
1509            });
1510        }
1511        assert_eq!(output.trim(), "OscStart\nOscData('X')\nOscEnd('')");
1512    }
1513
1514    #[test]
1515    fn dcs_header_with_colon_is_ignored_case1() {
1516        // ESC P 1:2 q ... ST   -> colon inside header params (invalid)
1517        let ev = collect_events(b"\x1bP1:2qHELLO\x1b\\");
1518        // Expect: no DcsStart; the whole thing is ignored until ST
1519        assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1520    }
1521
1522    #[test]
1523    fn dcs_header_with_colon_is_ignored_case2() {
1524        // Colon immediately after ESC P, before any digit
1525        let ev = collect_events(b"\x1bP:1qDATA\x1b\\");
1526        assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1527    }
1528
1529    #[test]
1530    fn dcs_header_with_colon_is_ignored_case3() {
1531        // Mixed: digits;colon;digits then intermediates/final
1532        let ev = collect_events(b"\x1bP12:34!qPAYLOAD\x1b\\");
1533        assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1534    }
1535
1536    #[test]
1537    fn osc_aborted_by_can_mid_body() {
1538        // ESC ] 0;Title <CAN> more <BEL>
1539        let mut s = Vec::new();
1540        s.extend_from_slice(b"\x1b]0;Title");
1541        s.push(CAN);
1542        s.extend_from_slice(b"more\x07");
1543
1544        let ev = collect_debug(&s);
1545
1546        // EXPECT_SPEC_STRICT: no events at all (no Start/Data/End)
1547        // assert!(ev.is_empty(), "{ev:#?}");
1548
1549        // EXPECT_PUSH_PARSER: Start emitted, but NO Data, NO End
1550        assert!(ev.iter().any(|e| e.starts_with("OscStart")), "{ev:#?}");
1551        assert!(!ev.iter().any(|e| e.starts_with("OscData")), "{ev:#?}");
1552        assert!(!ev.iter().any(|e| e.starts_with("OscEnd")), "{ev:#?}");
1553    }
1554
1555    #[test]
1556    fn osc_aborted_by_sub_before_terminator() {
1557        let mut s = Vec::new();
1558        s.extend_from_slice(b"\x1b]52;c;YWJjZA==");
1559        s.push(SUB); // abort
1560        s.extend_from_slice(b"\x1b\\"); // would have been ST, but must be ignored after abort
1561
1562        let ev = collect_debug(&s);
1563        // SPEC-STRICT:
1564        // assert!(ev.is_empty(), "{ev:#?}");
1565        // PUSH-PARSER:
1566        assert!(ev.iter().any(|e| e.starts_with("OscStart")), "{ev:#?}");
1567        assert!(!ev.iter().any(|e| e.starts_with("OscData")), "{ev:#?}");
1568        assert!(!ev.iter().any(|e| e.starts_with("OscEnd")), "{ev:#?}");
1569    }
1570
1571    /// Collect raw VTEvent debug lines for quick assertions.
1572    fn collect_debug(input: &[u8]) -> Vec<String> {
1573        let mut out = Vec::new();
1574        let mut p = VTPushParser::new();
1575        p.feed_with(input, |ev: VTEvent| out.push(format!("{ev:?}")));
1576        out
1577    }
1578
1579    #[test]
1580    fn dcs_aborted_by_can_before_body() {
1581        // ESC P q <CAN> ... ST
1582        let mut s = Vec::new();
1583        s.extend_from_slice(b"\x1bPq"); // header (valid: final 'q')
1584        s.push(CAN);
1585        s.extend_from_slice(b"IGNORED\x1b\\"); // should be raw
1586
1587        let ev = collect_debug(&s);
1588
1589        assert_eq!(ev.len(), 4, "{ev:#?}");
1590        assert_eq!(ev[0], "DcsStart('', q)");
1591        assert_eq!(ev[1], "DcsCancel");
1592        assert_eq!(ev[2], "Raw('IGNORED')");
1593        assert_eq!(ev[3], "Esc('', \\)");
1594    }
1595
1596    #[test]
1597    fn dcs_aborted_by_can_mid_body() {
1598        // ESC P q ABC <CAN> more ST
1599        let mut s = Vec::new();
1600        s.extend_from_slice(b"\x1bPqABC");
1601        s.push(CAN);
1602        s.extend_from_slice(b"MORE\x1b\\"); // ignored after abort
1603
1604        let ev = collect_debug(&s);
1605
1606        assert_eq!(ev.len(), 4, "{ev:#?}");
1607        assert_eq!(ev[0], "DcsStart('', q)");
1608        assert_eq!(ev[1], "DcsCancel");
1609        assert_eq!(ev[2], "Raw('MORE')");
1610        assert_eq!(ev[3], "Esc('', \\)");
1611    }
1612
1613    /* ========= SOS / PM / APC (ESC X, ESC ^, ESC _) ========= */
1614
1615    #[test]
1616    fn spa_aborted_by_can_is_ignored() {
1617        // ESC _ data <CAN> more ST
1618        let mut s = Vec::new();
1619        s.extend_from_slice(b"\x1b_hello");
1620        s.push(CAN);
1621        s.extend_from_slice(b"world\x1b\\");
1622
1623        let ev = collect_debug(&s);
1624        assert_eq!(ev.len(), 2, "{ev:#?}");
1625        assert_eq!(ev[0], "Raw('world')");
1626        assert_eq!(ev[1], "Esc('', \\)");
1627    }
1628
1629    #[test]
1630    fn spa_sub_aborts_too() {
1631        let mut s = Vec::new();
1632        s.extend_from_slice(b"\x1bXhello");
1633        s.push(SUB);
1634        s.extend_from_slice(b"world\x1b\\");
1635        let ev = collect_debug(&s);
1636        assert_eq!(ev.len(), 2, "{ev:#?}");
1637        assert_eq!(ev[0], "Raw('world')");
1638        assert_eq!(ev[1], "Esc('', \\)");
1639    }
1640
1641    /* ========= Sanity: CAN outside strings is a C0 EXECUTE ========= */
1642
1643    #[test]
1644    fn can_in_ground_is_c0() {
1645        let mut s = Vec::new();
1646        s.extend_from_slice(b"abc");
1647        s.push(CAN);
1648        s.extend_from_slice(b"def");
1649        let ev = collect_debug(&s);
1650        // Expect Raw("abc"), C0(0x18), Raw("def")
1651        assert_eq!(ev.len(), 3, "{ev:#?}");
1652        assert_eq!(ev[0], "Raw('abc')");
1653        assert_eq!(ev[1], "C0(18)");
1654        assert_eq!(ev[2], "Raw('def')");
1655    }
1656
1657    /// Brute force sweep of all three-byte sequences to ensure we can recover
1658    /// from all invalid escape sequences (unless CSI/OSC/DCS/SOS/PM/APC).
1659    #[test]
1660    fn three_byte_sequences_capturable() {
1661        let mut bytes = vec![];
1662        for i in 0..=0xFFFFFF_u32 {
1663            bytes.clear();
1664            let test_bytes = i.to_le_bytes();
1665            let test_bytes = &test_bytes[..3];
1666            if test_bytes.iter().any(|b| b == &0) {
1667                continue;
1668            }
1669            if test_bytes[0] == 0x1b && matches!(test_bytes[1], CSI | DCS | OSC | APC | PM | SOS) {
1670                continue;
1671            }
1672            if test_bytes[1] == 0x1b && matches!(test_bytes[2], CSI | DCS | OSC | APC | PM | SOS) {
1673                continue;
1674            }
1675
1676            let mut parser = VTPushParser::<VT_PARSER_INTEREST_ALL>::new_with();
1677            parser.feed_with(test_bytes, |event: VTEvent| {
1678                let mut chunk = [0_u8; 3];
1679                let b = event.encode(&mut chunk).unwrap_or_else(|_| {
1680                    panic!("Failed to encode event {test_bytes:02X?} -> {event:?}")
1681                });
1682                bytes.extend_from_slice(&chunk[..b]);
1683            });
1684            if let Some(event) = parser.idle() {
1685                let mut chunk = [0_u8; 3];
1686                let b = event.encode(&mut chunk).unwrap_or_else(|_| {
1687                    panic!("Failed to encode event {test_bytes:02X?} -> {event:?}")
1688                });
1689                bytes.extend_from_slice(&chunk[..b]);
1690            }
1691
1692            if bytes.len() != 3 || bytes != test_bytes {
1693                eprintln!("Failed to parse:");
1694                parser.feed_with(test_bytes, |event: VTEvent| {
1695                    eprintln!("{event:?}");
1696                });
1697                if let Some(event) = parser.idle() {
1698                    eprintln!("{event:?}");
1699                }
1700                assert_eq!(bytes, test_bytes, "{test_bytes:02X?} -> {bytes:02X?}");
1701            }
1702        }
1703    }
1704}