stdio_override/
lib.rs

1#![deny(missing_docs)]
2#![cfg_attr(test, deny(warnings))]
3
4//! # Stdio-Override
5//!
6//! This crate provides a library for overriding Stdio streams. <br>
7//! It provides a guard for the replacement so that when the guard is dropped the streams are switched back
8//! and the replacement stream will be closed.
9//!
10//! You can create multiple stdio overrides, but if you attempt to drop them out of order then they
11//! will panic.
12//!
13//! You can use the [`os_pipe`](https://docs.rs/os_pipe) crate to capture the standard streams in
14//! memory.
15//!
16//! **Notice:** When trying to use this in tests you **must** run with `cargo test -- --test-threads=1 --nocapture` otherwise it will redirect stdout/stderr again.
17//!
18//! This library is made to be intuitive and easy to use.
19//!
20//! ## Examples
21//! Stdout:
22//! ```rust
23//!# fn main() -> std::io::Result<()> {
24//! use stdio_override::StdoutOverride;
25//! use std::fs;
26//! let file_name = "./test.txt";
27//!# std::fs::remove_file(file_name);
28//! let guard = StdoutOverride::from_file(file_name)?;
29//!
30//! println!("Isan to Stdout!");
31//! let contents = fs::read_to_string(file_name)?;
32//! assert_eq!("Isan to Stdout!\n", contents);
33//!
34//! drop(guard);
35//! println!("Outside!");
36//!
37//!# Ok(())
38//!# }
39//! ```
40//!
41//! Stderr:
42//! ```rust
43//! # fn main() -> std::io::Result<()> {
44//! use stdio_override::StderrOverride;
45//! use std::fs;
46//! let file_name = "./testerr.txt";
47//! # std::fs::remove_file(file_name);
48//! let guard = StderrOverride::from_file(file_name)?;
49//!
50//! eprintln!("Failure to stderr");
51//! let contents = fs::read_to_string(file_name)?;
52//! assert_eq!("Failure to stderr\n", contents);
53//!
54//! drop(guard);
55//! eprintln!("Stderr is back!");
56//!
57//! # Ok(())
58//! # }
59//! ```
60//!
61//! Stdin:
62//! ```rust
63//! # fn main() -> std::io::Result<()> {
64//! # use std::{fs::{self, File}, io::{self, Write}};
65//! use stdio_override::StdinOverride;
66//! let file_name = "./inputs.txt";
67//! fs::write(file_name, "Inputs to stdin")?;
68//!
69//! let guard = StdinOverride::from_file(file_name)?;
70//! let mut user_input = String::new();
71//! io::stdin().read_line(&mut user_input)?;
72//!
73//! drop(guard);
74//!
75//! assert_eq!("Inputs to stdin", user_input);
76//! // Stdin is working as usual again, because the guard is dropped.
77//!
78//!# Ok(())
79//!# }
80//! ```
81
82use 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/// An overridden standard input.
98///
99/// Reading from this reads the original standard input. When it is dropped the standard input
100/// will be reset.
101#[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    /// Read standard input from the raw file descriptor or handle. It must be readable.
114    ///
115    /// The stream is not owned, so it is your job to close it later. Closing it while this exists
116    /// will not close the standard error.
117    pub fn from_raw(raw: imp::Raw) -> io::Result<Self> {
118        Self::from_raw_inner(raw, false)
119    }
120    /// Read standard input from the owned raw file descriptor or handle. It must be readable.
121    ///
122    /// The stream is owned, and so you must not use it after passing it to this function.
123    pub fn from_raw_owned(raw: imp::Raw) -> io::Result<Self> {
124        Self::from_raw_inner(raw, true)
125    }
126    /// Read standard input from the IO device. The device must be readable.
127    ///
128    /// Dropping the IO device after calling this function will not close the standard input.
129    pub fn from_io_ref<T: imp::AsRaw>(io: &T) -> io::Result<Self> {
130        Self::from_raw(imp::as_raw(io))
131    }
132    /// Read standard input from the IO device. The device must be readable.
133    pub fn from_io<T: imp::IntoRaw>(io: T) -> io::Result<Self> {
134        Self::from_raw_owned(imp::into_raw(io))
135    }
136    /// Read standard input from the file at that file path.
137    ///
138    /// The file must exist and be readable.
139    pub fn from_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
140        Self::from_io(File::open(path)?)
141    }
142    /// Reset the standard input to its state before this type was constructed.
143    ///
144    /// This can be called to manually handle errors produced by the destructor.
145    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/// An overridden standard output.
182///
183/// Writing to this writes to the original standard output. When it is dropped the standard output
184/// will be reset.
185#[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    /// Redirect standard output to the raw file descriptor or handle. It must be writable.
198    ///
199    /// The stream is not owned, so it is your job to close it later. Closing it while this exists
200    /// will not close the standard output.
201    pub fn from_raw(raw: imp::Raw) -> io::Result<Self> {
202        Self::from_raw_inner(raw, false)
203    }
204    /// Redirect standard output to the owned raw file descriptor or handle. It must be writable.
205    ///
206    /// The stream is owned, and so you must not use it after passing it to this function.
207    pub fn from_raw_owned(raw: imp::Raw) -> io::Result<Self> {
208        Self::from_raw_inner(raw, true)
209    }
210    /// Redirect standard output to the IO device. The device must be writable.
211    ///
212    /// Dropping the IO device after calling this function will not close the standard output.
213    pub fn from_io_ref<T: imp::AsRaw>(io: &T) -> io::Result<Self> {
214        Self::from_raw(imp::as_raw(io))
215    }
216    /// Redirect standard output to the IO device. The device must be writable.
217    pub fn from_io<T: imp::IntoRaw>(io: T) -> io::Result<Self> {
218        Self::from_raw_owned(imp::into_raw(io))
219    }
220    /// Redirect the standard output to the file at that file path.
221    ///
222    /// The file will be created if it does not exist, and will be truncated if it does.
223    pub fn from_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
224        Self::from_io(File::create(path)?)
225    }
226    /// Reset the standard output to its state before this type was constructed.
227    ///
228    /// This can be called to manually handle errors produced by the destructor.
229    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/// An overridden standard error.
272///
273/// Writing to this writes to the original standard error. When it is dropped the standard error
274/// will be reset.
275#[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    /// Redirect standard error to the raw file descriptor or handle. It must be writable.
288    ///
289    /// The stream is not owned, so it is your job to close it later. Closing it while this exists
290    /// will not close the standard error.
291    pub fn from_raw(raw: imp::Raw) -> io::Result<Self> {
292        Self::from_raw_inner(raw, false)
293    }
294    /// Redirect standard error to the owned raw file descriptor or handle. It must be writable.
295    ///
296    /// The stream is owned, and so you must not use it after passing it to this function.
297    pub fn from_raw_owned(raw: imp::Raw) -> io::Result<Self> {
298        Self::from_raw_inner(raw, true)
299    }
300    /// Redirect standard error to the IO device. The device must be writable.
301    ///
302    /// Dropping the IO device after calling this function will not close the standard error.
303    pub fn from_io_ref<T: imp::AsRaw>(io: &T) -> io::Result<Self> {
304        Self::from_raw(imp::as_raw(io))
305    }
306    /// Redirect standard error to the IO device. The device must be writable.
307    pub fn from_io<T: imp::IntoRaw>(io: T) -> io::Result<Self> {
308        Self::from_raw_owned(imp::into_raw(io))
309    }
310    /// Redirect the standard error to the file at that file path.
311    ///
312    /// The file will be created if it does not exist, and will be truncated if it does.
313    pub fn from_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
314        Self::from_io(File::create(path)?)
315    }
316    /// Reset the standard error to its state before this type was constructed.
317    ///
318    /// This can be called to manually handle errors produced by the destructor.
319    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}