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
71pub trait AsFdExt: AsFd {
72    /// Borrow the current file descriptor as a BorrowFd.
73    ///
74    /// This method provides a cross-platform way of calling
75    /// `as_fd()` on Unix and `as_handle()` on Windows.
76    fn borrow_file(&self) -> BorrowedFd<'_> {
77        borrow_fd(self)
78    }
79
80    /// Duplicates the current file descriptor as an OwnedFd.
81    ///
82    /// This is a shorthand for to `file.borrow_fd().try_clone_to_owned()`.
83    fn duplicate_file(&self) -> Result<OwnedFd> {
84        self.borrow_file().try_clone_to_owned()
85    }
86}
87
88impl<T: AsFd> AsFdExt for T {}
89
90#[must_use]
91/// A type that restores a replaced file descriptor when it's dropped
92pub struct Guard {
93    stdio: Stdio,
94    backup: Option<OwnedFd>,
95}
96
97impl Guard {
98    /// Consume the guard without restoring the file descriptor.
99    pub fn forget(self) {
100        self.into_inner();
101    }
102
103    /// Consume the guard returning an OwnedFd with the original file descriptor
104    pub fn into_inner(mut self) -> OwnedFd {
105        self.backup.take().unwrap()
106    }
107
108    /// Obtain a BorrowFd to the original file descriptor
109    pub fn borrow_inner(&self) -> BorrowedFd<'_> {
110        self.backup.as_ref().unwrap().borrow_file()
111    }
112}
113
114impl Drop for Guard {
115    fn drop(&mut self) {
116        if let Some(backup) = self.backup.take() {
117            let _ = unsafe { override_stdio(backup, self.stdio) };
118        }
119    }
120}
121
122/// Returns a file that can be written to.
123/// The file discards all bytes written to it.
124///
125/// ```rust
126/// # use stdio_utils::null;
127/// # use std::io::Write;
128/// let mut f = null()?;
129/// f.write_all(b"hello world")?;
130/// # std::io::Result::Ok(())
131/// ```
132///
133/// It can be used to dicard all the data written
134/// to stdout
135///
136/// ```rust
137/// # use stdio_utils::{null, StdioOverride as _};
138/// # use std::io::Write;
139/// # use std::io::stdout;
140/// # let mut lock = stdout().lock();
141/// let _guard = null()?.override_stdout();
142/// println!("hello world!"); // this will never print
143/// # std::io::Result::Ok(())
144/// ```
145pub fn null() -> Result<File> {
146    File::options()
147        .create(false)
148        .read(false)
149        .append(true)
150        .open(DEV_NULL)
151}