1use std::collections::VecDeque;
7use std::sync::{Arc, Mutex};
8use std::time::{Duration, Instant};
9
10const DEFAULT_BATCH_TIMEOUT_MS: u64 = 16; const MAX_BUFFER_SIZE: usize = 64 * 1024; #[derive(Debug, Clone)]
18pub struct StdinBufferOptions {
19 pub batch_timeout_ms: u64,
21 pub max_buffer_size: usize,
23 pub handle_bracketed_paste: bool,
25}
26
27impl Default for StdinBufferOptions {
28 fn default() -> Self {
29 Self {
30 batch_timeout_ms: DEFAULT_BATCH_TIMEOUT_MS,
31 max_buffer_size: MAX_BUFFER_SIZE,
32 handle_bracketed_paste: true,
33 }
34 }
35}
36
37#[derive(Debug, Clone)]
39pub enum StdinBufferEvent {
40 Data(String),
42 PasteStart,
44 PasteEnd(String),
46 Batch(String),
48}
49
50pub struct StdinBuffer {
52 options: StdinBufferOptions,
53 buffer: Mutex<String>,
54 events: Mutex<VecDeque<StdinBufferEvent>>,
55 in_paste: Mutex<bool>,
56 paste_buffer: Mutex<String>,
57 last_input: Mutex<Option<Instant>>,
58 callbacks: Mutex<Vec<Arc<dyn Fn(&StdinBufferEvent) + Send + Sync>>>,
59}
60
61impl StdinBuffer {
62 pub fn new() -> Self {
64 Self::with_options(StdinBufferOptions::default())
65 }
66
67 pub fn with_options(options: StdinBufferOptions) -> Self {
69 Self {
70 options,
71 buffer: Mutex::new(String::new()),
72 events: Mutex::new(VecDeque::new()),
73 in_paste: Mutex::new(false),
74 paste_buffer: Mutex::new(String::new()),
75 last_input: Mutex::new(None),
76 callbacks: Mutex::new(Vec::new()),
77 }
78 }
79
80 pub fn on_event(&self, callback: Arc<dyn Fn(&StdinBufferEvent) + Send + Sync>) {
82 if let Ok(mut callbacks) = self.callbacks.lock() {
83 callbacks.push(callback);
84 }
85 }
86
87 pub fn process(&self, data: &str) {
89 if data.is_empty() {
90 return;
91 }
92
93 if let Ok(mut last) = self.last_input.lock() {
95 *last = Some(Instant::now());
96 }
97
98 if self.options.handle_bracketed_paste {
100 self.process_with_bracketed_paste(data);
101 } else {
102 self.process_simple(data);
103 }
104 }
105
106 fn process_with_bracketed_paste(&self, data: &str) {
108 let mut remaining = data;
109
110 while !remaining.is_empty() {
111 let in_paste = *self.in_paste.lock().unwrap();
112
113 if in_paste {
114 if let Some(end_pos) = remaining.find("\x1b[201~") {
116 let paste_content = &remaining[..end_pos];
118 if let Ok(mut paste_buffer) = self.paste_buffer.lock() {
119 paste_buffer.push_str(paste_content);
120 let complete_paste = paste_buffer.clone();
121 paste_buffer.clear();
122
123 self.emit_event(StdinBufferEvent::PasteEnd(complete_paste));
125 }
126 if let Ok(mut in_paste) = self.in_paste.lock() {
127 *in_paste = false;
128 }
129 remaining = &remaining[end_pos + 6..]; } else {
131 if let Ok(mut paste_buffer) = self.paste_buffer.lock() {
133 paste_buffer.push_str(remaining);
134 }
135 break;
136 }
137 } else {
138 if let Some(start_pos) = remaining.find("\x1b[200~") {
140 if start_pos > 0 {
142 self.process_simple(&remaining[..start_pos]);
143 }
144
145 if let Ok(mut in_paste) = self.in_paste.lock() {
147 *in_paste = true;
148 }
149 self.emit_event(StdinBufferEvent::PasteStart);
150
151 remaining = &remaining[start_pos + 6..]; } else {
153 self.process_simple(remaining);
155 break;
156 }
157 }
158 }
159 }
160
161 fn process_simple(&self, data: &str) {
163 let should_batch = self.should_batch();
165
166 if should_batch {
167 if let Ok(mut buffer) = self.buffer.lock() {
169 if buffer.len() + data.len() <= self.options.max_buffer_size {
170 buffer.push_str(data);
171 } else {
172 let flushed = std::mem::take(&mut *buffer);
174 self.emit_event(StdinBufferEvent::Batch(flushed));
175 buffer.push_str(data);
176 }
177 }
178 } else {
179 self.flush();
181
182 self.emit_event(StdinBufferEvent::Data(data.to_string()));
184 }
185 }
186
187 fn should_batch(&self) -> bool {
189 if let Ok(last) = self.last_input.lock() {
190 if let Some(last_time) = *last {
191 let elapsed = last_time.elapsed();
192 if elapsed < Duration::from_millis(1) {
194 return false;
195 }
196 return elapsed < Duration::from_millis(self.options.batch_timeout_ms);
197 }
198 }
199 false
200 }
201
202 pub fn flush(&self) {
204 if let Ok(mut buffer) = self.buffer.lock() {
205 if !buffer.is_empty() {
206 let data = std::mem::take(&mut *buffer);
207 self.emit_event(StdinBufferEvent::Batch(data));
208 }
209 }
210 }
211
212 pub fn is_in_paste(&self) -> bool {
214 *self.in_paste.lock().unwrap()
215 }
216
217 fn emit_event(&self, event: StdinBufferEvent) {
219 if let Ok(callbacks) = self.callbacks.lock() {
220 for callback in callbacks.iter() {
221 callback(&event);
222 }
223 }
224
225 if let Ok(mut events) = self.events.lock() {
227 events.push_back(event);
228 }
229 }
230
231 pub fn pop_event(&self) -> Option<StdinBufferEvent> {
233 self.events.lock().ok()?.pop_front()
234 }
235
236 pub fn clear_events(&self) {
238 if let Ok(mut events) = self.events.lock() {
239 events.clear();
240 }
241 }
242
243 pub fn reset(&self) {
245 if let Ok(mut buffer) = self.buffer.lock() {
246 buffer.clear();
247 }
248 if let Ok(mut in_paste) = self.in_paste.lock() {
249 *in_paste = false;
250 }
251 if let Ok(mut paste_buffer) = self.paste_buffer.lock() {
252 paste_buffer.clear();
253 }
254 if let Ok(mut last_input) = self.last_input.lock() {
255 *last_input = None;
256 }
257 self.clear_events();
258 }
259}
260
261impl Default for StdinBuffer {
262 fn default() -> Self {
263 Self::new()
264 }
265}
266
267#[cfg(test)]
268mod tests {
269 use super::*;
270
271 #[test]
272 fn test_simple_input() {
273 let buffer = StdinBuffer::new();
274 let received = Arc::new(Mutex::new(String::new()));
275 let received_clone = received.clone();
276
277 buffer.on_event(Arc::new(move |event| {
278 if let StdinBufferEvent::Data(data) = event {
279 *received_clone.lock().unwrap() = data.clone();
280 }
281 }));
282
283 buffer.process("hello");
284 assert_eq!(*received.lock().unwrap(), "hello");
285 }
286
287 #[test]
288 fn test_bracketed_paste() {
289 let buffer = StdinBuffer::new();
290 let received = Arc::new(Mutex::new(String::new()));
291 let received_clone = received.clone();
292
293 buffer.on_event(Arc::new(move |event| {
294 if let StdinBufferEvent::PasteEnd(data) = event {
295 *received_clone.lock().unwrap() = data.clone();
296 }
297 }));
298
299 buffer.process("\x1b[200~pasted content\x1b[201~");
300 assert_eq!(*received.lock().unwrap(), "pasted content");
301 }
302
303 #[test]
304 fn test_reset() {
305 let buffer = StdinBuffer::new();
306 buffer.process("data");
307 buffer.reset();
308
309 assert!(!buffer.is_in_paste());
310 }
311}