Skip to main content

rtb_vcs/git/
error.rs

1//! `RepoError` โ€” the wrapped error model for the git-ops slice.
2//!
3//! Per v0.5 scope A8 (resolution 2026-05-12), `gix::Error` and
4//! `git2::Error` are wrapped, not leaked, so the public API stays
5//! stable across backend swaps. The internal mapping from backend
6//! errors to these variants lives in this module's `From` impls.
7//! Tests assert on variant shape (`matches!(err, RepoError::OpenFailed
8//! { .. })`) rather than backend internals.
9//!
10//! See `docs/development/specs/2026-05-11-v0.5-scope.md` ยง3.4 for the
11//! full variant table.
12
13use std::path::PathBuf;
14
15use miette::Diagnostic;
16use thiserror::Error;
17
18/// Errors surfaced by every method on [`crate::git::Repo`].
19///
20/// Variant shape is part of the public API; backend internals are not.
21/// See the module docs for the rationale.
22#[derive(Debug, Error, Diagnostic)]
23#[non_exhaustive]
24pub enum RepoError {
25    /// Filesystem-path failure (missing parent, permission denied,
26    /// symlink loop). Path carried for diagnostics.
27    #[error("io error at `{path}`: {source}")]
28    #[diagnostic(code(rtb_vcs::git::io))]
29    Io {
30        /// The path the operation tried to use.
31        path: PathBuf,
32        /// Underlying I/O failure.
33        #[source]
34        source: std::io::Error,
35    },
36
37    /// `Repo::open` could not load the repository at the given path.
38    /// Backend-specific reason in `cause` (stringified).
39    #[error("could not open repository at `{path}`: {cause}")]
40    #[diagnostic(code(rtb_vcs::git::open_failed))]
41    OpenFailed {
42        /// The path that failed to open.
43        path: PathBuf,
44        /// Stringified backend error.
45        cause: String,
46    },
47
48    /// `Repo::init` could not create a repository at the given path.
49    #[error("could not init repository at `{path}`: {cause}")]
50    #[diagnostic(code(rtb_vcs::git::init_failed))]
51    InitFailed {
52        /// The path where init was attempted.
53        path: PathBuf,
54        /// Stringified backend error.
55        cause: String,
56    },
57
58    /// Clone setup or transport failure (URL parse, refs negotiation,
59    /// network). Surfaces in `Repo::clone` (v0.5 commit 3).
60    #[error("could not clone `{url}`: {cause}")]
61    #[diagnostic(code(rtb_vcs::git::clone_failed))]
62    CloneFailed {
63        /// The URL being cloned from.
64        url: String,
65        /// Stringified backend error.
66        cause: String,
67    },
68
69    /// Fetch transport / refs failure. Surfaces in `Repo::fetch`
70    /// (v0.5 commit 4).
71    #[error("could not fetch from `{remote}`: {cause}")]
72    #[diagnostic(code(rtb_vcs::git::fetch_failed))]
73    FetchFailed {
74        /// The remote name (typically `origin`).
75        remote: String,
76        /// Stringified backend error.
77        cause: String,
78    },
79
80    /// Checkout could not switch to `revspec`. Surfaces in
81    /// `Repo::checkout` (v0.5 commit 4).
82    #[error("could not check out `{revspec}`: {cause}")]
83    #[diagnostic(code(rtb_vcs::git::checkout_failed))]
84    CheckoutFailed {
85        /// The revspec the caller asked for.
86        revspec: String,
87        /// Stringified backend error.
88        cause: String,
89    },
90
91    /// Commit creation / staging failure. Surfaces in `Repo::commit`
92    /// (v0.5 commit 3).
93    #[error("could not create commit: {cause}")]
94    #[diagnostic(code(rtb_vcs::git::commit_failed))]
95    CommitFailed {
96        /// Stringified backend error.
97        cause: String,
98    },
99
100    /// Status walk failure. Surfaces in `Repo::status` when gix
101    /// cannot iterate the worktree (e.g. permission errors).
102    #[error("could not compute status: {cause}")]
103    #[diagnostic(code(rtb_vcs::git::status_failed))]
104    StatusFailed {
105        /// Stringified backend error.
106        cause: String,
107    },
108
109    /// Commit-graph walk failure. Surfaces in `Repo::walk` for
110    /// failures that aren't [`RepoError::RevspecNotFound`] (gix
111    /// internal error, object-store I/O, unsupported revspec kind).
112    #[error("could not walk commits: {cause}")]
113    #[diagnostic(code(rtb_vcs::git::walk_failed))]
114    WalkFailed {
115        /// Stringified backend error.
116        cause: String,
117    },
118
119    /// Tree-diff failure. Surfaces in `Repo::diff` for failures
120    /// that aren't [`RepoError::RevspecNotFound`] (gix internal
121    /// error reading trees, blob diffing).
122    #[error("could not diff trees: {cause}")]
123    #[diagnostic(code(rtb_vcs::git::diff_failed))]
124    DiffFailed {
125        /// Stringified backend error.
126        cause: String,
127    },
128
129    /// Push transport / refs failure. Only reachable when the
130    /// `git2-fallback` Cargo feature is enabled.
131    #[error("could not push `{refspec}` to `{remote}`: {cause}")]
132    #[diagnostic(code(rtb_vcs::git::push_failed))]
133    PushFailed {
134        /// The remote name.
135        remote: String,
136        /// The refspec being pushed.
137        refspec: String,
138        /// Stringified backend error.
139        cause: String,
140    },
141
142    /// Caller-facing error for revspecs (e.g. `HEAD~999`, a missing
143    /// tag) that don't resolve to a known object.
144    #[error("revspec `{revspec}` not found")]
145    #[diagnostic(code(rtb_vcs::git::revspec_not_found))]
146    RevspecNotFound {
147        /// The revspec the caller asked for.
148        revspec: String,
149    },
150
151    /// Dirty-working-tree guard tripped. Listed paths are dirty.
152    /// Operations affected: `checkout`, path-based `commit`.
153    #[error("working tree is dirty: {} path(s) need attention", paths.len())]
154    #[diagnostic(code(rtb_vcs::git::dirty_working_tree))]
155    DirtyWorkingTree {
156        /// The dirty paths discovered.
157        paths: Vec<PathBuf>,
158    },
159
160    /// Credential resolution failed. Wraps `rtb-credentials`'s
161    /// existing error โ€” not a backend leak because `rtb-credentials`
162    /// is part of the framework's stable public surface.
163    #[error("credential resolution failed: {0}")]
164    #[diagnostic(code(rtb_vcs::git::auth))]
165    Auth(
166        #[from]
167        #[source]
168        rtb_credentials::CredentialError,
169    ),
170
171    /// Push attempted with `git2-fallback` disabled. Help text points
172    /// at the feature flag so users know how to enable push.
173    #[error("push is not supported without the `git2-fallback` Cargo feature")]
174    #[diagnostic(
175        code(rtb_vcs::git::push_unsupported),
176        help("enable the `git2-fallback` feature on `rtb-vcs` to opt into libgit2-backed push")
177    )]
178    PushUnsupported,
179}