use std::fmt;
use crate::provider_v3::reason::WireReason;
#[derive(Debug)]
pub enum Error {
Refusal {
reason: WireReason,
detail: String,
},
Declaration {
detail: String,
},
}
impl Error {
pub fn refuse(reason: WireReason, detail: impl Into<String>) -> Self {
Self::Refusal {
reason,
detail: detail.into(),
}
}
pub fn declaration(detail: impl Into<String>) -> Self {
Self::Declaration {
detail: detail.into(),
}
}
#[must_use]
pub const fn reason(&self) -> Option<WireReason> {
match self {
Self::Refusal { reason, .. } => Some(*reason),
Self::Declaration { .. } => None,
}
}
#[must_use]
pub fn detail(&self) -> &str {
match self {
Self::Refusal { detail, .. } | Self::Declaration { detail } => detail,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Refusal { reason, detail } => write!(f, "{reason}: {detail}"),
Self::Declaration { detail } => write!(f, "provider declaration is invalid: {detail}"),
}
}
}
impl std::error::Error for Error {}
impl From<crate::setup_core::Error> for Error {
fn from(error: crate::setup_core::Error) -> Self {
Self::refuse(WireReason::from(error.reason()), error.detail().to_owned())
}
}
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used, clippy::panic)]
use super::*;
#[test]
fn a_declaration_error_carries_no_wire_reason() {
assert!(Error::declaration("no component kinds").reason().is_none());
}
#[test]
fn a_refusal_carries_the_reason_the_consumer_matches_on() {
let error = Error::refuse(WireReason::PathEscapesTarget, "../etc/passwd");
assert_eq!(error.reason(), Some(WireReason::PathEscapesTarget));
assert!(error.to_string().starts_with("path_escapes_target: "));
}
#[test]
fn a_kernel_failure_becomes_a_refusal_with_its_translated_reason() {
let kernel = crate::setup_core::Error::new(crate::setup_core::ReasonCode::Stale, "target moved");
let error = Error::from(kernel);
assert_eq!(error.reason(), Some(WireReason::Stale));
assert_eq!(error.detail(), "target moved");
}
}