use std::time::Duration;
use crate::Screen;
pub type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error(
"timed out after {timeout:?} while waiting for {waiting_for}\n\
--- screen at timeout ---\n{screen}"
)]
Timeout {
waiting_for: String,
timeout: Duration,
screen: Screen,
},
#[error(
"terminal closed (EOF) while waiting for {waiting_for}\n\
--- final screen ---\n{screen}"
)]
Eof {
waiting_for: String,
screen: Screen,
},
#[error("failed to spawn `{command}`: {reason}")]
Spawn {
command: String,
reason: String,
},
#[error("PTY error: {0}")]
Pty(String),
#[error("i/o error: {0}")]
Io(#[from] std::io::Error),
}
impl Error {
#[must_use]
pub fn screen(&self) -> Option<&Screen> {
match self {
Error::Timeout { screen, .. } | Error::Eof { screen, .. } => Some(screen),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::screen::{Cell, Style};
fn tiny_screen() -> Screen {
let mut cells = Vec::new();
for ch in ['o', 'k'] {
cells.push(Cell::new(ch.to_string(), Style::default(), false, false));
}
cells.push(Cell::new(String::new(), Style::default(), false, false));
Screen::from_parts(3, 1, 0, 2, true, cells)
}
#[test]
fn timeout_display_embeds_screen_dump() {
let err = Error::Timeout {
waiting_for: "text \"ready\"".into(),
timeout: Duration::from_millis(250),
screen: tiny_screen(),
};
let msg = err.to_string();
assert!(msg.contains("timed out after 250ms"), "{msg}");
assert!(msg.contains("--- screen at timeout ---"), "{msg}");
assert!(msg.contains("size: 3x1 cursor: 0,2"), "{msg}");
assert!(msg.contains("\nok"), "{msg}");
assert_eq!(err.screen().unwrap().size(), (3, 1));
}
}