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