1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
// TODO: #![doc(include = "../README.md")] once
// https://github.com/rust-lang/rust/issues/44732 or
// https://github.com/rust-lang/rust/issues/78835 is stable
//! # serde-error
//!
//! `serde-error` provides a (de)serializable `Error` type implementing
//! `std::error::Error`, that can easily be used to transmit errors over
//! the wire.
//!
//! ## Should I use this?
//!
//! This crate is production-grade. However, you probably do not want to
//! use it: usually, it makes much more sense to just sum up the error as
//! some type, instead of serializing the whole causality chain.
//!
//! The use case for which this crate was designed is running Rust
//! WebAssembly blobs inside a Rust wasmtime-running host. In such a case
//! the causality chain is clearly kept across the serialization boundary,
//! and it thus makes sense to keep it all.
//!
//! In some other cases it may make sense to serialize the whole causality
//! chain, but most often it makes most sense to just not serialize
//! errors.
//!
//! As such, please use `serde-error` with parsimony.
//!
//! ## How should I use this?
//!
//! ```rust
//! use anyhow::Context;
//! use std::error::Error;
//!
//! fn foo() -> anyhow::Result<()> {
//!     // ...
//!     Err(anyhow::anyhow!("Failed smurfing the smurfs"))
//! }
//!
//! fn bar() -> anyhow::Result<()> {
//!     // ...
//!     foo().context("Running foo")
//! }
//!
//! fn main() {
//!     if let Err(returned_err) = bar() {
//!         let s = bincode::serialize(&serde_error::Error::new(&*returned_err))
//!             .expect("Serializing error");
//!         let d: serde_error::Error = bincode::deserialize(&s)
//!             .expect("Deserializing error");
//!         let e = anyhow::Error::from(d);
//!         assert_eq!(e.to_string(), "Running foo");
//!         assert_eq!(e.source().unwrap().to_string(), "Failed smurfing the smurfs");
//!     } else {
//!         panic!("bar did not return an error");
//!     }
//! }
//! ```


// TODO: once backtrace lands stable, consider trying to serialize the backtrace too? not sure it
// makes sense though.

#[derive(serde::Deserialize, serde::Serialize)]
pub struct Error {
    description: String,
    source: Option<Box<Error>>,
}

impl Error {
    pub fn new<T>(e: &T) -> Error
    where
        T: ?Sized + std::error::Error,
    {
        Error {
            description: e.to_string(),
            source: e.source().map(|s| Box::new(Error::new(s))),
        }
    }
}

impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn 'static + std::error::Error)> {
        self.source.as_ref().map(|s| &**s as &(dyn 'static + std::error::Error))
    }

    fn description(&self) -> &str {
        &self.description
    }
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.description)
    }
}

impl std::fmt::Debug for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.description)
    }
}