use std::path::PathBuf;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(
"Cannot init: {0:?} already exists and is not empty. Remove it first, or use `zenops repo pull` if it's already a zenops repo."
)]
DirNotEmpty(PathBuf),
#[error(
"Cannot init: {0:?} already exists. Bootstrap refuses to touch an existing path; remove it first, or pass a URL to clone into it."
)]
DirExists(PathBuf),
#[error(
"Cannot init: {0:?} already contains a .git directory. Looks like a zenops repo already exists; remove it first or skip init."
)]
GitDirExists(PathBuf),
#[error("Failed to initialize git repo: {source}")]
GitInitFailed {
#[source]
source: xshell::Error,
},
#[error(
"init bootstrap requires a terminal for prompts; clone with a URL instead, or run from a TTY"
)]
NeedsTty,
#[error(
"Cloned repo at {0:?} has no config.toml at its root. Is this a zenops config repo? The clone was left in place so you can inspect it."
)]
NoConfigToml(PathBuf),
#[error("Failed to clone {url}: {source}")]
CloneFailed {
url: String,
#[source]
source: xshell::Error,
},
#[error("Init I/O error at {0:?}: {1}")]
Io(PathBuf, #[source] std::io::Error),
}
impl PartialEq for Error {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::DirNotEmpty(l), Self::DirNotEmpty(r)) => l == r,
(Self::DirExists(l), Self::DirExists(r)) => l == r,
(Self::GitDirExists(l), Self::GitDirExists(r)) => l == r,
(Self::GitInitFailed { source: l }, Self::GitInitFailed { source: r }) => {
l.to_string() == r.to_string()
}
(Self::NeedsTty, Self::NeedsTty) => true,
(Self::NoConfigToml(l), Self::NoConfigToml(r)) => l == r,
(
Self::CloneFailed {
url: l_url,
source: l_src,
},
Self::CloneFailed {
url: r_url,
source: r_src,
},
) => l_url == r_url && l_src.to_string() == r_src.to_string(),
(Self::Io(l0, l1), Self::Io(r0, r1)) => l0 == r0 && l1.kind() == r1.kind(),
_ => false,
}
}
}
#[cfg(test)]
mod tests {
use std::io;
use std::path::PathBuf;
use similar_asserts::assert_eq;
use xshell::{Shell, cmd};
use super::*;
fn io(kind: io::ErrorKind) -> io::Error {
io::Error::from(kind)
}
fn xshell_err() -> xshell::Error {
let sh = Shell::new().unwrap();
cmd!(sh, "false").quiet().run().unwrap_err()
}
#[test]
fn unit_variants_compare_equal_to_themselves() {
assert_eq!(Error::NeedsTty, Error::NeedsTty);
}
#[test]
fn dir_variants_eq_and_ne() {
assert_eq!(
Error::DirNotEmpty(PathBuf::from("/a")),
Error::DirNotEmpty(PathBuf::from("/a"))
);
assert_ne!(
Error::DirNotEmpty(PathBuf::from("/a")),
Error::DirNotEmpty(PathBuf::from("/b"))
);
assert_eq!(
Error::DirExists(PathBuf::from("/a")),
Error::DirExists(PathBuf::from("/a"))
);
assert_eq!(
Error::GitDirExists(PathBuf::from("/a")),
Error::GitDirExists(PathBuf::from("/a"))
);
assert_eq!(
Error::NoConfigToml(PathBuf::from("/a")),
Error::NoConfigToml(PathBuf::from("/a"))
);
}
#[test]
fn init_git_init_failed_eq_compares_display_string() {
let a = Error::GitInitFailed {
source: xshell_err(),
};
let b = Error::GitInitFailed {
source: xshell_err(),
};
assert_eq!(a, b);
}
#[test]
fn init_clone_failed_eq_compares_url_and_display() {
let a = Error::CloneFailed {
url: "https://example/x".to_string(),
source: xshell_err(),
};
let b = Error::CloneFailed {
url: "https://example/x".to_string(),
source: xshell_err(),
};
let c = Error::CloneFailed {
url: "https://example/y".to_string(),
source: xshell_err(),
};
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn init_io_eq_and_ne() {
let a = Error::Io(PathBuf::from("/x"), io(io::ErrorKind::Other));
let b = Error::Io(PathBuf::from("/x"), io(io::ErrorKind::Other));
let c = Error::Io(PathBuf::from("/x"), io(io::ErrorKind::NotFound));
assert_eq!(a, b);
assert_ne!(a, c);
}
}