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::Backspace => println!("×"),
Key::Invalid => println!("???"), Key::Invalid => println!("???"),
Key::Error => println!("ERROR"), Key::Error => println!("ERROR"),
_ => {},
} }
stdout.flush().unwrap(); 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 std::io::{Read, Write, Error, ErrorKind, Result as IoResult};
use {IntoRawMode, TerminalError}; use IntoRawMode;
#[cfg(feature = "nightly")] #[cfg(feature = "nightly")]
use std::io::{Chars, CharsError}; use std::io::{Chars, CharsError};
@ -30,6 +30,10 @@ pub enum Key {
// TODO handle errors better? // TODO handle errors better?
/// IO error. /// IO error.
Error, Error,
#[allow(missing_docs)]
#[doc(hidden)]
__IsNotComplete
} }
/// An iterator over input keys. /// 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 /// EOT and ETX will abort the prompt, returning `None`. Newline or carriage return will
/// complete the password input. /// 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 { 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 _raw = try!(writer.into_raw_mode());
let mut passbuf = Vec::with_capacity(30); let mut passbuf = Vec::with_capacity(30);
for c in self.bytes() { for c in self.bytes() {
match c { match c {
Err(_) => return Err(TerminalError::StdinError), Err(e) => return Err(e),
Ok(0) | Ok(3) | Ok(4) => return Ok(None), Ok(0) | Ok(3) | Ok(4) => return Ok(None),
Ok(b'\n') | Ok(b'\r') => break, Ok(b'\n') | Ok(b'\r') => break,
Ok(c) => passbuf.push(c), 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)) Ok(Some(passwd))
} }

View File

@ -24,9 +24,6 @@ pub use input::{TermRead, Key};
#[cfg(feature = "nightly")] #[cfg(feature = "nightly")]
pub use input::Keys; pub use input::Keys;
mod error;
pub use error::TerminalError;
mod raw; mod raw;
pub use raw::{IntoRawMode, RawTerminal}; 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 std::ops::{Deref, DerefMut};
use TerminalError;
/// A terminal restorer, which keeps the previous state of the terminal, and restores it, when /// A terminal restorer, which keeps the previous state of the terminal, and restores it, when
/// dropped. /// dropped.
#[cfg(target_os = "redox")] #[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". /// Types which can be converted into "raw mode".
pub trait IntoRawMode: Sized { pub trait IntoRawMode: Sized {
/// Switch to raw mode. /// 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 /// 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 /// 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. /// 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 { impl<W: Write> IntoRawMode for W {
#[cfg(not(target_os = "redox"))] #[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}; 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(); let prev_ios = ios.clone();
if err != 0 { if exit != 0 {
return Err(TerminalError::LoadAttrError); return Err(Error::new(ErrorKind::Other, "Unable to get Termios attribute."));
} }
unsafe { unsafe {
@ -75,7 +83,7 @@ impl<W: Write> IntoRawMode for W {
} }
if set_terminal_attr(&mut ios as *mut _) != 0 { if set_terminal_attr(&mut ios as *mut _) != 0 {
Err(TerminalError::SetAttrError) Err(Error::new(ErrorKind::Other, "Unable to set Termios attribute."))
} else { } else {
Ok(RawTerminal { Ok(RawTerminal {
prev_ios: prev_ios, prev_ios: prev_ios,
@ -84,17 +92,13 @@ impl<W: Write> IntoRawMode for W {
} }
} }
#[cfg(target_os = "redox")] #[cfg(target_os = "redox")]
fn into_raw_mode(self) -> Result<RawTerminal<W>, TerminalError> { fn into_raw_mode(self) -> IoResult<RawTerminal<W>> {
use TermControl; use TermControl;
if let Err(_) = self.csi("r") { self.csi("r").map(|_| RawTerminal {
Err(TerminalError::StdoutError)
} else {
Ok(RawTerminal {
output: self, output: self,
}) })
} }
}
} }
#[cfg(test)] #[cfg(test)]

View File

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