stdio-override 0.2.0

Rust library for overriding Stdin/Stdout/Stderr with a different stream
Documentation
use std::fs::File;
use std::io;
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};

use libc::c_int;
use libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO};

pub(crate) use std::os::unix::io::{AsRawFd as AsRaw, IntoRawFd as IntoRaw, RawFd as Raw};

pub(crate) fn as_raw(io: &impl AsRawFd) -> RawFd {
    io.as_raw_fd()
}
pub(crate) fn into_raw(io: impl IntoRawFd) -> RawFd {
    io.into_raw_fd()
}

pub(crate) fn override_stdin(io: RawFd, owned: bool) -> io::Result<File> {
    override_stdio(STDIN_FILENO, io, owned)
}
pub(crate) fn override_stdout(io: RawFd, owned: bool) -> io::Result<File> {
    override_stdio(STDOUT_FILENO, io, owned)
}
pub(crate) fn override_stderr(io: RawFd, owned: bool) -> io::Result<File> {
    override_stdio(STDERR_FILENO, io, owned)
}

pub(crate) fn reset_stdin(old: RawFd) -> io::Result<()> {
    set_stdio(STDIN_FILENO, old)
}
pub(crate) fn reset_stdout(old: RawFd) -> io::Result<()> {
    set_stdio(STDOUT_FILENO, old)
}
pub(crate) fn reset_stderr(old: RawFd) -> io::Result<()> {
    set_stdio(STDERR_FILENO, old)
}

fn override_stdio(stdio: RawFd, other: RawFd, owned: bool) -> io::Result<File> {
    let original = io_res(unsafe { libc::dup(stdio) })?;
    set_stdio(stdio, other)?;

    if owned {
        io_res(unsafe { libc::close(other) })?;
    }

    Ok(unsafe { File::from_raw_fd(original) })
}

fn set_stdio(stdio: RawFd, other: RawFd) -> io::Result<()> {
    io_res(unsafe { libc::dup2(other, stdio) })?;
    Ok(())
}

#[cfg(test)]
#[test]
fn test_original() -> io::Result<()> {
    use std::io::{Read, Write};

    let (mut rx, tx) = os_pipe::pipe()?;

    let real_stdout = override_stdio(STDOUT_FILENO, tx.into_raw_fd(), true)?.into_raw_fd();

    println!("Let's see where it's saved");
    io::stdout().lock().flush()?;

    set_stdio(STDOUT_FILENO, real_stdout)?;

    let mut contents = String::new();
    rx.read_to_string(&mut contents)?;
    assert_eq!("Let\'s see where it\'s saved\n", contents);

    println!("got back");

    Ok(())
}

fn io_res(res: c_int) -> io::Result<c_int> {
    if res == -1 {
        Err(io::Error::last_os_error())
    } else {
        Ok(res)
    }
}

impl AsRawFd for crate::StdinOverride {
    fn as_raw_fd(&self) -> RawFd {
        self.original.as_raw_fd()
    }
}
impl AsRawFd for crate::StdoutOverride {
    fn as_raw_fd(&self) -> RawFd {
        self.original.as_raw_fd()
    }
}
impl AsRawFd for crate::StderrOverride {
    fn as_raw_fd(&self) -> RawFd {
        self.original.as_raw_fd()
    }
}