vcs_core/error.rs
1//! The facade's error type: a thin wrapper that adds repo-detection failures on
2//! top of the underlying [`processkit::Error`] the per-tool clients return.
3
4use std::path::PathBuf;
5
6/// An error from a [`Repo`](crate::Repo) operation.
7#[derive(Debug)]
8#[non_exhaustive]
9pub enum Error {
10 /// [`Repo::open`](crate::Repo::open) found no `.git`/`.jj` from the start dir
11 /// up to the filesystem root.
12 NotARepository(PathBuf),
13 /// A worktree/workspace lookup by path matched no attached worktree.
14 WorktreeNotFound(PathBuf),
15 /// A filesystem operation failed (e.g. removing a workspace directory).
16 Io(std::io::Error),
17 /// An underlying `vcs-git` / `vcs-jj` (i.e. `processkit`) error.
18 Vcs(processkit::Error),
19}
20
21impl Error {
22 /// Whether this wraps a merge/rebase **conflict** from the backend — so a
23 /// caller can branch on "conflict, resolve it" vs. a hard failure without
24 /// matching on [`processkit::Error`] internals. (Recognises git's conflict
25 /// markers; jj surfaces conflicts as state, not errors — see
26 /// [`Repo::in_progress_state`](crate::Repo::in_progress_state).)
27 pub fn is_conflict(&self) -> bool {
28 matches!(self, Error::Vcs(e) if vcs_git::is_merge_conflict(e))
29 }
30
31 /// Whether this is a benign "nothing to commit" — an empty commit attempt the
32 /// caller likely wants to treat as a no-op.
33 pub fn is_nothing_to_commit(&self) -> bool {
34 matches!(self, Error::Vcs(e) if vcs_git::is_nothing_to_commit(e))
35 }
36
37 /// Whether this is a **transient** fetch/network failure worth retrying
38 /// (DNS, connection reset, timeout). The underlying clients already retry
39 /// their own fetches; this is for retrying higher-level flows.
40 pub fn is_transient_fetch(&self) -> bool {
41 matches!(self, Error::Vcs(e)
42 if vcs_git::is_transient_fetch_error(e) || vcs_jj::is_transient_fetch_error(e))
43 }
44}
45
46impl std::fmt::Display for Error {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 match self {
49 Error::NotARepository(p) => {
50 write!(
51 f,
52 "no git or jj repository found at or above {}",
53 p.display()
54 )
55 }
56 Error::WorktreeNotFound(p) => {
57 write!(f, "no worktree found at {}", p.display())
58 }
59 Error::Io(e) => write!(f, "{e}"),
60 Error::Vcs(e) => write!(f, "{e}"),
61 }
62 }
63}
64
65impl std::error::Error for Error {
66 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
67 match self {
68 Error::Io(e) => Some(e),
69 Error::Vcs(e) => Some(e),
70 _ => None,
71 }
72 }
73}
74
75impl From<std::io::Error> for Error {
76 fn from(e: std::io::Error) -> Self {
77 Error::Io(e)
78 }
79}
80
81impl From<processkit::Error> for Error {
82 fn from(e: processkit::Error) -> Self {
83 Error::Vcs(e)
84 }
85}
86
87/// `Result` specialised to the facade [`Error`].
88pub type Result<T> = std::result::Result<T, Error>;