Skip to main content

bonds_core/
error.rs

1use thiserror::Error;
2
3/// Coarse error categories used by the CLI to pick a color.
4/// This is not an exhaustive list of all error cases, just broad buckets for consistent CLI rendering.
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum ErrorKind {
7    /// User provided invalid or unusable data (e.g. path, timestamp, identifier).
8    Input,
9    /// No bond matched the provided identifier or path.
10    NotFound,
11    /// A bond already exists for the provided identifier or target path.
12    Conflict,
13    /// An unexpected error occurred at runtime (e.g. IO, database, serialization).
14    Runtime,
15    /// Configuration file parse or write error.
16    Config,
17}
18
19/// Errors surfaced by the API
20/// This is the main error type returned by all public methods in this crate. It wraps various underlying error types (IO, SQLite, serialization) as well as domain-specific errors like NotFound or AlreadyExists. The `kind()` method allows callers to categorize errors for consistent CLI rendering.
21#[derive(Error, Debug)]
22pub enum BondError {
23    /// Filesystem or OS I/O failure.
24    #[error("IO error: {0}")]
25    Io(#[from] std::io::Error),
26
27    /// SQLite query/connection failure.
28    #[error("SQLite error: {0}")]
29    Sqlite(#[from] rusqlite::Error),
30
31    /// JSON serialization/deserialization failure.
32    #[error("Serialization error: {0}")]
33    Serde(#[from] serde_json::Error),
34
35    /// A conflicting bond record already exists.
36    #[error("Bond already exists")]
37    AlreadyExists,
38
39    /// The requested target path already exists and cannot be replaced.
40    #[error("Target already exists: {0}")]
41    TargetExists(String),
42
43    /// No bond matched the provided identifier.
44    #[error("Bond not found: {0}")]
45    NotFound(String),
46
47    /// A provided path is invalid or unusable.
48    #[error("Invalid path: {0}")]
49    InvalidPath(String),
50
51    /// Configuration file parse or write error.
52    #[error("config error: {0}")]
53    Config(String),
54
55    /// The identifier prefix matched more than one bond.
56    #[error("ambiguous identifier '{0}': use more characters")]
57    AmbiguousId(String),
58
59    /// Failed to parse or interpret a timestamp.
60    #[error("invalid timestamp: {0}")]
61    InvalidTimestamp(String),
62}
63
64impl BondError {
65    /// Return a broad category so the CLI can render the error consistently.
66    /// This is not meant to be an exhaustive categorization of all error cases, just a few buckets that map to different CLI colors and exit codes.
67    pub fn kind(&self) -> ErrorKind {
68        match self {
69            Self::InvalidPath(_) | Self::InvalidTimestamp(_) | Self::AmbiguousId(_) => {
70                ErrorKind::Input
71            }
72            Self::NotFound(_) => ErrorKind::NotFound,
73            Self::AlreadyExists | Self::TargetExists(_) => ErrorKind::Conflict,
74            Self::Io(_) | Self::Sqlite(_) | Self::Serde(_) => ErrorKind::Runtime,
75            Self::Config(_) => ErrorKind::Config,
76        }
77    }
78}