From aa2d08087cf541dd547319e38cd47f0bff1588c9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 8 Oct 2019 01:24:18 -0700 Subject: [PATCH] Support context on Option --- .travis.yml | 2 +- src/context.rs | 38 ++++++++++++++++++++++++++++++++++++++ src/error.rs | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a50d9f0..3d16e63 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,5 +9,5 @@ script: cargo test matrix: include: - - rust: 1.32.0 + - rust: 1.34.0 script: cargo check diff --git a/src/context.rs b/src/context.rs index 06f3cff..3eabbb6 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,4 +1,5 @@ use crate::Error; +use std::convert::Infallible; use std::error::Error as StdError; use std::fmt::{self, Debug, Display}; @@ -103,6 +104,43 @@ impl Context for Result { } } +/// ``` +/// # type T = (); +/// # +/// use anyhow::{Context, Result}; +/// +/// fn maybe_get() -> Option { +/// # const IGNORE: &str = stringify! { +/// ... +/// # }; +/// # unimplemented!() +/// } +/// +/// fn demo() -> Result<()> { +/// let t = maybe_get().context("there is no T")?; +/// # const IGNORE: &str = stringify! { +/// ... +/// # }; +/// # unimplemented!() +/// } +/// ``` +impl Context for Option { + fn context(self, context: C) -> Result + where + C: Display + Send + Sync + 'static, + { + self.ok_or_else(|| Error::from_display(context, backtrace!())) + } + + fn with_context(self, context: F) -> Result + where + C: Display + Send + Sync + 'static, + F: FnOnce() -> C, + { + self.ok_or_else(|| Error::from_display(context(), backtrace!())) + } +} + pub(crate) struct ContextError { pub error: E, pub context: C, diff --git a/src/error.rs b/src/error.rs index feb3668..cf71a4f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -59,6 +59,18 @@ impl Error { unsafe { Error::construct(error, type_id, backtrace) } } + pub(crate) fn from_display(message: M, backtrace: Option) -> Self + where + M: Display + Send + Sync + 'static, + { + let error = DisplayError(message); + let type_id = TypeId::of::(); + + // Safety: DisplayError is repr(transparent) so DisplayError has the + // same layout as the typeid specifies. + unsafe { Error::construct(error, type_id, backtrace) } + } + // Takes backtrace as argument rather than capturing it here so that the // user sees one fewer layer of wrapping noise in the backtrace. // @@ -424,6 +436,29 @@ where impl StdError for MessageError where M: Display + Debug + 'static {} +#[repr(transparent)] +struct DisplayError(M); + +impl Debug for DisplayError +where + M: Display, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } +} + +impl Display for DisplayError +where + M: Display, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } +} + +impl StdError for DisplayError where M: Display + 'static {} + impl ErrorImpl<()> { fn error(&self) -> &(dyn StdError + Send + Sync + 'static) { unsafe { &*(self.vtable.object)(&self.error) }