ag_git/error.rs
1use std::time::Duration;
2
3use crate::rebase;
4
5/// Typed error returned by git infrastructure operations.
6///
7/// Wraps command execution failures, output parsing issues, and I/O errors so
8/// callers can distinguish error categories without parsing opaque strings.
9#[derive(Debug, thiserror::Error)]
10pub enum GitError {
11 /// A git subprocess exited with a non-zero status.
12 #[error("{command}: {stderr}")]
13 CommandFailed {
14 /// The git command that was executed (e.g. `"git rebase main"`).
15 command: String,
16 /// Human-readable detail extracted from stderr/stdout.
17 stderr: String,
18 },
19
20 /// A git subprocess exceeded its configured runtime bound.
21 #[error("{command} timed out after {timeout:?}")]
22 CommandTimedOut {
23 /// Git invocation that exceeded the timeout.
24 command: String,
25 /// Configured command timeout.
26 timeout: Duration,
27 },
28
29 /// Git command output could not be parsed into the expected structure.
30 #[error("{0}")]
31 OutputParse(String),
32
33 /// The requested repository or worktree is no longer available.
34 #[error("{detail}")]
35 RepositoryUnavailable {
36 /// Original repository-discovery failure detail.
37 detail: String,
38 },
39
40 /// A filesystem or process-spawn operation failed.
41 #[error("{0}")]
42 Io(#[from] std::io::Error),
43
44 /// A repository declares pre-commit validation but its Git hook is
45 /// unavailable.
46 #[error(
47 "pre-commit validation is configured by `{config_file}`, but the Git pre-commit hook is \
48 not installed or executable. Install it with one of these commands:\n\n prek install\n \
49 pre-commit install\n\nAgentty will continue for now, but missing configured hooks will \
50 become an error in a future release."
51 )]
52 PreCommitHookMissing {
53 /// Repository-root-relative configuration file that declares
54 /// validation.
55 config_file: String,
56 },
57
58 /// A `tokio::task::spawn_blocking` join failed.
59 #[error("Join error: {0}")]
60 Join(#[from] tokio::task::JoinError),
61}
62
63impl GitError {
64 /// Returns whether a failed command reports Git index-lock contention.
65 ///
66 /// This identifies the lock failure without implying that the lock is
67 /// stale or safe to remove.
68 #[must_use]
69 pub fn is_index_locked(&self) -> bool {
70 matches!(self, Self::CommandFailed { stderr, .. } if rebase::is_git_index_lock_error(stderr))
71 }
72}
73
74#[cfg(test)]
75#[path = "error_test.rs"]
76mod tests;