use crate::containerization_error;
use std::fmt;
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum Error {
Failed {
action: String,
message: String,
code: Option<containerization_error::Code>,
},
Unavailable {
action: String,
message: String,
},
}
impl Error {
pub fn failed(action: impl Into<String>, message: impl fmt::Display) -> Self {
Self::Failed {
action: action.into(),
message: message.to_string(),
code: None,
}
}
pub(crate) fn failed_with_code(
action: impl Into<String>,
message: impl fmt::Display,
code: Option<containerization_error::Code>,
) -> Self {
Self::Failed {
action: action.into(),
message: message.to_string(),
code,
}
}
#[cfg_attr(target_os = "macos", allow(dead_code))]
pub(crate) fn unavailable(action: impl Into<String>, message: impl fmt::Display) -> Self {
Self::Unavailable {
action: action.into(),
message: message.to_string(),
}
}
pub fn action(&self) -> &str {
match self {
Self::Failed { action, .. } | Self::Unavailable { action, .. } => action,
}
}
pub fn is_code(&self, code: containerization_error::Code) -> bool {
matches!(self, Self::Failed { code: Some(thrown), .. } if *thrown == code)
}
}
impl fmt::Display for Error {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Failed { action, message, .. } => write!(formatter, "could not {action}: {message}"),
Self::Unavailable { action, message } => write!(formatter, "cannot {action}: {message}"),
}
}
}
impl std::error::Error for Error {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn says_what_was_attempted_and_what_it_said() {
assert_eq!(
Error::failed("boot session-one", "no kernel at /nowhere").to_string(),
"could not boot session-one: no kernel at /nowhere"
);
assert_eq!(
Error::unavailable("read the image store", "it has never been provisioned").to_string(),
"cannot read the image store: it has never been provisioned"
);
}
#[test]
fn names_the_action_without_its_message() {
assert_eq!(Error::failed("boot session-one", "nope").action(), "boot session-one");
}
#[test]
fn has_only_the_code_it_was_thrown_with() {
let not_found = containerization_error::Code::NotFound;
let error = Error::failed_with_code("get alpine", "not found", Some(not_found));
assert!(error.is_code(not_found));
assert!(!error.is_code(containerization_error::Code::Exists));
assert!(!Error::failed("get alpine", "not found").is_code(not_found));
assert!(!Error::unavailable("get alpine", "not on macOS").is_code(not_found));
}
}