vtty/recording.rs
1// Copyright © Fangdun Tsai <fundon@pindash.io>
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4//! The owned, trait-free event API: `Event` + `Recording`.
5
6use alloc::vec::Vec;
7use smallvec::SmallVec;
8
9use crate::perform::Perform;
10
11/// A parsed escape-sequence event — the dispatch as data instead of a
12/// [`crate::Perform`] callback. Mirrors `vtparse`'s `VTAction`; useful for
13/// differential testing. Params/intermediates use inline `SmallVec`s (≤6
14/// params, ≤2 intermediates allocates nothing); only OSC bodies are heap
15/// `Vec`s.
16#[derive(Debug, Clone, PartialEq, Eq)]
17#[non_exhaustive]
18pub enum Event {
19 /// A printable character (UTF-8 decoded).
20 Print(char),
21 /// A C0/C1 control byte.
22 Execute(u8),
23 /// A CSI sequence: `ESC [ <params> <intermediates> <final>`.
24 CsiDispatch {
25 /// Collected numeric parameters (inline up to 6).
26 params: SmallVec<i32, 6>,
27 /// Intermediate bytes: private markers + `0x20..0x2f`.
28 intermediates: SmallVec<u8, 2>,
29 /// The final byte selecting the command.
30 final_byte: u8,
31 },
32 /// An escape sequence: `ESC <intermediates> <byte>`.
33 EscDispatch {
34 /// Intermediate bytes (`0x20..0x2f`).
35 intermediates: SmallVec<u8, 2>,
36 /// The final byte selecting the command.
37 byte: u8,
38 },
39 /// A DCS header: `ESC P <params> <intermediates> <final>`.
40 DcsHook {
41 /// Collected numeric parameters (inline up to 6).
42 params: SmallVec<i32, 6>,
43 /// Intermediate bytes: private markers + `0x20..0x2f`.
44 intermediates: SmallVec<u8, 2>,
45 /// The final byte selecting the command.
46 final_byte: u8,
47 },
48 /// A DCS payload byte.
49 DcsPut(u8),
50 /// The DCS string ended.
51 DcsUnhook,
52 /// An OSC command ended: `ESC ] <params split on ';'> (BEL | ST)`. The
53 /// outer list is inline up to 8 params; each body is a heap `Vec<u8>`
54 /// (variable-length byte strings have to be owned).
55 OscDispatch {
56 /// Parameters split on `;`, each an owned byte string.
57 params: SmallVec<Vec<u8>, 8>,
58 /// Whether the sequence was terminated by BEL (vs ST).
59 bell_terminated: bool,
60 },
61}
62
63/// Records parser events into a `Vec<Event>` (the enum-based, trait-free API).
64///
65/// The [`crate::Perform`] callback path is the zero-allocation hot path; this
66/// recording allocates one `Event` per dispatch, for tests and tooling.
67#[derive(Debug, Default)]
68pub struct Recording {
69 /// Decoded events in dispatch order.
70 pub events: Vec<Event>,
71}
72
73impl Recording {
74 /// Compile-time default: an empty recording.
75 pub const DEFAULT: Self = Self { events: Vec::new() };
76
77 /// An empty recording (no events).
78 #[inline]
79 pub const fn new() -> Self {
80 Self::DEFAULT
81 }
82}
83
84impl Perform for Recording {
85 fn print(&mut self, c: char) {
86 self.events.push(Event::Print(c));
87 }
88 fn print_bytes(&mut self, s: &[u8]) {
89 for &b in s {
90 self.events.push(Event::Print(b as char));
91 }
92 }
93 fn execute(&mut self, byte: u8) {
94 self.events.push(Event::Execute(byte));
95 }
96 fn csi_dispatch(&mut self, params: &[i32], _sep: u16, intermediates: &[u8], final_byte: u8) {
97 self.events.push(Event::CsiDispatch {
98 params: params.iter().copied().collect(),
99 intermediates: intermediates.iter().copied().collect(),
100 final_byte,
101 });
102 }
103 fn esc_dispatch(&mut self, intermediates: &[u8], byte: u8) {
104 self.events.push(Event::EscDispatch {
105 intermediates: intermediates.iter().copied().collect(),
106 byte,
107 });
108 }
109 fn hook(&mut self, params: &[i32], intermediates: &[u8], final_byte: u8) {
110 self.events.push(Event::DcsHook {
111 params: params.iter().copied().collect(),
112 intermediates: intermediates.iter().copied().collect(),
113 final_byte,
114 });
115 }
116 fn put(&mut self, byte: u8) {
117 self.events.push(Event::DcsPut(byte));
118 }
119 fn unhook(&mut self) {
120 self.events.push(Event::DcsUnhook);
121 }
122 fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool) {
123 self.events.push(Event::OscDispatch {
124 params: params.iter().map(|p| p.to_vec()).collect(),
125 bell_terminated,
126 });
127 }
128}