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 VTCapturePushParser {
119 pub const fn new() -> VTCapturePushParser {
120 VTCapturePushParser::new_with_interest::<VT_PARSER_INTEREST_DEFAULT>()
121 }
122
123 pub const fn new_with_interest<const INTEREST: u8>() -> VTCapturePushParser<INTEREST> {
124 VTCapturePushParser::new_with()
125 }
126}
127
128impl<const INTEREST: u8> VTCapturePushParser<INTEREST> {
129 const fn new_with() -> Self {
130 Self {
131 parser: VTPushParser::new_with(),
132 capture: VTCaptureInternal::None,
133 }
134 }
135
136 pub fn is_ground(&self) -> bool {
137 self.parser.is_ground()
138 }
139
140 pub fn idle(&mut self) -> Option<VTCaptureEvent<'static>> {
141 self.parser
142 .idle()
143 .map(|event| VTCaptureEvent::VTEvent(event))
144 }
145
146 pub fn feed_with<'this, 'input, F: for<'any> FnMut(VTCaptureEvent<'any>) -> VTInputCapture>(
147 &'this mut self,
148 mut input: &'input [u8],
149 cb: &mut F,
150 ) {
151 while !input.is_empty() {
152 match &mut self.capture {
153 VTCaptureInternal::None => {
154 let count = self.parser.feed_with_abortable(input, &mut |event| {
156 let capture_mode = cb(VTCaptureEvent::VTEvent(event));
157 match capture_mode {
158 VTInputCapture::None => {
159 }
161 VTInputCapture::Count(count) => {
162 self.capture = VTCaptureInternal::Count(count);
163 }
164 VTInputCapture::CountUtf8(count) => {
165 self.capture = VTCaptureInternal::CountUtf8(count);
166 }
167 VTInputCapture::Terminator(terminator) => {
168 self.capture = VTCaptureInternal::Terminator(terminator, 0);
169 }
170 }
171 false });
173
174 input = &input[count..];
175 }
176 capture => {
177 if let Some(captured_data) = capture.feed(&mut input) {
179 cb(VTCaptureEvent::Capture(captured_data));
180 }
181
182 if matches!(self.capture, VTCaptureInternal::None) {
184 cb(VTCaptureEvent::CaptureEnd);
185 }
186 }
187 }
188 }
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195
196 #[test]
197 fn test_capture_paste() {
198 let mut output = String::new();
199 let mut parser = VTCapturePushParser::new();
200 parser.feed_with(b"raw\x1b[200~paste\x1b[201~raw", &mut |event| {
201 output.push_str(&format!("{:?}\n", event));
202 match event {
203 VTCaptureEvent::VTEvent(VTEvent::Csi(csi)) => {
204 if csi.params.try_parse::<usize>(0).unwrap_or(0) == 200 {
205 VTInputCapture::Terminator(b"\x1b[201~")
206 } else {
207 VTInputCapture::None
208 }
209 }
210 _ => VTInputCapture::None,
211 }
212 });
213 assert_eq!(
214 output.trim(),
215 r#"
216VTEvent(Raw('raw'))
217VTEvent(Csi(, '200', '', '~'))
218Capture([112, 97, 115, 116, 101])
219CaptureEnd
220VTEvent(Raw('raw'))
221"#
222 .trim()
223 );
224 }
225
226 #[test]
227 fn test_capture_count() {
228 let mut output = String::new();
229 let mut parser = VTCapturePushParser::new();
230 parser.feed_with(b"raw\x1b[Xpaste\x1b[Yraw", &mut |event| {
231 output.push_str(&format!("{:?}\n", event));
232 match event {
233 VTCaptureEvent::VTEvent(VTEvent::Csi(csi)) => {
234 if csi.final_byte == b'X' {
235 VTInputCapture::Count(5)
236 } else {
237 VTInputCapture::None
238 }
239 }
240 _ => VTInputCapture::None,
241 }
242 });
243 assert_eq!(
244 output.trim(),
245 r#"
246VTEvent(Raw('raw'))
247VTEvent(Csi(, '', 'X'))
248Capture([112, 97, 115, 116, 101])
249CaptureEnd
250VTEvent(Csi(, '', 'Y'))
251VTEvent(Raw('raw'))
252"#
253 .trim()
254 );
255 }
256
257 #[test]
258 fn test_capture_count_utf8_but_ascii() {
259 let mut output = String::new();
260 let mut parser = VTCapturePushParser::new();
261 parser.feed_with(b"raw\x1b[Xpaste\x1b[Yraw", &mut |event| {
262 output.push_str(&format!("{:?}\n", event));
263 match event {
264 VTCaptureEvent::VTEvent(VTEvent::Csi(csi)) => {
265 if csi.final_byte == b'X' {
266 VTInputCapture::CountUtf8(5)
267 } else {
268 VTInputCapture::None
269 }
270 }
271 _ => VTInputCapture::None,
272 }
273 });
274 assert_eq!(
275 output.trim(),
276 r#"
277VTEvent(Raw('raw'))
278VTEvent(Csi(, '', 'X'))
279Capture([112, 97, 115, 116, 101])
280CaptureEnd
281VTEvent(Csi(, '', 'Y'))
282VTEvent(Raw('raw'))
283"#
284 .trim()
285 );
286 }
287
288 #[test]
289 fn test_capture_count_utf8() {
290 let mut output = String::new();
291 let mut parser = VTCapturePushParser::new();
292 let input = "raw\u{001b}[X🤖🦕✅😀🕓\u{001b}[Yraw".as_bytes();
293 parser.feed_with(input, &mut |event| {
294 output.push_str(&format!("{:?}\n", event));
295 match event {
296 VTCaptureEvent::VTEvent(VTEvent::Csi(csi)) => {
297 if csi.final_byte == b'X' {
298 VTInputCapture::CountUtf8(5)
299 } else {
300 VTInputCapture::None
301 }
302 }
303 _ => VTInputCapture::None,
304 }
305 });
306 assert_eq!(output.trim(), r#"
307VTEvent(Raw('raw'))
308VTEvent(Csi(, '', 'X'))
309Capture([240, 159, 164, 150, 240, 159, 166, 149, 226, 156, 133, 240, 159, 152, 128, 240, 159, 149, 147])
310CaptureEnd
311VTEvent(Csi(, '', 'Y'))
312VTEvent(Raw('raw'))
313"#.trim());
314 }
315
316 #[test]
317 fn test_capture_terminator_partial_match() {
318 let mut output = String::new();
319 let mut parser = VTCapturePushParser::new();
320
321 parser.feed_with(b"start\x1b[200~part\x1b[201ial\x1b[201~end", &mut |event| {
322 output.push_str(&format!("{:?}\n", event));
323 match event {
324 VTCaptureEvent::VTEvent(VTEvent::Csi(csi)) => {
325 if csi.final_byte == b'~'
326 && csi.params.try_parse::<usize>(0).unwrap_or(0) == 200
327 {
328 VTInputCapture::Terminator(b"\x1b[201~")
329 } else {
330 VTInputCapture::None
331 }
332 }
333 _ => VTInputCapture::None,
334 }
335 });
336
337 assert_eq!(
338 output.trim(),
339 r#"VTEvent(Raw('start'))
340VTEvent(Csi(, '200', '', '~'))
341Capture([112, 97, 114, 116, 27, 91, 50, 48, 49, 105, 97, 108])
342CaptureEnd
343VTEvent(Raw('end'))"#
344 );
345 }
346}