Skip to main content

turbo_vision/core/
error.rs

1// (C) 2025 - Enzo Lombardi
2
3//! Error types for Turbo Vision operations.
4//!
5//! This module provides the main error types used throughout the library,
6//! with proper backtrace support and context preservation.
7
8use std::backtrace::Backtrace;
9use std::fmt::{Display, Formatter};
10use std::path::PathBuf;
11
12/// Error type for Turbo Vision operations.
13///
14/// Wraps error kinds with backtrace support for debugging.
15///
16/// # Examples
17///
18/// ```rust,no_run
19/// use turbo_vision::core::error::Result;
20///
21/// fn init_app() -> Result<()> {
22///     // Operations that can fail
23///     Ok(())
24/// }
25/// ```
26#[derive(Debug)]
27pub struct TurboVisionError {
28    kind: ErrorKind,
29    backtrace: Backtrace,
30}
31
32/// The specific kind of error that occurred.
33#[derive(Debug)]
34#[allow(
35    dead_code,
36    reason = "error kinds reserved for the internal error API, not all constructed yet"
37)]
38pub(crate) enum ErrorKind {
39    /// I/O operation failed
40    Io(std::io::Error),
41
42    /// Terminal initialization failed
43    TerminalInit(String),
44
45    /// Invalid input provided
46    InvalidInput(String),
47
48    /// Parse error
49    Parse(String),
50
51    /// File operation failed
52    FileOperation {
53        path: PathBuf,
54        source: std::io::Error,
55    },
56}
57
58impl TurboVisionError {
59    pub(crate) fn new(kind: ErrorKind) -> Self {
60        Self {
61            kind,
62            backtrace: Backtrace::capture(),
63        }
64    }
65
66    /// Creates a terminal initialization error.
67    #[allow(dead_code, reason = "reserved error constructor, no call site yet")]
68    pub(crate) fn terminal_init(msg: impl Into<String>) -> Self {
69        Self::new(ErrorKind::TerminalInit(msg.into()))
70    }
71
72    /// Creates an invalid input error.
73    #[allow(dead_code, reason = "reserved error constructor, no call site yet")]
74    pub(crate) fn invalid_input(msg: impl Into<String>) -> Self {
75        Self::new(ErrorKind::InvalidInput(msg.into()))
76    }
77
78    /// Creates a parse error.
79    #[allow(dead_code, reason = "reserved error constructor, no call site yet")]
80    pub(crate) fn parse(msg: impl Into<String>) -> Self {
81        Self::new(ErrorKind::Parse(msg.into()))
82    }
83
84    /// Creates a file operation error.
85    #[allow(dead_code, reason = "reserved error constructor, no call site yet")]
86    pub(crate) fn file_operation(path: impl Into<PathBuf>, source: std::io::Error) -> Self {
87        Self::new(ErrorKind::FileOperation {
88            path: path.into(),
89            source,
90        })
91    }
92
93    /// Returns `true` if this error is an I/O error.
94    pub fn is_io(&self) -> bool {
95        matches!(self.kind, ErrorKind::Io(_))
96    }
97
98    /// Returns `true` if this error is a terminal initialization error.
99    pub fn is_terminal_init(&self) -> bool {
100        matches!(self.kind, ErrorKind::TerminalInit(_))
101    }
102
103    /// Returns `true` if this error is an invalid input error.
104    pub fn is_invalid_input(&self) -> bool {
105        matches!(self.kind, ErrorKind::InvalidInput(_))
106    }
107
108    /// Returns `true` if this error is a parse error.
109    pub fn is_parse(&self) -> bool {
110        matches!(self.kind, ErrorKind::Parse(_))
111    }
112
113    /// Returns `true` if this error is a file operation error.
114    pub fn is_file_operation(&self) -> bool {
115        matches!(self.kind, ErrorKind::FileOperation { .. })
116    }
117
118    /// Returns the file path if this is a file operation error.
119    pub fn file_path(&self) -> Option<&std::path::Path> {
120        match &self.kind {
121            ErrorKind::FileOperation { path, .. } => Some(path),
122            _ => None,
123        }
124    }
125}
126
127impl Display for TurboVisionError {
128    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
129        match &self.kind {
130            ErrorKind::Io(e) => write!(f, "I/O error: {e}")?,
131            ErrorKind::TerminalInit(msg) => write!(f, "Terminal initialization failed: {msg}")?,
132            ErrorKind::InvalidInput(msg) => write!(f, "Invalid input: {msg}")?,
133            ErrorKind::Parse(msg) => write!(f, "Parse error: {msg}")?,
134            ErrorKind::FileOperation { path, source } => write!(
135                f,
136                "File operation failed for '{}': {}",
137                path.display(),
138                source
139            )?,
140        }
141
142        // Include backtrace if captured
143        if self.backtrace.status() == std::backtrace::BacktraceStatus::Captured {
144            write!(f, "\n\nBacktrace:\n{}", self.backtrace)?;
145        }
146
147        Ok(())
148    }
149}
150
151impl std::error::Error for TurboVisionError {
152    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
153        match &self.kind {
154            ErrorKind::Io(e) => Some(e),
155            ErrorKind::FileOperation { source, .. } => Some(source),
156            _ => None,
157        }
158    }
159}
160
161impl From<std::io::Error> for TurboVisionError {
162    fn from(e: std::io::Error) -> Self {
163        Self::new(ErrorKind::Io(e))
164    }
165}
166
167/// Result type for Turbo Vision operations.
168///
169/// This is a type alias for `Result<T, TurboVisionError>`.
170pub type Result<T> = std::result::Result<T, TurboVisionError>;