#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
Notify(notify::Error),
Vcs(vcs_core::Error),
Io(std::io::Error),
}
impl Error {
pub fn is_transient(&self) -> bool {
match self {
Error::Vcs(e) => e.is_transient(),
Error::Io(e) => e.kind() == std::io::ErrorKind::TimedOut,
_ => false,
}
}
pub fn is_not_found(&self) -> bool {
matches!(self, Error::Vcs(e) if e.is_not_found())
}
pub fn processkit_error(&self) -> Option<&processkit::Error> {
match self {
Error::Vcs(vcs_core::Error::Vcs(e)) => Some(e),
_ => None,
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::Notify(e) => write!(f, "filesystem watch failed: {e}"),
Error::Vcs(e) => write!(f, "{e}"),
Error::Io(e) => write!(f, "{e}"),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Notify(e) => Some(e),
Error::Vcs(e) => Some(e),
Error::Io(e) => Some(e),
}
}
}
impl From<notify::Error> for Error {
fn from(e: notify::Error) -> Self {
Error::Notify(e)
}
}
impl From<vcs_core::Error> for Error {
fn from(e: vcs_core::Error) -> Self {
Error::Vcs(e)
}
}
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
Error::Io(e)
}
}
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn classifiers_and_accessor_reach_through_the_vcs_layer() {
let transient = Error::Vcs(vcs_core::Error::Vcs(processkit::Error::Spawn {
program: "git".into(),
source: std::io::Error::from(std::io::ErrorKind::Interrupted),
}));
assert!(transient.is_transient(), "interrupted spawn is transient");
assert!(!transient.is_not_found());
assert!(
transient.processkit_error().is_some(),
"reaches the inner error"
);
let missing = Error::Vcs(vcs_core::Error::Vcs(processkit::Error::NotFound {
program: "jj".into(),
searched: None,
}));
assert!(missing.is_not_found(), "missing binary is not-found");
assert!(!missing.is_transient());
assert!(missing.processkit_error().is_some());
let io = Error::Io(std::io::Error::from(std::io::ErrorKind::PermissionDenied));
assert!(!io.is_transient() && !io.is_not_found());
assert!(
io.processkit_error().is_none(),
"no subprocess behind an Io error"
);
let baseline_timeout = Error::Io(std::io::Error::from(std::io::ErrorKind::TimedOut));
assert!(
baseline_timeout.is_transient(),
"a baseline TimedOut is transient (retryable)"
);
}
}