ttyaskpass 2.0.0-alpha

a safely passphrase prompt library.
Documentation
use std::mem;
use std::fs::File;
use std::io::{ self, Read, Write };
use std::os::unix::io::AsRawFd;
use libc::{ termios, tcgetattr, tcsetattr, cfmakeraw };
use termion::get_tty;
use termion::event::Key;
use termion::input::TermRead;


pub struct RawTTY<F: AsRawFd> {
    prev_termios: termios,
    tty: F
}

impl RawTTY<File> {
    pub fn new() -> io::Result<RawTTY<File>> {
        Self::from_tty(get_tty()?)
    }
}

impl<F: AsRawFd> RawTTY<F> {
    pub fn from_tty(tty: F) -> io::Result<RawTTY<F>> {
        unsafe {
            let tty_fd = tty.as_raw_fd();
            let mut ios = mem::zeroed();

            if tcgetattr(tty_fd, &mut ios) != 0 {
                return Err(io::Error::last_os_error())
            }
            let prev_termios = ios;

            cfmakeraw(&mut ios);

            if tcsetattr(tty_fd, 0, &ios) != 0 {
                return Err(io::Error::last_os_error())
            }

            Ok(RawTTY { prev_termios, tty })
        }
    }
}

impl<F: AsRawFd + Read> Read for RawTTY<F> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.tty.read(buf)
    }
}

impl<F: AsRawFd + Write> Write for RawTTY<F> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.tty.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.tty.flush()
    }
}

impl<F: AsRawFd> Drop for RawTTY<F> {
    fn drop(&mut self) {
        unsafe {
            tcsetattr(self.tty.as_raw_fd(), 0, &self.prev_termios);
        }
    }
}

pub fn read_from_tty<T, F>(raw_tty: T, mut f: F) -> io::Result<()>
    where
        T: Read,
        F: FnMut(Key) -> io::Result<bool>
{
    for key in Some(Ok(Key::Null)).into_iter()
        .chain(raw_tty.keys())
    {
        if f(key?)? {
            break
        }
    }

    Ok(())
}

#[cfg_attr(
    any(target_os = "macos", target_os = "ios"),
    should_panic
)]
#[test]
fn test_raw_tty_create() {
    assert!(RawTTY::new().is_ok());
}