stdio_utils/lib.rs
1#[cfg_attr(unix, path = "sys/unix.rs")]
2#[cfg_attr(windows, path = "sys/windows.rs")]
3mod sys;
4
5use std::fs::File;
6use std::io::Result;
7
8use crate::sys::{borrow_fd, override_stdio, AsFd, BorrowedFd, OwnedFd, DEV_NULL};
9
10#[derive(Clone, Copy)]
11enum Stdio {
12 Stdin,
13 Stdout,
14 Stderr,
15}
16
17pub trait StdioOverride: AsFd {
18 /// Replace the process standard output with a duplicate of the file descriptor.
19 ///
20 /// ```rust
21 /// # use stdio_utils::StdioOverride as _;
22 /// # use std::fs::{File, read_to_string};
23 /// # use std::io::stdout;
24 /// # let mut lock = stdout().lock();
25 /// let _guard = File::create("./output.txt")?.override_stdout()?;
26 /// println!("hello world!");
27 /// let output = read_to_string("./output.txt")?;
28 /// assert_eq!(output, "hello world!\n");
29 /// # std::io::Result::Ok(())
30 /// ```
31 fn override_stdout(&self) -> Result<Guard> {
32 let stdio = Stdio::Stdout;
33 let backup = unsafe { override_stdio(self, stdio) }?;
34 let backup = Some(backup);
35 Ok(Guard { backup, stdio })
36 }
37
38 /// Replace the process standard error with a duplicate of the file descriptor.
39 ///
40 /// See [duplicate_to_stdout](StdioOverride::override_stdout).
41 fn override_stderr(&self) -> Result<Guard> {
42 let stdio = Stdio::Stderr;
43 let backup = unsafe { override_stdio(self, stdio) }?;
44 let backup = Some(backup);
45 Ok(Guard { backup, stdio })
46 }
47
48 /// Replace the process standard input with a duplicate of the file descriptor.
49 ///
50 /// ```rust
51 /// # use stdio_utils::StdioOverride as _;
52 /// # use std::fs::{File, write};
53 /// # use std::io::{stdin, stdout, read_to_string};
54 /// # let mut lock = stdout().lock();
55 /// write("./input.txt", "hello world!")?;
56 /// let _guard = File::open("./input.txt")?.override_stdin()?;
57 /// let input = read_to_string(stdin())?;
58 /// assert_eq!(input, "hello world!");
59 /// # std::io::Result::Ok(())
60 /// ```
61 fn override_stdin(&self) -> Result<Guard> {
62 let stdio = Stdio::Stdin;
63 let backup = unsafe { override_stdio(self, stdio) }?;
64 let backup = Some(backup);
65 Ok(Guard { backup, stdio })
66 }
67}
68
69impl<T: AsFd> StdioOverride for T {}
70
71#[must_use]
72/// A type that restores a replaced file descriptor when it's dropped
73pub struct Guard {
74 stdio: Stdio,
75 backup: Option<OwnedFd>,
76}
77
78impl Guard {
79 /// Consume the guard without restoring the file descriptor.
80 pub fn forget(self) {
81 self.into_inner();
82 }
83
84 /// Consume the guard returning an OwnedFd with the original file descriptor
85 pub fn into_inner(mut self) -> OwnedFd {
86 self.backup.take().unwrap()
87 }
88
89 /// Obtain a BorrowFd to the original file descriptor
90 pub fn borrow_inner(&self) -> BorrowedFd<'_> {
91 borrow_fd(self.backup.as_ref().unwrap())
92 }
93}
94
95impl Drop for Guard {
96 fn drop(&mut self) {
97 if let Some(backup) = self.backup.take() {
98 let _ = unsafe { override_stdio(backup, self.stdio) };
99 }
100 }
101}
102
103/// Returns a file that can be written to.
104/// The file discards all bytes written to it.
105///
106/// ```rust
107/// # use stdio_utils::null;
108/// # use std::io::Write;
109/// let mut f = null()?;
110/// f.write_all(b"hello world")?;
111/// # std::io::Result::Ok(())
112/// ```
113///
114/// It can be used to dicard all the data written
115/// to stdout
116///
117/// ```rust
118/// # use stdio_utils::{null, StdioOverride as _};
119/// # use std::io::Write;
120/// # use std::io::stdout;
121/// # let mut lock = stdout().lock();
122/// let _guard = null()?.override_stdout();
123/// println!("hello world!"); // this will never print
124/// # std::io::Result::Ok(())
125/// ```
126pub fn null() -> Result<File> {
127 File::options()
128 .create(false)
129 .read(false)
130 .append(true)
131 .open(DEV_NULL)
132}