use std::sync::Arc;
use anyhow::Error;
use thiserror::Error;
#[derive(Error, Debug, Clone)]
#[error(transparent)]
pub struct SharedError {
#[from]
error: Arc<Error>,
}
impl SharedError {
pub fn inner(&self) -> &Error {
&self.error
}
pub fn new_arcederror(error: Arc<anyhow::Error>) -> Self {
Self { error }
}
}
pub trait IntoSharedError<Ret> {
fn shared_error(self) -> Ret;
}
impl<E: Into<Error>> IntoSharedError<SharedError> for E {
fn shared_error(self) -> SharedError {
SharedError {
error: Arc::new(self.into()),
}
}
}
impl<T, E: Into<Error>> IntoSharedError<Result<T, SharedError>> for Result<T, E> {
fn shared_error(self) -> Result<T, SharedError> {
self.map_err(|err| err.shared_error())
}
}
impl slog::KV for SharedError {
fn serialize(
&self,
_record: &slog::Record<'_>,
serializer: &mut dyn slog::Serializer,
) -> slog::Result {
serializer.emit_str("error", &format!("{self}"))?;
serializer.emit_str("error_debug", &format!("{self:#?}"))?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::error::Error as _;
use super::*;
#[derive(Debug, Error)]
enum TestError {
#[error("Some error: {0}")]
SomeError(String),
}
#[test]
fn test_convert_to_shared_error() {
let error = TestError::SomeError("some context".to_owned());
let shared_error: SharedError = error.shared_error();
assert_eq!(
shared_error.inner().to_string(),
"Some error: some context".to_owned()
);
assert_eq!(
shared_error.to_string(),
"Some error: some context".to_owned()
);
assert!(shared_error.source().is_none());
}
#[test]
#[allow(clippy::redundant_clone)]
fn test_clone_shared_error() {
let error = TestError::SomeError("some context".to_owned());
let shared_error: SharedError = error.shared_error();
let cloned_error = shared_error.clone();
assert_eq!(
cloned_error.inner().to_string(),
"Some error: some context".to_owned()
);
assert_eq!(
cloned_error.to_string(),
"Some error: some context".to_owned()
);
assert!(cloned_error.source().is_none());
}
#[test]
fn test_convert_to_result_with_shared_error() {
fn some_fallible_func() -> Result<(), SharedError> {
let result: Result<(), TestError> =
Err(TestError::SomeError("some context".to_owned()));
result.shared_error()
}
let shared_error_result = some_fallible_func();
match shared_error_result {
Ok(_) => panic!("Can't be an Ok result"),
Err(shared_error) => {
assert_eq!(
shared_error.to_string(),
"Some error: some context".to_owned()
);
assert!(shared_error.source().is_none());
}
}
}
#[test]
fn test_convert_to_result_with_shared_error_anyhow() {
fn some_fallible_func() -> Result<(), SharedError> {
let result: Result<(), Error> =
Err(TestError::SomeError("some context".to_owned()).into());
result.shared_error()
}
let shared_error_result = some_fallible_func();
match shared_error_result {
Ok(_) => panic!("Can't be an Ok result"),
Err(shared_error) => {
assert_eq!(
shared_error.to_string(),
"Some error: some context".to_owned()
);
assert!(shared_error.source().is_none());
}
}
}
}