Skip to content

Commit 92a94ac

Browse files
committed
Merge pull request #49 from cmr/error-handling
Error handling overhaul
2 parents 0961aa7 + 3ae6bb0 commit 92a94ac

6 files changed

Lines changed: 418 additions & 193 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22

33
name = "term"
4-
version = "0.2.14"
4+
version = "0.3.0"
55
authors = ["The Rust Project Developers", "Steven Allen"]
66
license = "MIT/Apache-2.0"
77
readme = "README.md"

src/lib.rs

Lines changed: 167 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
//! t.fg(term::color::RED).unwrap();
4949
//! writeln!(t, "world!").unwrap();
5050
//!
51-
//! assert!(t.reset().unwrap());
51+
//! t.reset().unwrap();
5252
//! }
5353
//! ```
5454
//!
@@ -181,6 +181,153 @@ pub enum Attr {
181181
BackgroundColor(color::Color)
182182
}
183183

184+
/// An error arising from interacting with the terminal.
185+
#[derive(Debug)]
186+
pub enum Error {
187+
/// Indicates an error from any underlying IO
188+
Io(io::Error),
189+
/// Indicates an error during terminfo parsing
190+
TerminfoParsing(terminfo::Error),
191+
/// Indicates an error expanding a parameterized string from the terminfo database
192+
ParameterizedExpansion(terminfo::parm::Error),
193+
/// Indicates that the terminal does not support the requested operation.
194+
NotSupported,
195+
/// Indicates that the `TERM` environment variable was unset, and thus we were unable to detect
196+
/// which terminal we should be using.
197+
TermUnset,
198+
/// Indicates that we were unable to find a terminfo entry for the requested terminal.
199+
TerminfoEntryNotFound,
200+
/// Indicates that the cursor could not be moved to the requested position.
201+
CursorDestinationInvalid,
202+
/// Indicates that the terminal does not support displaying the requested color.
203+
///
204+
/// This is like `NotSupported`, but more specific.
205+
ColorOutOfRange,
206+
#[doc(hidden)]
207+
/// Please don't match against this - if you do, we can't promise we won't break your crate
208+
/// with a semver-compliant version bump.
209+
__Nonexhaustive,
210+
}
211+
212+
// manually implemented because std::io::Error does not implement Eq/PartialEq
213+
impl std::cmp::PartialEq for Error {
214+
fn eq(&self, other: &Error) -> bool {
215+
use Error::*;
216+
match self {
217+
&Io(_) => {
218+
false
219+
}
220+
&TerminfoParsing(ref inner1) => {
221+
match other {
222+
&TerminfoParsing(ref inner2) => inner1 == inner2,
223+
_ => false
224+
}
225+
}
226+
&ParameterizedExpansion(ref inner1) => {
227+
match other {
228+
&ParameterizedExpansion(ref inner2) => inner1 == inner2,
229+
_ => false
230+
}
231+
}
232+
&NotSupported => {
233+
match other {
234+
&NotSupported => true,
235+
_ => false
236+
}
237+
}
238+
&TermUnset => {
239+
match other {
240+
&TermUnset => true,
241+
_ => false
242+
}
243+
}
244+
&TerminfoEntryNotFound => {
245+
match other {
246+
&TerminfoEntryNotFound => true,
247+
_ => false
248+
}
249+
}
250+
&CursorDestinationInvalid => {
251+
match other {
252+
&CursorDestinationInvalid => true,
253+
_ => false
254+
}
255+
}
256+
&ColorOutOfRange => {
257+
match other {
258+
&ColorOutOfRange => true,
259+
_ => false
260+
}
261+
}
262+
&__Nonexhaustive => {
263+
match other {
264+
&__Nonexhaustive => true,
265+
_ => false
266+
}
267+
}
268+
}
269+
}
270+
}
271+
272+
/// The canonical `Result` type using this crate's Error type.
273+
pub type Result<T> = std::result::Result<T, Error>;
274+
275+
impl std::fmt::Display for Error {
276+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
277+
use std::error::Error;
278+
if let &::Error::Io(ref e) = self {
279+
write!(f, "{}", e)
280+
} else {
281+
f.write_str(self.description())
282+
}
283+
}
284+
}
285+
286+
impl std::error::Error for Error {
287+
fn description(&self) -> &str {
288+
use Error::*;
289+
use std::error::Error;
290+
match self {
291+
&Io(ref io) => io.description(),
292+
&TerminfoParsing(ref e) => e.description(),
293+
&ParameterizedExpansion(ref e) => e.description(),
294+
&NotSupported => "operation not supported by the terminal",
295+
&TermUnset => "TERM environment variable unset, unable to detect a terminal",
296+
&TerminfoEntryNotFound => "could not find a terminfo entry for this terminal",
297+
&CursorDestinationInvalid => "could not move cursor to requested position",
298+
&ColorOutOfRange => "color not supported by the terminal",
299+
&__Nonexhaustive => "placeholder variant that shouldn't be used",
300+
}
301+
}
302+
303+
fn cause(&self) -> Option<&std::error::Error> {
304+
match self {
305+
&Error::Io(ref io) => Some(io),
306+
&Error::TerminfoParsing(ref e) => Some(e),
307+
&Error::ParameterizedExpansion(ref e) => Some(e),
308+
_ => None,
309+
}
310+
}
311+
}
312+
313+
impl std::convert::From<io::Error> for Error {
314+
fn from(val: io::Error) -> Self {
315+
Error::Io(val)
316+
}
317+
}
318+
319+
impl std::convert::From<terminfo::Error> for Error {
320+
fn from(val: terminfo::Error) -> Self {
321+
Error::TerminfoParsing(val)
322+
}
323+
}
324+
325+
impl std::convert::From<terminfo::parm::Error> for Error {
326+
fn from(val: terminfo::parm::Error) -> Self {
327+
Error::ParameterizedExpansion(val)
328+
}
329+
}
330+
184331
/// A terminal with similar capabilities to an ANSI Terminal
185332
/// (foreground/background colors etc).
186333
pub trait Terminal: Write {
@@ -192,56 +339,53 @@ pub trait Terminal: Write {
192339
/// If the color is a bright color, but the terminal only supports 8 colors,
193340
/// the corresponding normal color will be used instead.
194341
///
195-
/// Returns `Ok(true)` if the color was set, `Ok(false)` otherwise, and `Err(e)`
196-
/// if there was an I/O error.
197-
fn fg(&mut self, color: color::Color) -> io::Result<bool>;
342+
/// Returns `Ok(())` if the color change code was sent to the terminal, or `Err(e)` if there
343+
/// was an error.
344+
fn fg(&mut self, color: color::Color) -> Result<()>;
198345

199346
/// Sets the background color to the given color.
200347
///
201348
/// If the color is a bright color, but the terminal only supports 8 colors,
202349
/// the corresponding normal color will be used instead.
203350
///
204-
/// Returns `Ok(true)` if the color was set, `Ok(false)` otherwise, and `Err(e)`
205-
/// if there was an I/O error.
206-
fn bg(&mut self, color: color::Color) -> io::Result<bool>;
351+
/// Returns `Ok(())` if the color change code was sent to the terminal, or `Err(e)` if there
352+
/// was an error.
353+
fn bg(&mut self, color: color::Color) -> Result<()>;
207354

208-
/// Sets the given terminal attribute, if supported. Returns `Ok(true)`
209-
/// if the attribute was supported, `Ok(false)` otherwise, and `Err(e)` if
210-
/// there was an I/O error.
211-
fn attr(&mut self, attr: Attr) -> io::Result<bool>;
355+
/// Sets the given terminal attribute, if supported. Returns `Ok(())` if the attribute is
356+
/// supported and was sent to the terminal, or `Err(e)` if there was an error or the attribute
357+
/// wasn't supported.
358+
fn attr(&mut self, attr: Attr) -> Result<()>;
212359

213360
/// Returns whether the given terminal attribute is supported.
214361
fn supports_attr(&self, attr: Attr) -> bool;
215362

216363
/// Resets all terminal attributes and colors to their defaults.
217364
///
218-
/// Returns `Ok(true)` if the terminal was reset, `Ok(false)` otherwise, and `Err(e)` if there
219-
/// was an I/O error.
365+
/// Returns `Ok(())` if the reset code was printed, or `Err(e)` if there was an error.
220366
///
221367
/// *Note: This does not flush.*
222368
///
223369
/// That means the reset command may get buffered so, if you aren't planning on doing anything
224370
/// else that might flush stdout's buffer (e.g. writing a line of text), you should flush after
225371
/// calling reset.
226-
fn reset(&mut self) -> io::Result<bool>;
372+
fn reset(&mut self) -> Result<()>;
227373

228374
/// Moves the cursor up one line.
229375
///
230-
/// Returns `Ok(true)` if the cursor was moved, `Ok(false)` otherwise, and `Err(e)`
231-
/// if there was an I/O error.
232-
fn cursor_up(&mut self) -> io::Result<bool>;
376+
/// Returns `Ok(())` if the cursor movement code was printed, or `Err(e)` if there was an
377+
/// error.
378+
fn cursor_up(&mut self) -> Result<()>;
233379

234380
/// Deletes the text from the cursor location to the end of the line.
235381
///
236-
/// Returns `Ok(true)` if the text was deleted, `Ok(false)` otherwise, and `Err(e)`
237-
/// if there was an I/O error.
238-
fn delete_line(&mut self) -> io::Result<bool>;
382+
/// Returns `Ok(())` if the deletion code was printed, or `Err(e)` if there was an error.
383+
fn delete_line(&mut self) -> Result<()>;
239384

240385
/// Moves the cursor to the left edge of the current line.
241386
///
242-
/// Returns `Ok(true)` if the text was deleted, `Ok(false)` otherwise, and `Err(e)`
243-
/// if there was an I/O error.
244-
fn carriage_return(&mut self) -> io::Result<bool>;
387+
/// Returns `Ok(true)` if the deletion code was printed, or `Err(e)` if there was an error.
388+
fn carriage_return(&mut self) -> Result<()>;
245389

246390
/// Gets an immutable reference to the stream inside
247391
fn get_ref<'a>(&'a self) -> &'a Self::Output;

0 commit comments

Comments
 (0)