Skip to main content

vcs_forge/
error.rs

1//! The facade's error type: the underlying [`processkit::Error`] the wrapper
2//! clients return, plus an [`Unsupported`](Error::Unsupported) variant for an
3//! operation a given forge's CLI does not provide.
4
5use crate::ForgeKind;
6
7/// An error from a [`Forge`](crate::Forge) operation.
8#[derive(Debug)]
9#[non_exhaustive]
10pub enum Error {
11    /// An underlying `vcs-github` / `vcs-gitlab` / `vcs-gitea` (i.e. `processkit`)
12    /// error.
13    Forge(processkit::Error),
14    /// The operation isn't available on this forge's CLI — e.g. `repo_view`,
15    /// `pr_mark_ready`, and `pr_checks` on Gitea, whose `tea` has no command for
16    /// them. The `operation` is the [`ForgeApi`](crate::ForgeApi) method name.
17    Unsupported {
18        /// Which forge lacks the operation.
19        forge: ForgeKind,
20        /// The [`ForgeApi`](crate::ForgeApi) method that isn't supported.
21        operation: &'static str,
22    },
23}
24
25impl Error {
26    /// Whether this is a **transient** network failure worth retrying (DNS,
27    /// connection reset, timeout) — forge commands are network-bound, so a higher
28    /// flow may want to retry. Named to match the wrapper classifiers
29    /// ([`vcs_cli_support::is_transient_fetch_error`]).
30    pub fn is_transient_fetch_error(&self) -> bool {
31        matches!(self, Error::Forge(e) if vcs_cli_support::is_transient_fetch_error(e))
32    }
33
34    /// Whether this is an [`Unsupported`](Error::Unsupported) operation (rather
35    /// than a forge/network failure).
36    pub fn is_unsupported(&self) -> bool {
37        matches!(self, Error::Unsupported { .. })
38    }
39}
40
41impl std::fmt::Display for Error {
42    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43        match self {
44            Error::Forge(e) => write!(f, "{e}"),
45            Error::Unsupported { forge, operation } => {
46                write!(f, "{} does not support `{operation}`", forge.as_str())
47            }
48        }
49    }
50}
51
52impl std::error::Error for Error {
53    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
54        match self {
55            Error::Forge(e) => Some(e),
56            Error::Unsupported { .. } => None,
57        }
58    }
59}
60
61impl From<processkit::Error> for Error {
62    fn from(e: processkit::Error) -> Self {
63        Error::Forge(e)
64    }
65}
66
67/// `Result` specialised to the facade [`Error`].
68pub type Result<T> = std::result::Result<T, Error>;