oxur_repl/eval/
output_capture.rs1use std::sync::{Arc, Mutex};
10
11#[derive(Debug, Clone, Default, PartialEq, Eq)]
13pub struct CapturedOutput {
14 pub stdout: String,
16
17 pub stderr: String,
19}
20
21impl CapturedOutput {
22 pub fn new() -> Self {
24 Self::default()
25 }
26
27 pub fn is_empty(&self) -> bool {
29 self.stdout.is_empty() && self.stderr.is_empty()
30 }
31
32 pub fn stdout_option(&self) -> Option<String> {
34 if self.stdout.is_empty() {
35 None
36 } else {
37 Some(self.stdout.clone())
38 }
39 }
40
41 pub fn stderr_option(&self) -> Option<String> {
43 if self.stderr.is_empty() {
44 None
45 } else {
46 Some(self.stderr.clone())
47 }
48 }
49}
50
51#[derive(Debug, Default)]
56pub struct OutputCapturer {
57 stdout: Arc<Mutex<String>>,
59
60 stderr: Arc<Mutex<String>>,
62}
63
64impl OutputCapturer {
65 pub fn new() -> Self {
67 Self {
68 stdout: Arc::new(Mutex::new(String::new())),
69 stderr: Arc::new(Mutex::new(String::new())),
70 }
71 }
72
73 pub fn capture_stdout(&self, output: &str) {
77 if let Ok(mut stdout) = self.stdout.lock() {
78 stdout.push_str(output);
79 }
80 }
81
82 pub fn capture_stderr(&self, output: &str) {
86 if let Ok(mut stderr) = self.stderr.lock() {
87 stderr.push_str(output);
88 }
89 }
90
91 pub fn get_output(&self) -> CapturedOutput {
93 let stdout = self.stdout.lock().map(|s| s.clone()).unwrap_or_default();
94
95 let stderr = self.stderr.lock().map(|s| s.clone()).unwrap_or_default();
96
97 CapturedOutput { stdout, stderr }
98 }
99
100 pub fn clear(&self) {
102 if let Ok(mut stdout) = self.stdout.lock() {
103 stdout.clear();
104 }
105 if let Ok(mut stderr) = self.stderr.lock() {
106 stderr.clear();
107 }
108 }
109
110 pub fn with_capture<F, R>(&self, f: F) -> R
136 where
137 F: FnOnce() -> R,
138 {
139 self.clear();
141
142 f()
144 }
145}
146
147pub fn simulate_execution(code: impl AsRef<str>, capturer: &OutputCapturer) -> String {
152 let code = code.as_ref();
153 if code.contains("eprintln!(\"") {
157 if let Some(start) = code.find("eprintln!(\"") {
158 if let Some(end) = code[start..].find("\")") {
159 let msg = &code[start + 11..start + end];
160 capturer.capture_stderr(&format!("{}\n", msg));
161 }
162 }
163 }
164
165 if let Some(start) = code.find("println!(\"") {
167 let is_println = if start > 0 { !code[..start].ends_with('e') } else { true };
169
170 if is_println {
171 if let Some(end) = code[start..].find("\")") {
172 let msg = &code[start + 10..start + end];
173 capturer.capture_stdout(&format!("{}\n", msg));
174 }
175 }
176 }
177
178 format!("executed: {}", code.chars().take(50).collect::<String>())
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185
186 #[test]
187 fn test_captured_output_new() {
188 let output = CapturedOutput::new();
189 assert!(output.is_empty());
190 assert_eq!(output.stdout, "");
191 assert_eq!(output.stderr, "");
192 }
193
194 #[test]
195 fn test_captured_output_is_empty() {
196 let mut output = CapturedOutput::new();
197 assert!(output.is_empty());
198
199 output.stdout = "test".to_string();
200 assert!(!output.is_empty());
201
202 output.stdout.clear();
203 output.stderr = "error".to_string();
204 assert!(!output.is_empty());
205 }
206
207 #[test]
208 fn test_captured_output_options() {
209 let output = CapturedOutput { stdout: "output".to_string(), stderr: "".to_string() };
210
211 assert_eq!(output.stdout_option(), Some("output".to_string()));
212 assert_eq!(output.stderr_option(), None);
213 }
214
215 #[test]
216 fn test_output_capturer_new() {
217 let capturer = OutputCapturer::new();
218 let output = capturer.get_output();
219 assert!(output.is_empty());
220 }
221
222 #[test]
223 fn test_capture_stdout() {
224 let capturer = OutputCapturer::new();
225
226 capturer.capture_stdout("Hello, ");
227 capturer.capture_stdout("world!\n");
228
229 let output = capturer.get_output();
230 assert_eq!(output.stdout, "Hello, world!\n");
231 assert_eq!(output.stderr, "");
232 }
233
234 #[test]
235 fn test_capture_stderr() {
236 let capturer = OutputCapturer::new();
237
238 capturer.capture_stderr("Error: ");
239 capturer.capture_stderr("something went wrong\n");
240
241 let output = capturer.get_output();
242 assert_eq!(output.stdout, "");
243 assert_eq!(output.stderr, "Error: something went wrong\n");
244 }
245
246 #[test]
247 fn test_capture_both() {
248 let capturer = OutputCapturer::new();
249
250 capturer.capture_stdout("Output line 1\n");
251 capturer.capture_stderr("Warning: test\n");
252 capturer.capture_stdout("Output line 2\n");
253
254 let output = capturer.get_output();
255 assert_eq!(output.stdout, "Output line 1\nOutput line 2\n");
256 assert_eq!(output.stderr, "Warning: test\n");
257 }
258
259 #[test]
260 fn test_clear() {
261 let capturer = OutputCapturer::new();
262
263 capturer.capture_stdout("test");
264 capturer.capture_stderr("error");
265
266 assert!(!capturer.get_output().is_empty());
267
268 capturer.clear();
269
270 let output = capturer.get_output();
271 assert!(output.is_empty());
272 }
273
274 #[test]
275 fn test_with_capture() {
276 let capturer = OutputCapturer::new();
277
278 let result = capturer.with_capture(|| {
279 capturer.capture_stdout("captured\n");
280 42
281 });
282
283 assert_eq!(result, 42);
284 assert_eq!(capturer.get_output().stdout, "captured\n");
285 }
286
287 #[test]
288 fn test_with_capture_clears_previous() {
289 let capturer = OutputCapturer::new();
290
291 capturer.capture_stdout("old output\n");
292
293 capturer.with_capture(|| {
294 capturer.capture_stdout("new output\n");
295 });
296
297 let output = capturer.get_output();
298 assert_eq!(output.stdout, "new output\n");
299 }
300
301 #[test]
302 fn test_simulate_execution_println() {
303 let capturer = OutputCapturer::new();
304
305 let code = r#"println!("Hello from Oxur")"#;
306 simulate_execution(code, &capturer);
307
308 let output = capturer.get_output();
309 assert_eq!(output.stdout, "Hello from Oxur\n");
310 assert_eq!(output.stderr, "");
311 }
312
313 #[test]
314 fn test_simulate_execution_eprintln() {
315 let capturer = OutputCapturer::new();
316
317 let code = r#"eprintln!("Error message")"#;
318 simulate_execution(code, &capturer);
319
320 let output = capturer.get_output();
321 assert_eq!(output.stdout, "");
322 assert_eq!(output.stderr, "Error message\n");
323 }
324
325 #[test]
326 fn test_simulate_execution_both() {
327 let capturer = OutputCapturer::new();
328
329 let code = r#"println!("output"); eprintln!("error")"#;
330 simulate_execution(code, &capturer);
331
332 let output = capturer.get_output();
333 assert_eq!(output.stdout, "output\n");
334 assert_eq!(output.stderr, "error\n");
335 }
336
337 #[test]
338 fn test_simulate_execution_no_output() {
339 let capturer = OutputCapturer::new();
340
341 let code = "let x = 42;";
342 let result = simulate_execution(code, &capturer);
343
344 assert!(result.contains("executed"));
345 let output = capturer.get_output();
346 assert!(output.is_empty());
347 }
348
349 #[test]
350 fn test_thread_safety() {
351 use std::thread;
352
353 let capturer = Arc::new(OutputCapturer::new());
354 let mut handles = vec![];
355
356 for i in 0..10 {
357 let capturer_clone = Arc::clone(&capturer);
358 let handle = thread::spawn(move || {
359 capturer_clone.capture_stdout(&format!("Thread {}\n", i));
360 });
361 handles.push(handle);
362 }
363
364 for handle in handles {
365 handle.join().unwrap();
366 }
367
368 let output = capturer.get_output();
369 assert_eq!(output.stdout.lines().count(), 10);
371 }
372}