use core::fmt::{self, Debug, Display}; use Fail; without_std! { /// An error with context around it. /// /// The context is intended to be a human-readable, user-facing explanation for the /// error that has occurred. The underlying error is not assumed to be end-user-relevant /// information. /// /// The `Display` impl for `Context` only prints the human-readable context, while the /// `Debug` impl also prints the underlying error. pub struct Context { context: D, } impl Context { /// Creates a new context without an underlying error message. pub fn new(context: D) -> Context { Context { context } } /// Returns a reference to the context provided with this error. pub fn get_context(&self) -> &D { &self.context } /// Maps `Context` to `Context` by applying a function to the contained context. pub fn map(self, op: F) -> Context where F: FnOnce(D) -> T, T: Display + Send + Sync + 'static { Context { context: op(self.context), } } pub(crate) fn with_err(context: D, _: E) -> Context { Context { context } } } impl Fail for Context { } impl Debug for Context { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.context) } } impl Display for Context { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.context) } } #[test] fn test_map() { let ctx = Context::new("a string").map(|s| format!("{} with some more stuff", s)); assert_eq!(ctx.context, String::from("a string with some more stuff")); } } with_std! { use {Error, Backtrace}; /// An error with context around it. /// /// The context is intended to be a human-readable, user-facing explanation for the /// error that has occurred. The underlying error is not assumed to be end-user-relevant /// information. /// /// The `Display` impl for `Context` only prints the human-readable context, while the /// `Debug` impl also prints the underlying error. pub struct Context { context: D, failure: Either, } impl Context { /// Creates a new context without an underlying error message. pub fn new(context: D) -> Context { let failure = Either::This(Backtrace::new()); Context { context, failure } } /// Returns a reference to the context provided with this error. pub fn get_context(&self) -> &D { &self.context } /// Maps `Context` to `Context` by applying a function to the contained context. pub fn map(self, op: F) -> Context where F: FnOnce(D) -> T, T: Display + Send + Sync + 'static { Context { context: op(self.context), failure: self.failure, } } pub(crate) fn with_err>(context: D, error: E) -> Context { let failure = Either::That(error.into()); Context { context, failure } } } impl Fail for Context { fn name(&self) -> Option<&str> { self.failure.as_cause().and_then(|x| x.name()) } fn cause(&self) -> Option<&dyn Fail> { self.failure.as_cause() } fn backtrace(&self) -> Option<&Backtrace> { Some(self.failure.backtrace()) } } impl Debug for Context { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}\n\n{}", self.failure, self.context) } } impl Display for Context { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.context) } } enum Either { This(A), That(B), } impl Either { fn backtrace(&self) -> &Backtrace { match *self { Either::This(ref backtrace) => backtrace, Either::That(ref error) => error.backtrace(), } } fn as_cause(&self) -> Option<&dyn Fail> { match *self { Either::This(_) => None, Either::That(ref error) => Some(error.as_fail()) } } } impl Debug for Either { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Either::This(ref backtrace) => write!(f, "{:?}", backtrace), Either::That(ref error) => write!(f, "{:?}", error), } } } #[test] fn test_map() { let ctx = Context::new("a string").map(|s| format!("{} with some more stuff", s)); assert_eq!(ctx.context, String::from("a string with some more stuff")); } } impl From for Context where D: Display + Send + Sync + 'static, { fn from(display: D) -> Context { Context::new(display) } }