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::Backspace => println!("×"),
|
||||||
Key::Invalid => println!("???"),
|
Key::Invalid => println!("???"),
|
||||||
Key::Error => println!("ERROR"),
|
Key::Error => println!("ERROR"),
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
stdout.flush().unwrap();
|
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 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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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};
|
||||||
|
|
||||||
|
|
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 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,18 +92,14 @@ 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)]
|
||||||
mod 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"))]
|
#[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)]
|
||||||
|
|
Loading…
Reference in New Issue