#![warn(missing_docs)]
macro_rules! create_impl_interface {
($os:tt, $fdandle:ty) => {
#[cfg($os)]
mod $os;
#[cfg($os)]
type Fdandle = $fdandle;
#[cfg($os)]
pub type ShhStdout = Shh<$os::Impl, io::Stdout>;
#[cfg($os)]
pub type ShhStderr = Shh<$os::Impl, io::Stderr>;
#[cfg($os)]
pub fn stdout() -> io::Result<ShhStdout> {
Shh::new()
}
#[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;
pub trait Create {
fn create_files() -> io::Result<(File, File)>;
}
pub trait Divert<D> {
fn divert_std_stream(write_file: &File) -> io::Result<()>;
fn reinstate_std_stream(original_fdandle: Fdandle) -> io::Result<()>;
}
pub trait Device {
fn obtain_original() -> io::Result<Fdandle>;
}
pub trait ShhRead {
fn shh_read(read_file: &File, buf: &mut [u8]) -> io::Result<usize>;
}
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> {
let original = <D as Device>::obtain_original()?;
let (read_file, write_file) = <I as Create>::create_files()?;
let shh = Shh {
original,
read_file,
write_file,
impl_mrker: PhantomData,
device_mrker: PhantomData,
};
<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 impl<I: Divert<D>, D> Send for Shh<I, D> {}