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}