Skip to main content

contract_cli/
error.rs

1use thiserror::Error;
2
3#[derive(Error, Debug)]
4pub enum AppError {
5    #[error("Invalid input: {0}")]
6    InvalidInput(String),
7
8    #[error("Config error: {0}")]
9    Config(String),
10
11    #[error("Not found: {0}")]
12    NotFound(String),
13
14    #[error("Ambiguous: {0}")]
15    Ambiguous(String),
16
17    #[error("IO error: {0}")]
18    Io(#[from] std::io::Error),
19
20    #[error("Database error: {0}")]
21    Db(#[from] rusqlite::Error),
22
23    #[error("Serde error: {0}")]
24    Serde(#[from] serde_json::Error),
25
26    #[error("Render error: {0}")]
27    Render(String),
28
29    #[error("{0}")]
30    Transient(String),
31
32    #[error("Rate limited: {0}")]
33    RateLimited(String),
34
35    #[error(transparent)]
36    Core(#[from] finance_core::error::CoreError),
37
38    #[error("{0}")]
39    Other(String),
40}
41
42impl AppError {
43    pub fn exit_code(&self) -> i32 {
44        match self {
45            Self::InvalidInput(_) | Self::Ambiguous(_) | Self::NotFound(_) => 3,
46            Self::Config(_) => 2,
47            Self::RateLimited(_) => 4,
48            _ => 1,
49        }
50    }
51
52    pub fn error_code(&self) -> &'static str {
53        match self {
54            Self::InvalidInput(_) => "invalid_input",
55            Self::Ambiguous(_) => "ambiguous",
56            Self::Config(_) => "config_error",
57            Self::NotFound(_) => "not_found",
58            Self::Io(_) => "io_error",
59            Self::Db(_) => "db_error",
60            Self::Serde(_) => "serde_error",
61            Self::Render(_) => "render_error",
62            Self::Transient(_) => "transient_error",
63            Self::RateLimited(_) => "rate_limited",
64            Self::Core(_) => "core_error",
65            Self::Other(_) => "other",
66        }
67    }
68
69    pub fn suggestion(&self) -> &'static str {
70        match self {
71            Self::InvalidInput(_) => "Check arguments with: contract --help",
72            Self::Ambiguous(_) => "Use the full slug or a more specific identifier",
73            Self::Config(_) => "Check config with: contract config show",
74            Self::NotFound(_) => "List available entities with: contract <kind> list",
75            Self::Io(_) => "Retry the command",
76            Self::Db(_) => "Check database integrity: contract doctor",
77            Self::Serde(_) => "Retry the command; if it persists, run: contract doctor",
78            Self::Render(_) => "Typst render failed — run: contract doctor",
79            Self::Transient(_) => "Retry the command",
80            Self::RateLimited(_) => "Wait a moment and retry",
81            Self::Core(_) => "Check shared accounting state with: contract doctor",
82            Self::Other(_) => "Retry the command; if it persists, run: contract doctor",
83        }
84    }
85}
86
87pub type Result<T> = std::result::Result<T, AppError>;