1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
//! (Rust) Silence stderr and stdout, optionally rerouting it. //! //! Stdout Gagging //! //! ```rust //! println!("STDOUT GAGGING", ); //! println!("you will see this"); //! let shh = shh::stdout().unwrap(); //! println!("but not this"); //! drop(shh); //! println!("and this"); //! ``` //! //! Stderr Gagging //! //! ```rust //! println!("STDERR GAGGING", ); //! eprintln!("you will see this"); //! let shh = shh::stderr().unwrap(); //! eprintln!("but not this"); //! drop(shh); //! eprintln!("and this"); //! ``` //! //! Redirecting Example //! //! ```rust //! println!("REDIRECTING", ); //! use std::io::{Read, Write}; //! //! std::thread::spawn(move || { //! let mut shh = shh::stdout().unwrap(); //! let mut stderr = std::io::stderr(); //! loop { //! let mut buf = Vec::new(); //! shh.read_to_end(&mut buf).unwrap(); //! stderr.write_all(&buf).unwrap(); //! } //! }); //! //! println!("This should be printed on stderr"); //! eprintln!("This will be printed on stderr as well"); //! //! // This will exit and close the spawned thread. //! // In most cases you will want to setup a channel and send a break signal to the loop, //! // and then join the thread back into it once you are finished. //! ``` //! //! # Scoping //! //! The struct `Shh` implements the `Drop` trait. Upon going out of scope, the redirection is reset and resources are cleaned up. A `Shh` will only last for the scope, and where no local variable is used, the silencing will not work. //! //! ## Example - Silencing Dropped Early //! ```rust //! println!("you will see this"); //! shh::stdout().unwrap(); // Shh struct is created, and dropped, here //! println!("and expect not to see this, but you will"); //! ``` //! //! To fix this, just assign a local variable //! ```rust //! println!("you will see this"); //! let shh = shh::stdout().unwrap(); // Shh struct is created here //! println!("and expect not to see this"); //! drop(shh); // and dropped here //! println!("now it works!"); //! ``` #![warn(missing_docs)] /// Just string replace the standard api interface. macro_rules! create_impl_interface { ($os:tt, $fdandle:ty) => { #[cfg($os)] mod $os; /// My pet name for the unix Fd or windows Handle types. #[cfg($os)] type Fdandle = $fdandle; /// Type alias for `Shh`ing the stdout. #[cfg($os)] pub type ShhStdout = Shh<$os::Impl, io::Stdout>; /// Type alias for `Shh`ing the stderr. #[cfg($os)] pub type ShhStderr = Shh<$os::Impl, io::Stderr>; /// Silence and redirect the stdout stream. /// /// `Shh` implements `io::Read`, with all captured output able to be read back out. /// /// # Example /// ```rust /// println!("you will see this"); /// let shh = shh::stdout().unwrap(); /// println!("but not this"); /// drop(shh); /// println!("and this"); /// ``` #[cfg($os)] pub fn stdout() -> io::Result<ShhStdout> { Shh::new() } /// Silence and redirect the stderr stream. /// /// `Shh` implements `io::Read`, with all captured output able to be read back out. /// /// # Example /// ```rust /// eprintln!("you will see this"); /// let shh = shh::stderr().unwrap(); /// eprintln!("but not this"); /// drop(shh); /// eprintln!("and this"); /// ``` #[cfg($os)] pub fn stderr() -> io::Result<ShhStderr> { Shh::new() } }; } create_impl_interface!(windows, std::os::windows::io::RawHandle); create_impl_interface!(unix, std::os::unix::io::RawFd); use std::fs::File; use std::io::{self, Read}; use std::marker::PhantomData; /// Trait which can create a read and write pipe as files. pub trait Create { /// Create the read and write handles, taking ownership with `File`. /// Return in (read_file, write_file) fn create_files() -> io::Result<(File, File)>; } /// Trait which defines functions that divert and reinstate streams for std devices. pub trait Divert<D> { /// Divert from the device into the `write_file`'s handle/fd; fn divert_std_stream(write_file: &File) -> io::Result<()>; /// Reinstate the std device to output. Gives the original handle/fd for use. fn reinstate_std_stream(original_fdandle: Fdandle) -> io::Result<()>; } /// Trait to enable getting the handle/fd a std device was looking at. pub trait Device { /// Obtain the original handle/fd the std device was using. fn obtain_original() -> io::Result<Fdandle>; } /// Trait to read from file. /// /// Unfortunately, the windows implementation needs a little more massaging to read from the pipe, /// without completely blocking the thread. This trait effectively gives implementor a little more control /// over the reading if they want to inject their own method. Otherwise just call `.read()` on the `File`. pub trait ShhRead { /// Read contents of file into `buf`. Works the same way as [`Read`](https://doc.rust-lang.org/std/io/trait.Read.html) fn shh_read(read_file: &File, buf: &mut [u8]) -> io::Result<usize>; } /// A structure holding the redirection data. /// /// `Shh` implements `io::Read`, with all captured output able to be read back out. pub struct Shh<Impl, Device> where Impl: Divert<Device>, { original: Fdandle, write_file: File, read_file: File, impl_mrker: PhantomData<Impl>, device_mrker: PhantomData<Device>, } impl<I, D> Shh<I, D> where I: Create + Divert<D>, D: Device, { fn new() -> io::Result<Self> { // obtain current ptr to device let original = <D as Device>::obtain_original()?; // create data (a pipe or file) that can give a Fdandle to redirect to, and be closed let (read_file, write_file) = <I as Create>::create_files()?; let shh = Shh { original, read_file, write_file, impl_mrker: PhantomData, device_mrker: PhantomData, }; // redirect the device to the write fdandle <I as Divert<D>>::divert_std_stream(&shh.write_file)?; Ok(shh) } } impl<I: Divert<D>, D> Drop for Shh<I, D> { fn drop(&mut self) { <I as Divert<D>>::reinstate_std_stream(self.original).unwrap_or(()) } } impl<I: Divert<D> + ShhRead, D> Read for Shh<I, D> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { <I as ShhRead>::shh_read(&self.read_file, buf) } } /// Unsafe because of the `original: Fdandle`. This is retrieved from os and does not need /// cleaning up. unsafe impl<I: Divert<D>, D> Send for Shh<I, D> {}