1#![deny(missing_docs)]
2#![cfg_attr(test, deny(warnings))]
3
4use std::fs::File;
83use std::io::{self, IoSlice, IoSliceMut, Read, Write};
84use std::mem::ManuallyDrop;
85use std::path::Path;
86use std::sync::atomic::{AtomicUsize, Ordering};
87
88#[cfg(not(any(unix, windows)))]
89compile_error!("stdio-override only supports Unix and Windows");
90
91#[cfg_attr(unix, path = "unix.rs")]
92#[cfg_attr(windows, path = "windows.rs")]
93mod imp;
94
95static OVERRIDDEN_STDIN_COUNT: AtomicUsize = AtomicUsize::new(0);
96
97#[derive(Debug)]
102pub struct StdinOverride {
103 original: ManuallyDrop<File>,
104 index: usize,
105}
106impl StdinOverride {
107 fn from_raw_inner(raw: imp::Raw, owned: bool) -> io::Result<Self> {
108 Ok(Self {
109 original: ManuallyDrop::new(imp::override_stdin(raw, owned)?),
110 index: OVERRIDDEN_STDIN_COUNT.fetch_add(1, Ordering::SeqCst),
111 })
112 }
113 pub fn from_raw(raw: imp::Raw) -> io::Result<Self> {
118 Self::from_raw_inner(raw, false)
119 }
120 pub fn from_raw_owned(raw: imp::Raw) -> io::Result<Self> {
124 Self::from_raw_inner(raw, true)
125 }
126 pub fn from_io_ref<T: imp::AsRaw>(io: &T) -> io::Result<Self> {
130 Self::from_raw(imp::as_raw(io))
131 }
132 pub fn from_io<T: imp::IntoRaw>(io: T) -> io::Result<Self> {
134 Self::from_raw_owned(imp::into_raw(io))
135 }
136 pub fn from_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
140 Self::from_io(File::open(path)?)
141 }
142 pub fn reset(self) -> io::Result<()> {
146 self.reset_inner()?;
147 std::mem::forget(self);
148 Ok(())
149 }
150 fn reset_inner(&self) -> io::Result<()> {
151 if OVERRIDDEN_STDIN_COUNT.swap(self.index, Ordering::SeqCst) <= self.index {
152 panic!("Stdin override reset out of order!");
153 }
154 imp::reset_stdin(imp::as_raw(&*self.original))
155 }
156}
157impl Read for StdinOverride {
158 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
159 self.original.read(buf)
160 }
161 fn read_vectored(&mut self, bufs: &mut [IoSliceMut]) -> io::Result<usize> {
162 self.original.read_vectored(bufs)
163 }
164}
165impl Read for &'_ StdinOverride {
166 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
167 (&*self.original).read(buf)
168 }
169 fn read_vectored(&mut self, bufs: &mut [IoSliceMut]) -> io::Result<usize> {
170 (&*self.original).read_vectored(bufs)
171 }
172}
173impl Drop for StdinOverride {
174 fn drop(&mut self) {
175 let _ = self.reset_inner();
176 }
177}
178
179static OVERRIDDEN_STDOUT_COUNT: AtomicUsize = AtomicUsize::new(0);
180
181#[derive(Debug)]
186pub struct StdoutOverride {
187 original: ManuallyDrop<File>,
188 index: usize,
189}
190impl StdoutOverride {
191 fn from_raw_inner(raw: imp::Raw, owned: bool) -> io::Result<Self> {
192 Ok(Self {
193 original: ManuallyDrop::new(imp::override_stdout(raw, owned)?),
194 index: OVERRIDDEN_STDOUT_COUNT.fetch_add(1, Ordering::SeqCst),
195 })
196 }
197 pub fn from_raw(raw: imp::Raw) -> io::Result<Self> {
202 Self::from_raw_inner(raw, false)
203 }
204 pub fn from_raw_owned(raw: imp::Raw) -> io::Result<Self> {
208 Self::from_raw_inner(raw, true)
209 }
210 pub fn from_io_ref<T: imp::AsRaw>(io: &T) -> io::Result<Self> {
214 Self::from_raw(imp::as_raw(io))
215 }
216 pub fn from_io<T: imp::IntoRaw>(io: T) -> io::Result<Self> {
218 Self::from_raw_owned(imp::into_raw(io))
219 }
220 pub fn from_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
224 Self::from_io(File::create(path)?)
225 }
226 pub fn reset(self) -> io::Result<()> {
230 self.reset_inner()?;
231 std::mem::forget(self);
232 Ok(())
233 }
234 fn reset_inner(&self) -> io::Result<()> {
235 if OVERRIDDEN_STDOUT_COUNT.swap(self.index, Ordering::SeqCst) <= self.index {
236 panic!("Stdout override reset out of order!");
237 }
238 imp::reset_stdout(imp::as_raw(&*self.original))
239 }
240}
241impl Write for StdoutOverride {
242 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
243 self.original.write(buf)
244 }
245 fn write_vectored(&mut self, bufs: &[IoSlice]) -> io::Result<usize> {
246 self.original.write_vectored(bufs)
247 }
248 fn flush(&mut self) -> io::Result<()> {
249 self.original.flush()
250 }
251}
252impl Write for &'_ StdoutOverride {
253 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
254 (&*self.original).write(buf)
255 }
256 fn write_vectored(&mut self, bufs: &[IoSlice]) -> io::Result<usize> {
257 (&*self.original).write_vectored(bufs)
258 }
259 fn flush(&mut self) -> io::Result<()> {
260 (&*self.original).flush()
261 }
262}
263impl Drop for StdoutOverride {
264 fn drop(&mut self) {
265 let _ = self.reset_inner();
266 }
267}
268
269static OVERRIDDEN_STDERR_COUNT: AtomicUsize = AtomicUsize::new(0);
270
271#[derive(Debug)]
276pub struct StderrOverride {
277 original: ManuallyDrop<File>,
278 index: usize,
279}
280impl StderrOverride {
281 fn from_raw_inner(raw: imp::Raw, owned: bool) -> io::Result<Self> {
282 Ok(Self {
283 original: ManuallyDrop::new(imp::override_stderr(raw, owned)?),
284 index: OVERRIDDEN_STDERR_COUNT.fetch_add(1, Ordering::SeqCst),
285 })
286 }
287 pub fn from_raw(raw: imp::Raw) -> io::Result<Self> {
292 Self::from_raw_inner(raw, false)
293 }
294 pub fn from_raw_owned(raw: imp::Raw) -> io::Result<Self> {
298 Self::from_raw_inner(raw, true)
299 }
300 pub fn from_io_ref<T: imp::AsRaw>(io: &T) -> io::Result<Self> {
304 Self::from_raw(imp::as_raw(io))
305 }
306 pub fn from_io<T: imp::IntoRaw>(io: T) -> io::Result<Self> {
308 Self::from_raw_owned(imp::into_raw(io))
309 }
310 pub fn from_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
314 Self::from_io(File::create(path)?)
315 }
316 pub fn reset(self) -> io::Result<()> {
320 self.reset_inner()?;
321 std::mem::forget(self);
322 Ok(())
323 }
324 fn reset_inner(&self) -> io::Result<()> {
325 if OVERRIDDEN_STDERR_COUNT.swap(self.index, Ordering::SeqCst) <= self.index {
326 panic!("Stderr override reset out of order!");
327 }
328 imp::reset_stderr(imp::as_raw(&*self.original))
329 }
330}
331impl Write for StderrOverride {
332 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
333 self.original.write(buf)
334 }
335 fn write_vectored(&mut self, bufs: &[IoSlice]) -> io::Result<usize> {
336 self.original.write_vectored(bufs)
337 }
338 fn flush(&mut self) -> io::Result<()> {
339 self.original.flush()
340 }
341}
342impl Write for &'_ StderrOverride {
343 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
344 (&*self.original).write(buf)
345 }
346 fn write_vectored(&mut self, bufs: &[IoSlice]) -> io::Result<usize> {
347 (&*self.original).write_vectored(bufs)
348 }
349 fn flush(&mut self) -> io::Result<()> {
350 (&*self.original).flush()
351 }
352}
353impl Drop for StderrOverride {
354 fn drop(&mut self) {
355 let _ = self.reset_inner();
356 }
357}
358
359#[cfg(feature = "test-readme")]
360doc_comment::doctest!("../README.md");
361
362#[cfg(test)]
363mod test {
364 use crate::*;
365 use std::io::{stderr, stdin, stdout, Read, Result, Write};
366
367 use os_pipe::pipe;
368
369 #[test]
370 fn test_stdout() -> Result<()> {
371 let (mut rx, tx) = pipe()?;
372 let data = "12345";
373
374 let guard = StdoutOverride::from_io_ref(&tx)?;
375 print!("{}", data);
376 stdout().flush()?;
377 writeln!(&guard, "Outside! (1/2)")?;
378 drop(guard);
379
380 drop(tx);
381
382 let mut contents = String::new();
383 rx.read_to_string(&mut contents)?;
384 assert_eq!(data, contents);
385 println!("Outside! (2/2)");
386
387 Ok(())
388 }
389
390 #[test]
391 fn test_stderr() -> Result<()> {
392 let (mut rx, tx) = pipe()?;
393 let data = "123456";
394
395 let guard = StderrOverride::from_io_ref(&tx)?;
396 eprint!("{}", data);
397 stderr().flush()?;
398 writeln!(&guard, "Outside! (1/2)")?;
399 drop(guard);
400
401 drop(tx);
402
403 let mut contents = String::new();
404 rx.read_to_string(&mut contents)?;
405 assert_eq!(data, contents);
406 eprintln!("Outside! (2/2)");
407
408 Ok(())
409 }
410
411 #[test]
412 fn test_stdin() -> Result<()> {
413 let (rx, mut tx) = pipe()?;
414 let data = "12345\n";
415
416 write!(&tx, "{}", data)?;
417 tx.flush()?;
418
419 let guard = StdinOverride::from_io(rx)?;
420
421 print!("Please enter some text: ");
422 stdout().flush()?;
423
424 let mut s = String::new();
425 stdin().read_line(&mut s)?;
426
427 drop(guard);
428
429 assert_eq!(data, s);
430
431 println!("You typed: {}", s);
432
433 Ok(())
434 }
435
436 fn null() -> Result<File> {
437 File::create(if cfg!(windows) {
438 "nul"
439 } else if cfg!(target_os = "redox") {
440 "null:"
441 } else {
442 "/dev/null"
443 })
444 }
445
446 #[test]
447 fn test_multiple() -> Result<()> {
448 let null = null()?;
449
450 let guard_1 = StdoutOverride::from_io_ref(&null)?;
451 let guard_2 = StdoutOverride::from_io_ref(&null)?;
452
453 std::mem::forget(guard_2);
454 drop(guard_1);
455
456 Ok(())
457 }
458
459 #[test]
460 fn test_multiple_panic() -> Result<()> {
461 let null = null()?;
462
463 let guard_0 = StdoutOverride::from_io_ref(&null)?;
464 let guard_1 = StdoutOverride::from_io_ref(&null)?;
465 let guard_2 = StdoutOverride::from_io_ref(&null)?;
466 drop(guard_1);
467
468 let old_hook = std::panic::take_hook();
469 std::panic::set_hook(Box::new(|info| {
470 let payload: &'static str = info.payload().downcast_ref::<&'static str>().unwrap();
471 assert_eq!(payload, "Stdout override reset out of order!");
472 }));
473
474 assert!(std::panic::catch_unwind(|| drop(guard_2)).is_err());
475
476 std::panic::set_hook(old_hook);
477
478 drop(guard_0);
479
480 Ok(())
481 }
482}