use std::error::Error as StdError;
use std::fmt;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("name detection is on, but no name-detection model was given")]
RecognizerMissing,
#[error("name detection failed: {0}")]
Recognizer(#[from] RecognizerError),
#[error("vault: {0}")]
Vault(#[from] VaultError),
}
macro_rules! wrapped_error {
($(#[$doc:meta])* $name:ident) => {
$(#[$doc])*
#[derive(Debug)]
pub struct $name(Box<dyn StdError + Send + Sync>);
impl $name {
pub fn new(error: impl Into<Box<dyn StdError + Send + Sync>>) -> Self {
$name(error.into())
}
pub fn get_ref(&self) -> &(dyn StdError + Send + Sync + 'static) {
&*self.0
}
pub fn into_inner(self) -> Box<dyn StdError + Send + Sync> {
self.0
}
}
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl StdError for $name {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
self.0.source()
}
}
};
}
wrapped_error! {
VaultError
}
wrapped_error! {
RecognizerError
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn wrapped_errors_show_the_inner_message() {
let io = std::io::Error::other("disk full");
let e = Error::from(VaultError::new(io));
assert_eq!(e.to_string(), "vault: disk full");
let Error::Vault(v) = e else { unreachable!() };
assert!(v.get_ref().downcast_ref::<std::io::Error>().is_some());
assert_eq!(
Error::from(RecognizerError::new("model crashed")).to_string(),
"name detection failed: model crashed"
);
}
}