Skip to main content

tix/
error.rs

1//! Typed error surface for `tix`.
2//!
3//! Every fallible operation in the crate reports through [`TixError`],
4//! and the [`Result`] alias is the canonical return type used across the
5//! module boundaries. Consumers match on the variants rather than on
6//! ad-hoc strings; context strings carry human-readable detail.
7
8/// The crate-wide result type.
9pub type Result<T> = std::result::Result<T, TixError>;
10
11/// Typed errors raised by `tix`.
12#[derive(Debug, thiserror::Error)]
13pub enum TixError {
14    /// An I/O operation failed (read/write/rename/remove).
15    #[error("io error: {0}")]
16    Io(#[from] std::io::Error),
17
18    /// `tix.toml` or other config surface is invalid.
19    #[error("config error: {0}")]
20    Config(String),
21
22    /// A data file (issue frontmatter, config) could not be parsed.
23    #[error("parse error: {0}")]
24    Parse(String),
25
26    /// The requested issue/config path does not exist.
27    #[error("not found: {0}")]
28    NotFound(String),
29
30    /// User input failed validation.
31    #[error("invalid input: {0}")]
32    InvalidInput(String),
33
34    /// Launching or interacting with the `$EDITOR` failed.
35    #[error("editor error: {0}")]
36    Editor(String),
37
38    /// An external tool (`glab`, `linear-cli`, ...) failed.
39    #[error("external tool error: {0}")]
40    External(String),
41
42    /// Spec Kitty bridge error (`kitty-specs/` read, parse, or link).
43    #[error("kitty error: {0}")]
44    Kitty(String),
45}
46
47impl TixError {
48    /// Wrap an error with a path prefix for context.
49    pub fn with_path(self, path: impl std::fmt::Display) -> Self {
50        match self {
51            TixError::Io(e) => TixError::Io(e),
52            TixError::Config(msg) => TixError::Config(format!("{path}: {msg}")),
53            TixError::Parse(msg) => TixError::Parse(format!("{path}: {msg}")),
54            TixError::NotFound(msg) => TixError::NotFound(format!("{path}: {msg}")),
55            other => other,
56        }
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn result_alias_resolves_to_tix_error() {
66        let err: TixError = TixError::InvalidInput("nope".into());
67        let r: Result<()> = Err(err);
68        assert!(r.is_err());
69    }
70
71    #[test]
72    fn config_error_carries_path_when_prefixed() {
73        let e = TixError::Config("bad keys".into()).with_path("tix.toml");
74        assert_eq!(e.to_string(), "config error: tix.toml: bad keys");
75    }
76}