1use super::{GitError, ProviderError};
7use thiserror::Error;
8
9#[derive(Error, Debug)]
14pub enum AppError {
15 #[error("Configuration error: {0}")]
17 Config(String),
18
19 #[error("Authentication failed: {0}")]
21 Auth(String),
22
23 #[error("Provider error: {0}")]
25 Provider(
26 #[from]
27 #[source]
28 ProviderError,
29 ),
30
31 #[error("Git error: {0}")]
33 Git(
34 #[from]
35 #[source]
36 GitError,
37 ),
38
39 #[error("IO error: {0}")]
41 Io(
42 #[from]
43 #[source]
44 std::io::Error,
45 ),
46
47 #[error("Path error: {0}")]
49 Path(String),
50
51 #[error("Operation cancelled by user")]
53 Cancelled,
54
55 #[error("Operation interrupted")]
57 Interrupted,
58
59 #[error("{0}")]
61 Other(
62 #[from]
63 #[source]
64 anyhow::Error,
65 ),
66}
67
68impl AppError {
69 pub fn config(message: impl Into<String>) -> Self {
71 AppError::Config(message.into())
72 }
73
74 pub fn auth(message: impl Into<String>) -> Self {
76 AppError::Auth(message.into())
77 }
78
79 pub fn path(message: impl Into<String>) -> Self {
81 AppError::Path(message.into())
82 }
83
84 pub fn is_retryable(&self) -> bool {
86 match self {
87 AppError::Provider(e) => e.is_retryable(),
88 AppError::Git(e) => e.is_retryable(),
89 AppError::Io(e) => {
90 matches!(
92 e.kind(),
93 std::io::ErrorKind::TimedOut
94 | std::io::ErrorKind::Interrupted
95 | std::io::ErrorKind::WouldBlock
96 )
97 }
98 _ => false,
99 }
100 }
101
102 pub fn exit_code(&self) -> i32 {
104 match self {
105 AppError::Config(_) => 2,
106 AppError::Auth(_) => 3,
107 AppError::Provider(_) => 4,
108 AppError::Git(_) => 5,
109 AppError::Io(_) => 6,
110 AppError::Path(_) => 7,
111 AppError::Cancelled => 130, AppError::Interrupted => 130,
113 AppError::Other(_) => 1,
114 }
115 }
116
117 pub fn suggested_action(&self) -> &str {
119 match self {
120 AppError::Config(_) => {
121 "Check your config file for syntax errors, or run 'gisa init' to create one"
122 }
123 AppError::Auth(_) => "Run 'gh auth login' to authenticate with GitHub CLI",
124 AppError::Provider(e) => e.suggested_action(),
125 AppError::Git(e) => e.suggested_action(),
126 AppError::Io(_) => "Check file permissions and disk space",
127 AppError::Path(_) => "Check that the path exists and is accessible",
128 AppError::Cancelled | AppError::Interrupted => "Re-run the command to continue",
129 AppError::Other(_) => "Check the error message and try again",
130 }
131 }
132}
133
134pub type Result<T> = std::result::Result<T, AppError>;
136
137#[cfg(test)]
138#[path = "app_tests.rs"]
139mod tests;