tix-tracker 0.3.0

A terminal issue tracker: markdown + YAML frontmatter issues with a kanban TUI and CLI
Documentation
//! Typed error surface for `tix`.
//!
//! Every fallible operation in the crate reports through [`TixError`],
//! and the [`Result`] alias is the canonical return type used across the
//! module boundaries. Consumers match on the variants rather than on
//! ad-hoc strings; context strings carry human-readable detail.

/// The crate-wide result type.
pub type Result<T> = std::result::Result<T, TixError>;

/// Typed errors raised by `tix`.
#[derive(Debug, thiserror::Error)]
pub enum TixError {
    /// An I/O operation failed (read/write/rename/remove).
    #[error("io error: {0}")]
    Io(#[from] std::io::Error),

    /// `tix.toml` or other config surface is invalid.
    #[error("config error: {0}")]
    Config(String),

    /// A data file (issue frontmatter, config) could not be parsed.
    #[error("parse error: {0}")]
    Parse(String),

    /// The requested issue/config path does not exist.
    #[error("not found: {0}")]
    NotFound(String),

    /// User input failed validation.
    #[error("invalid input: {0}")]
    InvalidInput(String),

    /// Launching or interacting with the `$EDITOR` failed.
    #[error("editor error: {0}")]
    Editor(String),

    /// An external tool (`glab`, `linear-cli`, ...) failed.
    #[error("external tool error: {0}")]
    External(String),

    /// Spec Kitty bridge error (`kitty-specs/` read, parse, or link).
    #[error("kitty error: {0}")]
    Kitty(String),
}

impl TixError {
    /// Wrap an error with a path prefix for context.
    pub fn with_path(self, path: impl std::fmt::Display) -> Self {
        match self {
            TixError::Io(e) => TixError::Io(e),
            TixError::Config(msg) => TixError::Config(format!("{path}: {msg}")),
            TixError::Parse(msg) => TixError::Parse(format!("{path}: {msg}")),
            TixError::NotFound(msg) => TixError::NotFound(format!("{path}: {msg}")),
            other => other,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn result_alias_resolves_to_tix_error() {
        let err: TixError = TixError::InvalidInput("nope".into());
        let r: Result<()> = Err(err);
        assert!(r.is_err());
    }

    #[test]
    fn config_error_carries_path_when_prefixed() {
        let e = TixError::Config("bad keys".into()).with_path("tix.toml");
        assert_eq!(e.to_string(), "config error: tix.toml: bad keys");
    }
}