Use IO error, instead of a custom one

This commit is contained in:
Ticki 2016-03-13 11:55:24 +01:00
parent 7145651a83
commit 32d8ccfa24
6 changed files with 47 additions and 86 deletions

View File

@ -28,6 +28,7 @@ fn main() {
Key::Backspace => println!("×"),
Key::Invalid => println!("???"),
Key::Error => println!("ERROR"),
_ => {},
}
stdout.flush().unwrap();
}

View File

@ -1,47 +0,0 @@
use std::error::Error;
use std::fmt::{Display, Formatter, Error as FmtError};
/// An terminal error.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum TerminalError {
/// Failed to load attributes.
LoadAttrError,
/// Failed to set attributes.
SetAttrError,
/// Failed to get terminal size.
TermSizeError,
/// Failed to write to stdout.
StdoutError,
/// Failed to read from stdin.
StdinError,
/// Failed to parse number.
ParseError,
/// Invalid or malformed Unicode.
UnicodeError
}
impl TerminalError {
fn msg(self) -> &'static str {
match self {
TerminalError::LoadAttrError => "Failed to load Terminal attributes.",
TerminalError::SetAttrError => "Failed to set Terminal attribute.",
TerminalError::TermSizeError => "Failed to get terminal size.",
TerminalError::StdoutError => "Failed to write to stdout.",
TerminalError::StdinError => "Failed to read from stdin.",
TerminalError::ParseError => "Failed to parse number.",
TerminalError::UnicodeError => "Invalid or malformed Unicode.",
}
}
}
impl Error for TerminalError {
fn description(&self) -> &str {
self.msg()
}
}
impl Display for TerminalError {
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
f.write_str(self.msg())
}
}

View File

@ -1,5 +1,5 @@
use std::io::{Read, Write};
use {IntoRawMode, TerminalError};
use std::io::{Read, Write, Error, ErrorKind, Result as IoResult};
use IntoRawMode;
#[cfg(feature = "nightly")]
use std::io::{Chars, CharsError};
@ -30,6 +30,10 @@ pub enum Key {
// TODO handle errors better?
/// IO error.
Error,
#[allow(missing_docs)]
#[doc(hidden)]
__IsNotComplete
}
/// An iterator over input keys.
@ -77,7 +81,7 @@ pub trait TermRead {
///
/// EOT and ETX will abort the prompt, returning `None`. Newline or carriage return will
/// complete the password input.
fn read_passwd<W: Write>(&mut self, writer: &mut W) -> Result<Option<String>, TerminalError>;
fn read_passwd<W: Write>(&mut self, writer: &mut W) -> IoResult<Option<String>>;
}
impl<R: Read> TermRead for R {
@ -88,20 +92,20 @@ impl<R: Read> TermRead for R {
}
}
fn read_passwd<W: Write>(&mut self, writer: &mut W) -> Result<Option<String>, TerminalError> {
fn read_passwd<W: Write>(&mut self, writer: &mut W) -> IoResult<Option<String>> {
let _raw = try!(writer.into_raw_mode());
let mut passbuf = Vec::with_capacity(30);
for c in self.bytes() {
match c {
Err(_) => return Err(TerminalError::StdinError),
Err(e) => return Err(e),
Ok(0) | Ok(3) | Ok(4) => return Ok(None),
Ok(b'\n') | Ok(b'\r') => break,
Ok(c) => passbuf.push(c),
}
}
let passwd = try!(String::from_utf8(passbuf).map_err(|_| TerminalError::UnicodeError));
let passwd = try!(String::from_utf8(passbuf).map_err(|e| Error::new(ErrorKind::InvalidData, e)));
Ok(Some(passwd))
}

View File

@ -24,9 +24,6 @@ pub use input::{TermRead, Key};
#[cfg(feature = "nightly")]
pub use input::Keys;
mod error;
pub use error::TerminalError;
mod raw;
pub use raw::{IntoRawMode, RawTerminal};

View File

@ -1,8 +1,6 @@
use std::io::Write;
use std::io::{Write, Error, ErrorKind, Result as IoResult};
use std::ops::{Deref, DerefMut};
use TerminalError;
/// A terminal restorer, which keeps the previous state of the terminal, and restores it, when
/// dropped.
#[cfg(target_os = "redox")]
@ -49,6 +47,16 @@ impl<W> DerefMut for RawTerminal<W> {
}
}
impl<W: Write> Write for RawTerminal<W> {
fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
self.output.write(buf)
}
fn flush(&mut self) -> IoResult<()> {
self.output.flush()
}
}
/// Types which can be converted into "raw mode".
pub trait IntoRawMode: Sized {
/// Switch to raw mode.
@ -56,18 +64,18 @@ pub trait IntoRawMode: Sized {
/// Raw mode means that stdin won't be printed (it will instead have to be written manually by the
/// program). Furthermore, the input isn't canonicalised or buffered (that is, you can read from
/// stdin one byte of a time). The output is neither modified in any way.
fn into_raw_mode(self) -> Result<RawTerminal<Self>, TerminalError>;
fn into_raw_mode(self) -> IoResult<RawTerminal<Self>>;
}
impl<W: Write> IntoRawMode for W {
#[cfg(not(target_os = "redox"))]
fn into_raw_mode(self) -> Result<RawTerminal<W>, TerminalError> {
fn into_raw_mode(self) -> IoResult<RawTerminal<W>> {
use termios::{cfmakeraw, get_terminal_attr, set_terminal_attr};
let (mut ios, err) = get_terminal_attr();
let (mut ios, exit) = get_terminal_attr();
let prev_ios = ios.clone();
if err != 0 {
return Err(TerminalError::LoadAttrError);
if exit != 0 {
return Err(Error::new(ErrorKind::Other, "Unable to get Termios attribute."));
}
unsafe {
@ -75,7 +83,7 @@ impl<W: Write> IntoRawMode for W {
}
if set_terminal_attr(&mut ios as *mut _) != 0 {
Err(TerminalError::SetAttrError)
Err(Error::new(ErrorKind::Other, "Unable to set Termios attribute."))
} else {
Ok(RawTerminal {
prev_ios: prev_ios,
@ -84,16 +92,12 @@ impl<W: Write> IntoRawMode for W {
}
}
#[cfg(target_os = "redox")]
fn into_raw_mode(self) -> Result<RawTerminal<W>, TerminalError> {
fn into_raw_mode(self) -> IoResult<RawTerminal<W>> {
use TermControl;
if let Err(_) = self.csi("r") {
Err(TerminalError::StdoutError)
} else {
Ok(RawTerminal {
output: self,
})
}
self.csi("r").map(|_| RawTerminal {
output: self,
})
}
}

View File

@ -1,8 +1,8 @@
use std::io::{Error, ErrorKind, Result as IoResult};
#[cfg(not(target_os = "redox"))]
use libc::c_ushort;
use TerminalError;
#[cfg(not(target_os = "redox"))]
#[repr(C)]
struct TermSize {
@ -27,7 +27,7 @@ fn tiocgwinsz() -> u32 {
/// Get the size of the terminal.
#[cfg(not(target_os = "redox"))]
pub fn terminal_size() -> Result<(usize, usize), TerminalError> {
pub fn terminal_size() -> IoResult<(usize, usize)> {
use libc::ioctl;
use libc::STDOUT_FILENO;
@ -38,24 +38,26 @@ pub fn terminal_size() -> Result<(usize, usize), TerminalError> {
if ioctl(STDOUT_FILENO, tiocgwinsz(), &mut size as *mut _) == 0 {
Ok((size.col as usize, size.row as usize))
} else {
Err(TerminalError::TermSizeError)
Err(Error::new(ErrorKind::Other, "Unable to get the terminal size."))
}
}
}
/// Get the size of the terminal.
#[cfg(target_os = "redox")]
pub fn terminal_size() -> Result<(usize, usize), TerminalError> {
use std::env::var;
pub fn terminal_size() -> IoResult<(usize, usize), TerminalError> {
fn get_int(s: &'static str) -> IoResult<usize> {
use std::env::{VarError, var};
let w = var("COLUMNS").map_err(|_| TerminalError::TermSizeError).and_then(|x| {
x.parse().map_err(|_| TerminalError::ParseError)
});
let h = var("LINES").map_err(|_| TerminalError::TermSizeError).and_then(|x| {
x.parse().map_err(|_| TerminalError::ParseError)
});
var(s).map_err(|e| match e {
VarError::NotPresent => Error::new(ErrorKind::NotFound, e),
VarError::NotUnicode(u) => Error::new(ErrorKind::InvalidData, u),
}).and_then(|x| {
x.parse().map_err(|e| Error::new(ErrorKind::InvalidData, e))
})
}
Ok((try!(w), try!(h)))
Ok((try!(get_int("COLUMNS")), try!(get_int("LINES"))))
}
#[cfg(test)]