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("Migration error: {0}")]
24 Migration(#[from] refinery::Error),
25
26 #[error("Serde error: {0}")]
27 Serde(#[from] serde_json::Error),
28
29 #[error("Typst render error: {0}")]
30 Render(String),
31
32 #[error("Other: {0}")]
33 Other(String),
34}
35
36impl AppError {
37 pub fn exit_code(&self) -> i32 {
38 match self {
39 Self::InvalidInput(_) | Self::Ambiguous(_) => 3,
40 Self::Config(_) => 2,
41 Self::NotFound(_) => 3,
42 Self::Io(_) | Self::Render(_) | Self::Other(_) => 1,
43 Self::Db(_) | Self::Migration(_) | Self::Serde(_) => 1,
44 }
45 }
46
47 pub fn error_code(&self) -> &'static str {
48 match self {
49 Self::InvalidInput(_) => "invalid_input",
50 Self::Ambiguous(_) => "ambiguous",
51 Self::Config(_) => "config_error",
52 Self::NotFound(_) => "not_found",
53 Self::Io(_) => "io_error",
54 Self::Db(_) => "db_error",
55 Self::Migration(_) => "migration_error",
56 Self::Serde(_) => "serde_error",
57 Self::Render(_) => "render_error",
58 Self::Other(_) => "other",
59 }
60 }
61
62 pub fn suggestion(&self) -> &'static str {
63 match self {
64 Self::InvalidInput(_) => "Check arguments with: invoice --help",
65 Self::Ambiguous(_) => "Use the full id or a more specific slug",
66 Self::Config(_) => "Check config with: invoice config show",
67 Self::NotFound(_) => "List available entities with: invoice <kind> list",
68 Self::Io(_) => "Retry the command",
69 Self::Db(_) => "Check database integrity: invoice doctor",
70 Self::Migration(_) => "Database migration failed — check doctor",
71 Self::Serde(_) => "Malformed data — check input",
72 Self::Render(_) => "Typst render failed — run: invoice doctor",
73 Self::Other(_) => "",
74 }
75 }
76}
77
78pub type Result<T> = std::result::Result<T, AppError>;