Skip to main content

chronicle/
error.rs

1use snafu::Snafu;
2use std::path::PathBuf;
3
4#[derive(Debug, Snafu)]
5#[snafu(visibility(pub), module(chronicle_error))]
6pub enum ChronicleError {
7    #[snafu(display("not a git repository: {}", path.display()))]
8    NotARepository {
9        path: PathBuf,
10        #[snafu(implicit)]
11        location: snafu::Location,
12    },
13
14    #[snafu(display("chronicle not initialized (run `git chronicle init` first)"))]
15    NotInitialized {
16        #[snafu(implicit)]
17        location: snafu::Location,
18    },
19
20    #[snafu(display("git error: {source}"))]
21    Git {
22        source: GitError,
23        #[snafu(implicit)]
24        location: snafu::Location,
25    },
26
27    #[snafu(display("provider error: {source}"))]
28    Provider {
29        source: ProviderError,
30        #[snafu(implicit)]
31        location: snafu::Location,
32    },
33
34    #[snafu(display("agent error: {source}"))]
35    Agent {
36        source: AgentError,
37        #[snafu(implicit)]
38        location: snafu::Location,
39    },
40
41    #[snafu(display("AST error: {source}"))]
42    Ast {
43        source: AstError,
44        #[snafu(implicit)]
45        location: snafu::Location,
46    },
47
48    #[snafu(display("config error: {message}"))]
49    Config {
50        message: String,
51        #[snafu(implicit)]
52        location: snafu::Location,
53    },
54
55    #[snafu(display("IO error: {source}"))]
56    Io {
57        source: std::io::Error,
58        #[snafu(implicit)]
59        location: snafu::Location,
60    },
61
62    #[snafu(display("JSON error: {source}"))]
63    Json {
64        source: serde_json::Error,
65        #[snafu(implicit)]
66        location: snafu::Location,
67    },
68
69    #[snafu(display("annotation validation error: {message}"))]
70    Validation {
71        message: String,
72        #[snafu(implicit)]
73        location: snafu::Location,
74    },
75
76    #[snafu(display("setup error: {source}"))]
77    Setup {
78        source: SetupError,
79        #[snafu(implicit)]
80        location: snafu::Location,
81    },
82}
83
84#[derive(Debug, Snafu)]
85#[snafu(visibility(pub), module(git_error))]
86pub enum GitError {
87    #[snafu(display("git command failed: {message}"))]
88    CommandFailed {
89        message: String,
90        #[snafu(implicit)]
91        location: snafu::Location,
92    },
93
94    #[snafu(display("commit not found: {sha}"))]
95    CommitNotFound {
96        sha: String,
97        #[snafu(implicit)]
98        location: snafu::Location,
99    },
100
101    #[snafu(display("file not found: {path} at {commit}"))]
102    FileNotFound {
103        path: String,
104        commit: String,
105        #[snafu(implicit)]
106        location: snafu::Location,
107    },
108
109    #[snafu(display("notes ref missing: {refname}"))]
110    NotesRefMissing {
111        refname: String,
112        #[snafu(implicit)]
113        location: snafu::Location,
114    },
115
116    #[snafu(display("diff parse error: {message}"))]
117    DiffParse {
118        message: String,
119        #[snafu(implicit)]
120        location: snafu::Location,
121    },
122
123    #[snafu(display("IO error: {source}"))]
124    Io {
125        source: std::io::Error,
126        #[snafu(implicit)]
127        location: snafu::Location,
128    },
129}
130
131#[derive(Debug, Snafu)]
132#[snafu(visibility(pub), module(provider_error))]
133pub enum ProviderError {
134    #[snafu(display("no credentials found for any provider"))]
135    NoCredentials {
136        #[snafu(implicit)]
137        location: snafu::Location,
138    },
139
140    #[snafu(display("authentication failed: {message}"))]
141    AuthFailed {
142        message: String,
143        #[snafu(implicit)]
144        location: snafu::Location,
145    },
146
147    #[snafu(display("rate limited, retry after {retry_after_secs}s"))]
148    RateLimited {
149        retry_after_secs: u64,
150        #[snafu(implicit)]
151        location: snafu::Location,
152    },
153
154    #[snafu(display("request timeout"))]
155    Timeout {
156        #[snafu(implicit)]
157        location: snafu::Location,
158    },
159
160    #[snafu(display("API error: {message}"))]
161    Api {
162        message: String,
163        #[snafu(implicit)]
164        location: snafu::Location,
165    },
166
167    #[snafu(display("failed to parse response: {message}"))]
168    ParseResponse {
169        message: String,
170        #[snafu(implicit)]
171        location: snafu::Location,
172    },
173
174    #[snafu(display("HTTP error: {source}, at {location}"))]
175    Http {
176        source: Box<ureq::Transport>,
177        #[snafu(implicit)]
178        location: snafu::Location,
179    },
180
181    #[snafu(display("retries exhausted after {attempts} attempts"))]
182    RetriesExhausted {
183        attempts: u32,
184        #[snafu(implicit)]
185        location: snafu::Location,
186    },
187}
188
189#[derive(Debug, Snafu)]
190#[snafu(visibility(pub), module(agent_error))]
191pub enum AgentError {
192    #[snafu(display("provider error: {source}"))]
193    Provider {
194        source: ProviderError,
195        #[snafu(implicit)]
196        location: snafu::Location,
197    },
198
199    #[snafu(display("no annotations emitted by agent"))]
200    NoAnnotations {
201        #[snafu(implicit)]
202        location: snafu::Location,
203    },
204
205    #[snafu(display("max turns exceeded ({turns})"))]
206    MaxTurnsExceeded {
207        turns: u32,
208        #[snafu(implicit)]
209        location: snafu::Location,
210    },
211
212    #[snafu(display("invalid annotation: {message}"))]
213    InvalidAnnotation {
214        message: String,
215        #[snafu(implicit)]
216        location: snafu::Location,
217    },
218
219    #[snafu(display("git error: {source}"))]
220    Git {
221        source: GitError,
222        #[snafu(implicit)]
223        location: snafu::Location,
224    },
225
226    #[snafu(display("JSON error: {source}"))]
227    Json {
228        source: serde_json::Error,
229        #[snafu(implicit)]
230        location: snafu::Location,
231    },
232}
233
234#[derive(Debug, Snafu)]
235#[snafu(visibility(pub), module(ast_error))]
236pub enum AstError {
237    #[snafu(display("unsupported language: {extension}"))]
238    UnsupportedLanguage {
239        extension: String,
240        #[snafu(implicit)]
241        location: snafu::Location,
242    },
243
244    #[snafu(display("parse failed for {path}: {message}"))]
245    ParseFailed {
246        path: String,
247        message: String,
248        #[snafu(implicit)]
249        location: snafu::Location,
250    },
251
252    #[snafu(display("tree-sitter error: {message}"))]
253    TreeSitter {
254        message: String,
255        #[snafu(implicit)]
256        location: snafu::Location,
257    },
258}
259
260#[derive(Debug, Snafu)]
261#[snafu(visibility(pub), module(setup_error))]
262pub enum SetupError {
263    #[snafu(display("home directory not found, at {location}"))]
264    NoHomeDirectory {
265        #[snafu(implicit)]
266        location: snafu::Location,
267    },
268
269    #[snafu(display("git-chronicle binary not found on PATH, at {location}"))]
270    BinaryNotFound {
271        #[snafu(implicit)]
272        location: snafu::Location,
273    },
274
275    #[snafu(display("failed to write {path}: {source}, at {location}"))]
276    WriteFile {
277        path: String,
278        #[snafu(source)]
279        source: std::io::Error,
280        #[snafu(implicit)]
281        location: snafu::Location,
282    },
283
284    #[snafu(display("failed to read file {path}: {source}, at {location}"))]
285    ReadFile {
286        path: String,
287        #[snafu(source)]
288        source: std::io::Error,
289        #[snafu(implicit)]
290        location: snafu::Location,
291    },
292
293    #[snafu(display("failed to read user config: {source}, at {location}"))]
294    ReadConfig {
295        #[snafu(source)]
296        source: toml::de::Error,
297        #[snafu(implicit)]
298        location: snafu::Location,
299    },
300
301    #[snafu(display("failed to write user config: {source}, at {location}"))]
302    WriteConfig {
303        #[snafu(source)]
304        source: toml::ser::Error,
305        #[snafu(implicit)]
306        location: snafu::Location,
307    },
308
309    #[snafu(display(
310        "Claude CLI not found — install Claude Code or select a different provider, at {location}"
311    ))]
312    ClaudeCliNotFound {
313        #[snafu(implicit)]
314        location: snafu::Location,
315    },
316
317    #[snafu(display("ANTHROPIC_API_KEY environment variable not set, at {location}"))]
318    ApiKeyNotSet {
319        #[snafu(implicit)]
320        location: snafu::Location,
321    },
322
323    #[snafu(display("interactive input error: {source}, at {location}"))]
324    InteractiveInput {
325        #[snafu(source)]
326        source: std::io::Error,
327        #[snafu(implicit)]
328        location: snafu::Location,
329    },
330}
331
332pub type Result<T, E = ChronicleError> = std::result::Result<T, E>;