Skip to main content

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