1use crate::{VT_PARSER_INTEREST_DEFAULT, VTEvent, VTPushParser};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum VTInputCapture {
10 None,
13 Count(usize),
15 CountUtf8(usize),
17 Terminator(&'static [u8]),
19}
20
21#[cfg_attr(feature = "serde", derive(serde::Serialize))]
22#[derive(Debug)]
23pub enum VTCaptureEvent<'a> {
24 VTEvent(VTEvent<'a>),
25 Capture(&'a [u8]),
26 CaptureEnd,
27}
28
29enum VTCaptureInternal {
30 None,
31 Count(usize),
32 CountUtf8(usize),
33 Terminator(&'static [u8], usize),
34}
35
36impl VTCaptureInternal {
37 fn feed<'a>(&mut self, input: &mut &'a [u8]) -> Option<&'a [u8]> {
38 match self {
39 VTCaptureInternal::None => None,
40 VTCaptureInternal::Count(count) => {
41 if input.len() >= *count {
42 let (capture, rest) = input.split_at(*count);
43 *input = rest;
44 *self = VTCaptureInternal::None;
45 Some(capture)
46 } else {
47 None
48 }
49 }
50 VTCaptureInternal::CountUtf8(count) => {
51 let mut chars_found = 0;
53 let mut bytes_consumed = 0;
54
55 for (i, &byte) in input.iter().enumerate() {
56 if byte & 0xC0 != 0x80 {
58 chars_found += 1;
60 if chars_found == *count {
61 let mut j = i + 1;
64 while j < input.len() && input[j] & 0xC0 == 0x80 {
65 j += 1;
66 }
67 bytes_consumed = j;
68 break;
69 }
70 }
71 }
72
73 if chars_found == *count {
74 let (capture, rest) = input.split_at(bytes_consumed);
75 *input = rest;
76 *self = VTCaptureInternal::None;
77 Some(capture)
78 } else {
79 None
80 }
81 }
82 VTCaptureInternal::Terminator(terminator, _found) => {
83 if let Some(pos) = input
86 .windows(terminator.len())
87 .position(|window| window == *terminator)
88 {
89 let (capture, rest) = input.split_at(pos);
90 *input = &rest[terminator.len()..]; *self = VTCaptureInternal::None;
92 Some(capture)
93 } else {
94 if !input.is_empty() {
96 let (capture, rest) = input.split_at(input.len());
97 *input = rest;
98 Some(capture)
99 } else {
100 None
101 }
102 }
103 }
104 }
105 }
106}
107
108pub struct VTCapturePushParser<const INTEREST: u8 = VT_PARSER_INTEREST_DEFAULT> {
114 parser: VTPushParser<INTEREST>,
115 capture: VTCaptureInternal,
116}
117
118impl Default for VTCapturePushParser {
119 fn default() -> Self {
120 Self::new()
121 }
122}
123
124impl VTCapturePushParser {
125 pub const fn new() -> VTCapturePushParser {
126 VTCapturePushParser::new_with_interest::<VT_PARSER_INTEREST_DEFAULT>()
127 }
128
129 pub const fn new_with_interest<const INTEREST: u8>() -> VTCapturePushParser<INTEREST> {
130 VTCapturePushParser::new_with()
131 }
132}
133
134impl<const INTEREST: u8> VTCapturePushParser<INTEREST> {
135 const fn new_with() -> Self {
136 Self {
137 parser: VTPushParser::new_with(),
138 capture: VTCaptureInternal::None,
139 }
140 }
141
142 pub fn is_ground(&self) -> bool {
143 self.parser.is_ground()
144 }
145
146 pub fn idle(&mut self) -> Option<VTCaptureEvent<'static>> {
147 self.parser.idle().map(VTCaptureEvent::VTEvent)
148 }
149
150 pub fn feed_with<'this, 'input, F: for<'any> FnMut(VTCaptureEvent<'any>) -> VTInputCapture>(
151 &'this mut self,
152 mut input: &'input [u8],
153 cb: &mut F,
154 ) {
155 while !input.is_empty() {
156 match &mut self.capture {
157 VTCaptureInternal::None => {
158 let count = self.parser.feed_with_abortable(input, &mut |event| {
160 let capture_mode = cb(VTCaptureEvent::VTEvent(event));
161 match capture_mode {
162 VTInputCapture::None => {
163 }
165 VTInputCapture::Count(count) => {
166 self.capture = VTCaptureInternal::Count(count);
167 }
168 VTInputCapture::CountUtf8(count) => {
169 self.capture = VTCaptureInternal::CountUtf8(count);
170 }
171 VTInputCapture::Terminator(terminator) => {
172 self.capture = VTCaptureInternal::Terminator(terminator, 0);
173 }
174 }
175 false });
177
178 input = &input[count..];
179 }
180 capture => {
181 if let Some(captured_data) = capture.feed(&mut input) {
183 cb(VTCaptureEvent::Capture(captured_data));
184 }
185
186 if matches!(self.capture, VTCaptureInternal::None) {
188 cb(VTCaptureEvent::CaptureEnd);
189 }
190 }
191 }
192 }
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199
200 #[test]
201 fn test_capture_paste() {
202 let mut output = String::new();
203 let mut parser = VTCapturePushParser::new();
204 parser.feed_with(b"raw\x1b[200~paste\x1b[201~raw", &mut |event| {
205 output.push_str(&format!("{event:?}\n"));
206 match event {
207 VTCaptureEvent::VTEvent(VTEvent::Csi(csi)) => {
208 if csi.params.try_parse::<usize>(0).unwrap_or(0) == 200 {
209 VTInputCapture::Terminator(b"\x1b[201~")
210 } else {
211 VTInputCapture::None
212 }
213 }
214 _ => VTInputCapture::None,
215 }
216 });
217 assert_eq!(
218 output.trim(),
219 r#"
220VTEvent(Raw('raw'))
221VTEvent(Csi(, '200', '', '~'))
222Capture([112, 97, 115, 116, 101])
223CaptureEnd
224VTEvent(Raw('raw'))
225"#
226 .trim()
227 );
228 }
229
230 #[test]
231 fn test_capture_count() {
232 let mut output = String::new();
233 let mut parser = VTCapturePushParser::new();
234 parser.feed_with(b"raw\x1b[Xpaste\x1b[Yraw", &mut |event| {
235 output.push_str(&format!("{event:?}\n"));
236 match event {
237 VTCaptureEvent::VTEvent(VTEvent::Csi(csi)) => {
238 if csi.final_byte == b'X' {
239 VTInputCapture::Count(5)
240 } else {
241 VTInputCapture::None
242 }
243 }
244 _ => VTInputCapture::None,
245 }
246 });
247 assert_eq!(
248 output.trim(),
249 r#"
250VTEvent(Raw('raw'))
251VTEvent(Csi(, '', 'X'))
252Capture([112, 97, 115, 116, 101])
253CaptureEnd
254VTEvent(Csi(, '', 'Y'))
255VTEvent(Raw('raw'))
256"#
257 .trim()
258 );
259 }
260
261 #[test]
262 fn test_capture_count_utf8_but_ascii() {
263 let mut output = String::new();
264 let mut parser = VTCapturePushParser::new();
265 parser.feed_with(b"raw\x1b[Xpaste\x1b[Yraw", &mut |event| {
266 output.push_str(&format!("{event:?}\n"));
267 match event {
268 VTCaptureEvent::VTEvent(VTEvent::Csi(csi)) => {
269 if csi.final_byte == b'X' {
270 VTInputCapture::CountUtf8(5)
271 } else {
272 VTInputCapture::None
273 }
274 }
275 _ => VTInputCapture::None,
276 }
277 });
278 assert_eq!(
279 output.trim(),
280 r#"
281VTEvent(Raw('raw'))
282VTEvent(Csi(, '', 'X'))
283Capture([112, 97, 115, 116, 101])
284CaptureEnd
285VTEvent(Csi(, '', 'Y'))
286VTEvent(Raw('raw'))
287"#
288 .trim()
289 );
290 }
291
292 #[test]
293 fn test_capture_count_utf8() {
294 let mut output = String::new();
295 let mut parser = VTCapturePushParser::new();
296 let input = "raw\u{001b}[X🤖🦕✅😀🕓\u{001b}[Yraw".as_bytes();
297 parser.feed_with(input, &mut |event| {
298 output.push_str(&format!("{event:?}\n"));
299 match event {
300 VTCaptureEvent::VTEvent(VTEvent::Csi(csi)) => {
301 if csi.final_byte == b'X' {
302 VTInputCapture::CountUtf8(5)
303 } else {
304 VTInputCapture::None
305 }
306 }
307 _ => VTInputCapture::None,
308 }
309 });
310 assert_eq!(output.trim(), r#"
311VTEvent(Raw('raw'))
312VTEvent(Csi(, '', 'X'))
313Capture([240, 159, 164, 150, 240, 159, 166, 149, 226, 156, 133, 240, 159, 152, 128, 240, 159, 149, 147])
314CaptureEnd
315VTEvent(Csi(, '', 'Y'))
316VTEvent(Raw('raw'))
317"#.trim());
318 }
319
320 #[test]
321 fn test_capture_terminator_partial_match() {
322 let mut output = String::new();
323 let mut parser = VTCapturePushParser::new();
324
325 parser.feed_with(b"start\x1b[200~part\x1b[201ial\x1b[201~end", &mut |event| {
326 output.push_str(&format!("{event:?}\n"));
327 match event {
328 VTCaptureEvent::VTEvent(VTEvent::Csi(csi)) => {
329 if csi.final_byte == b'~'
330 && csi.params.try_parse::<usize>(0).unwrap_or(0) == 200
331 {
332 VTInputCapture::Terminator(b"\x1b[201~")
333 } else {
334 VTInputCapture::None
335 }
336 }
337 _ => VTInputCapture::None,
338 }
339 });
340
341 assert_eq!(
342 output.trim(),
343 r#"VTEvent(Raw('start'))
344VTEvent(Csi(, '200', '', '~'))
345Capture([112, 97, 114, 116, 27, 91, 50, 48, 49, 105, 97, 108])
346CaptureEnd
347VTEvent(Raw('end'))"#
348 );
349 }
350}