api: use thiserror for custom error type

pull/478/head
Phillip Cloud 3 years ago
parent 91a2519cc3
commit 2854cc7df4

@ -31,6 +31,7 @@ crossterm = { version = "0.19", optional = true }
easycurses = { version = "0.12.2", optional = true }
pancurses = { version = "0.16.1", optional = true, features = ["win32a"] }
serde = { version = "1", "optional" = true, features = ["derive"]}
thiserror = "1"
[dev-dependencies]
rand = "0.8"

@ -1,5 +1,3 @@
use std::io;
use crate::backend::Backend;
use crate::buffer::Cell;
use crate::layout::Rect;
@ -7,8 +5,10 @@ use crate::style::{Color, Modifier};
use crate::symbols::{bar, block};
#[cfg(unix)]
use crate::symbols::{line, DOT};
use crate::Error;
#[cfg(unix)]
use pancurses::{chtype, ToChtype};
use std::io;
use unicode_segmentation::UnicodeSegmentation;
pub struct CursesBackend {
@ -35,7 +35,7 @@ impl CursesBackend {
}
impl Backend for CursesBackend {
fn draw<'a, I>(&mut self, content: I) -> io::Result<()>
fn draw<'a, I>(&mut self, content: I) -> Result<(), Error>
where
I: Iterator<Item = (u16, u16, &'a Cell)>,
{

@ -1,7 +1,6 @@
use std::io;
use crate::buffer::Cell;
use crate::layout::Rect;
use crate::Error;
#[cfg(feature = "rustbox")]
mod rustbox;
@ -27,14 +26,14 @@ mod test;
pub use self::test::TestBackend;
pub trait Backend {
fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
fn draw<'a, I>(&mut self, content: I) -> Result<(), Error>
where
I: Iterator<Item = (u16, u16, &'a Cell)>;
fn hide_cursor(&mut self) -> Result<(), io::Error>;
fn show_cursor(&mut self) -> Result<(), io::Error>;
fn get_cursor(&mut self) -> Result<(u16, u16), io::Error>;
fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), io::Error>;
fn clear(&mut self) -> Result<(), io::Error>;
fn size(&self) -> Result<Rect, io::Error>;
fn flush(&mut self) -> Result<(), io::Error>;
fn hide_cursor(&mut self) -> Result<(), Error>;
fn show_cursor(&mut self) -> Result<(), Error>;
fn get_cursor(&mut self) -> Result<(u16, u16), Error>;
fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), Error>;
fn clear(&mut self) -> Result<(), Error>;
fn size(&self) -> Result<Rect, Error>;
fn flush(&mut self) -> Result<(), Error>;
}

@ -3,6 +3,7 @@ use crate::{
buffer::Cell,
layout::Rect,
style::{Color, Modifier},
Error,
};
use std::{
fmt,
@ -43,36 +44,41 @@ where
W: Write,
{
/// Clears the entire screen and move the cursor to the top left of the screen
fn clear(&mut self) -> io::Result<()> {
write!(self.stdout, "{}", termion::clear::All)?;
write!(self.stdout, "{}", termion::cursor::Goto(1, 1))?;
self.stdout.flush()
fn clear(&mut self) -> Result<(), Error> {
write!(self.stdout, "{}", termion::clear::All).map_err(Error::Clear)?;
write!(self.stdout, "{}", termion::cursor::Goto(1, 1))
.map_err(|e| Error::MoveCursor(Box::new(e), (1, 1)))?;
self.stdout.flush().map_err(Error::Flush)
}
/// Hides cursor
fn hide_cursor(&mut self) -> io::Result<()> {
write!(self.stdout, "{}", termion::cursor::Hide)?;
self.stdout.flush()
fn hide_cursor(&mut self) -> Result<(), Error> {
write!(self.stdout, "{}", termion::cursor::Hide).map_err(Error::HideCursor)?;
self.stdout.flush().map_err(Error::Flush)
}
/// Shows cursor
fn show_cursor(&mut self) -> io::Result<()> {
write!(self.stdout, "{}", termion::cursor::Show)?;
self.stdout.flush()
fn show_cursor(&mut self) -> Result<(), Error> {
write!(self.stdout, "{}", termion::cursor::Show).map_err(Error::ShowCursor)?;
self.stdout.flush().map_err(Error::Flush)
}
/// Gets cursor position (0-based index)
fn get_cursor(&mut self) -> io::Result<(u16, u16)> {
termion::cursor::DetectCursorPos::cursor_pos(&mut self.stdout).map(|(x, y)| (x - 1, y - 1))
fn get_cursor(&mut self) -> Result<(u16, u16), Error> {
termion::cursor::DetectCursorPos::cursor_pos(&mut self.stdout)
.map(|(x, y)| (x - 1, y - 1))
.map_err(Error::GetCursosPos)
}
/// Sets cursor position (0-based index)
fn set_cursor(&mut self, x: u16, y: u16) -> io::Result<()> {
write!(self.stdout, "{}", termion::cursor::Goto(x + 1, y + 1))?;
self.stdout.flush()
fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), Error> {
let (new_x, new_y) = (x + 1, y + 1);
write!(self.stdout, "{}", termion::cursor::Goto(new_x, new_y))
.map_err(|e| Error::MoveCursor(Box::new(e), (new_x, new_y)))?;
self.stdout.flush().map_err(Error::Flush)
}
fn draw<'a, I>(&mut self, content: I) -> io::Result<()>
fn draw<'a, I>(&mut self, content: I) -> Result<(), Error>
where
I: Iterator<Item = (u16, u16, &'a Cell)>,
{
@ -86,7 +92,8 @@ where
for (x, y, cell) in content {
// Move the cursor if the previous location was not (x - 1, y)
if !matches!(last_pos, Some(p) if x == p.0 + 1 && y == p.1) {
write!(string, "{}", termion::cursor::Goto(x + 1, y + 1)).unwrap();
write!(string, "{}", termion::cursor::Goto(x + 1, y + 1))
.map_err(|e| Error::MoveCursor(Box::new(e), (x + 1, y + 1)))?;
}
last_pos = Some((x, y));
if cell.modifier != modifier {
@ -98,15 +105,15 @@ where
to: cell.modifier
}
)
.unwrap();
.map_err(Error::ModifierDiff)?;
modifier = cell.modifier;
}
if cell.fg != fg {
write!(string, "{}", Fg(cell.fg)).unwrap();
write!(string, "{}", Fg(cell.fg)).map_err(Error::DrawForeground)?;
fg = cell.fg;
}
if cell.bg != bg {
write!(string, "{}", Bg(cell.bg)).unwrap();
write!(string, "{}", Bg(cell.bg)).map_err(Error::DrawBackground)?;
bg = cell.bg;
}
string.push_str(&cell.symbol);
@ -119,16 +126,17 @@ where
Bg(Color::Reset),
termion::style::Reset,
)
.map_err(|e| Error::Draw(Box::new(e)))
}
/// Return the size of the terminal
fn size(&self) -> io::Result<Rect> {
let terminal = termion::terminal_size()?;
fn size(&self) -> Result<Rect, Error> {
let terminal = termion::terminal_size().map_err(Error::GetTerminalSize)?;
Ok(Rect::new(0, 0, terminal.0, terminal.1))
}
fn flush(&mut self) -> io::Result<()> {
self.stdout.flush()
fn flush(&mut self) -> Result<(), Error> {
self.stdout.flush().map_err(Error::Flush)
}
}

@ -2,8 +2,9 @@ use crate::{
backend::Backend,
buffer::{Buffer, Cell},
layout::Rect,
Error,
};
use std::{fmt::Write, io};
use std::fmt::Write;
use unicode_width::UnicodeWidthStr;
/// A backend used for the integration tests.
@ -106,7 +107,7 @@ impl TestBackend {
}
impl Backend for TestBackend {
fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
fn draw<'a, I>(&mut self, content: I) -> Result<(), Error>
where
I: Iterator<Item = (u16, u16, &'a Cell)>,
{
@ -117,21 +118,21 @@ impl Backend for TestBackend {
Ok(())
}
fn hide_cursor(&mut self) -> Result<(), io::Error> {
fn hide_cursor(&mut self) -> Result<(), Error> {
self.cursor = false;
Ok(())
}
fn show_cursor(&mut self) -> Result<(), io::Error> {
fn show_cursor(&mut self) -> Result<(), Error> {
self.cursor = true;
Ok(())
}
fn get_cursor(&mut self) -> Result<(u16, u16), io::Error> {
fn get_cursor(&mut self) -> Result<(u16, u16), Error> {
Ok(self.pos)
}
fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), io::Error> {
fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), Error> {
self.pos = (x, y);
Ok(())
}
@ -141,11 +142,11 @@ impl Backend for TestBackend {
Ok(())
}
fn size(&self) -> Result<Rect, io::Error> {
fn size(&self) -> Result<Rect, Error> {
Ok(Rect::new(0, 0, self.width, self.height))
}
fn flush(&mut self) -> Result<(), io::Error> {
fn flush(&mut self) -> Result<(), Error> {
Ok(())
}
}

@ -0,0 +1,38 @@
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("failed to draw to terminal")]
Draw(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
#[error("failed to flush")]
Flush(#[source] std::io::Error),
#[error("failed to get terminal size")]
GetTerminalSize(#[source] std::io::Error),
#[error("failed to draw background")]
DrawBackground(#[source] std::fmt::Error),
#[error("failed to draw foreground")]
DrawForeground(#[source] std::fmt::Error),
#[error("failed to construct modifier diff")]
ModifierDiff(#[source] std::fmt::Error),
#[error("failed to move cursor to position: {1:?}")]
MoveCursor(
#[source] Box<dyn std::error::Error + Send + Sync + 'static>,
(u16, u16),
),
#[error("failed to get cursor position")]
GetCursosPos(#[source] std::io::Error),
#[error("failed to show cursor")]
ShowCursor(#[source] std::io::Error),
#[error("failed to hide cursor")]
HideCursor(#[source] std::io::Error),
#[error("failed to clear terminal")]
Clear(#[source] std::io::Error),
}

@ -149,6 +149,7 @@
pub mod backend;
pub mod buffer;
mod error;
pub mod layout;
pub mod style;
pub mod symbols;
@ -156,4 +157,5 @@ pub mod terminal;
pub mod text;
pub mod widgets;
pub use self::error::Error;
pub use self::terminal::{Frame, Terminal, TerminalOptions, Viewport};

@ -3,8 +3,8 @@ use crate::{
buffer::Buffer,
layout::Rect,
widgets::{StatefulWidget, Widget},
Error,
};
use std::io;
#[derive(Debug, Clone, PartialEq)]
/// UNSTABLE
@ -176,7 +176,7 @@ where
{
/// Wrapper around Terminal initialization. Each buffer is initialized with a blank string and
/// default colors for the foreground and the background
pub fn new(backend: B) -> io::Result<Terminal<B>> {
pub fn new(backend: B) -> Result<Terminal<B>, Error> {
let size = backend.size()?;
Terminal::with_options(
backend,
@ -190,7 +190,7 @@ where
}
/// UNSTABLE
pub fn with_options(backend: B, options: TerminalOptions) -> io::Result<Terminal<B>> {
pub fn with_options(backend: B, options: TerminalOptions) -> Result<Terminal<B>, Error> {
Ok(Terminal {
backend,
buffers: [
@ -225,7 +225,7 @@ where
/// Obtains a difference between the previous and the current buffer and passes it to the
/// current backend for drawing.
pub fn flush(&mut self) -> io::Result<()> {
pub fn flush(&mut self) -> Result<(), Error> {
let previous_buffer = &self.buffers[1 - self.current];
let current_buffer = &self.buffers[self.current];
let updates = previous_buffer.diff(current_buffer);
@ -235,7 +235,7 @@ where
/// Updates the Terminal so that internal buffers match the requested size. Requested size will
/// be saved so the size can remain consistent when rendering.
/// This leads to a full clear of the screen.
pub fn resize(&mut self, area: Rect) -> io::Result<()> {
pub fn resize(&mut self, area: Rect) -> Result<(), Error> {
self.buffers[self.current].resize(area);
self.buffers[1 - self.current].resize(area);
self.viewport.area = area;
@ -243,7 +243,7 @@ where
}
/// Queries the backend for size and resizes if it doesn't match the previous size.
pub fn autoresize(&mut self) -> io::Result<()> {
pub fn autoresize(&mut self) -> Result<(), Error> {
if self.viewport.resize_behavior == ResizeBehavior::Auto {
let size = self.size()?;
if size != self.viewport.area {
@ -255,16 +255,17 @@ where
/// Synchronizes terminal size, calls the rendering closure, flushes the current internal state
/// and prepares for the next draw call.
pub fn draw<F>(&mut self, f: F) -> io::Result<CompletedFrame>
pub fn draw<F, E>(&mut self, f: F) -> Result<CompletedFrame, Error>
where
F: FnOnce(&mut Frame<B>),
E: std::error::Error + Send + Sync + 'static,
F: FnOnce(&mut Frame<B>) -> Result<(), E>,
{
// Autoresize - otherwise we get glitches if shrinking or potential desync between widgets
// and the terminal (if growing), which may OOB.
self.autoresize()?;
let mut frame = self.get_frame();
f(&mut frame);
f(&mut frame).map_err(|e| Error::Draw(Box::new(e)))?;
// We can't change the cursor position right away because we have to flush the frame to
// stdout first. But we also can't keep the frame around, since it holds a &mut to
// Terminal. Thus, we're taking the important data out of the Frame and dropping it.
@ -293,28 +294,28 @@ where
})
}
pub fn hide_cursor(&mut self) -> io::Result<()> {
pub fn hide_cursor(&mut self) -> Result<(), Error> {
self.backend.hide_cursor()?;
self.hidden_cursor = true;
Ok(())
}
pub fn show_cursor(&mut self) -> io::Result<()> {
pub fn show_cursor(&mut self) -> Result<(), Error> {
self.backend.show_cursor()?;
self.hidden_cursor = false;
Ok(())
}
pub fn get_cursor(&mut self) -> io::Result<(u16, u16)> {
pub fn get_cursor(&mut self) -> Result<(u16, u16), Error> {
self.backend.get_cursor()
}
pub fn set_cursor(&mut self, x: u16, y: u16) -> io::Result<()> {
pub fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), Error> {
self.backend.set_cursor(x, y)
}
/// Clear the terminal and force a full redraw on the next draw call.
pub fn clear(&mut self) -> io::Result<()> {
pub fn clear(&mut self) -> Result<(), Error> {
self.backend.clear()?;
// Reset the back buffer to make sure the next update will redraw everything.
self.buffers[1 - self.current].reset();
@ -322,7 +323,7 @@ where
}
/// Queries the real size of the backend.
pub fn size(&self) -> io::Result<Rect> {
pub fn size(&self) -> Result<Rect, Error> {
self.backend.size()
}
}

Loading…
Cancel
Save