Use IO error, instead of a custom one
This commit is contained in:
parent
7145651a83
commit
32d8ccfa24
|
@ -28,6 +28,7 @@ fn main() {
|
|||
Key::Backspace => println!("×"),
|
||||
Key::Invalid => println!("???"),
|
||||
Key::Error => println!("ERROR"),
|
||||
_ => {},
|
||||
}
|
||||
stdout.flush().unwrap();
|
||||
}
|
||||
|
|
47
src/error.rs
47
src/error.rs
|
@ -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())
|
||||
}
|
||||
}
|
16
src/input.rs
16
src/input.rs
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
|
34
src/raw.rs
34
src/raw.rs
|
@ -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,18 +92,14 @@ 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 {
|
||||
self.csi("r").map(|_| RawTerminal {
|
||||
output: self,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
|
28
src/size.rs
28
src/size.rs
|
@ -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)]
|
||||
|
|
Loading…
Reference in New Issue