use std::backtrace::Backtrace;
use std::fmt::{Display, Formatter};
use std::path::PathBuf;
#[derive(Debug)]
pub struct TurboVisionError {
kind: ErrorKind,
backtrace: Backtrace,
}
#[derive(Debug)]
#[allow(dead_code)]
pub(crate) enum ErrorKind {
Io(std::io::Error),
TerminalInit(String),
InvalidInput(String),
Parse(String),
FileOperation {
path: PathBuf,
source: std::io::Error,
},
}
impl TurboVisionError {
pub(crate) fn new(kind: ErrorKind) -> Self {
Self {
kind,
backtrace: Backtrace::capture(),
}
}
#[allow(dead_code)]
pub(crate) fn terminal_init(msg: impl Into<String>) -> Self {
Self::new(ErrorKind::TerminalInit(msg.into()))
}
#[allow(dead_code)]
pub(crate) fn invalid_input(msg: impl Into<String>) -> Self {
Self::new(ErrorKind::InvalidInput(msg.into()))
}
#[allow(dead_code)]
pub(crate) fn parse(msg: impl Into<String>) -> Self {
Self::new(ErrorKind::Parse(msg.into()))
}
#[allow(dead_code)]
pub(crate) fn file_operation(path: impl Into<PathBuf>, source: std::io::Error) -> Self {
Self::new(ErrorKind::FileOperation {
path: path.into(),
source,
})
}
pub fn is_io(&self) -> bool {
matches!(self.kind, ErrorKind::Io(_))
}
pub fn is_terminal_init(&self) -> bool {
matches!(self.kind, ErrorKind::TerminalInit(_))
}
pub fn is_invalid_input(&self) -> bool {
matches!(self.kind, ErrorKind::InvalidInput(_))
}
pub fn is_parse(&self) -> bool {
matches!(self.kind, ErrorKind::Parse(_))
}
pub fn is_file_operation(&self) -> bool {
matches!(self.kind, ErrorKind::FileOperation { .. })
}
pub fn file_path(&self) -> Option<&std::path::Path> {
match &self.kind {
ErrorKind::FileOperation { path, .. } => Some(path),
_ => None,
}
}
}
impl Display for TurboVisionError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match &self.kind {
ErrorKind::Io(e) => write!(f, "I/O error: {}", e)?,
ErrorKind::TerminalInit(msg) => write!(f, "Terminal initialization failed: {}", msg)?,
ErrorKind::InvalidInput(msg) => write!(f, "Invalid input: {}", msg)?,
ErrorKind::Parse(msg) => write!(f, "Parse error: {}", msg)?,
ErrorKind::FileOperation { path, source } => {
write!(
f,
"File operation failed for '{}': {}",
path.display(),
source
)?
}
}
if self.backtrace.status() == std::backtrace::BacktraceStatus::Captured {
write!(f, "\n\nBacktrace:\n{}", self.backtrace)?;
}
Ok(())
}
}
impl std::error::Error for TurboVisionError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self.kind {
ErrorKind::Io(e) => Some(e),
ErrorKind::FileOperation { source, .. } => Some(source),
_ => None,
}
}
}
impl From<std::io::Error> for TurboVisionError {
fn from(e: std::io::Error) -> Self {
Self::new(ErrorKind::Io(e))
}
}
pub type Result<T> = std::result::Result<T, TurboVisionError>;