Skip to content

Commit

Permalink
Add must-install feature, so that a non-default handler can be the on… (
Browse files Browse the repository at this point in the history
#52)

* Add must-install feature, so that a non-default handler can be the only handler consuming .text.

* Provide a better panic message if `must-install` feature is enabled.

Co-authored-by: Jane Lusby <[email protected]>

* Bump version because new feature was added.

* Convert must-install feature to auto-install to avoid negative features.

* Add ability to manually install DefaultHandler (for when auto-install is disabled).

* Ensure doctests pass when auto-install feature is disabled.

* Integration tests now succeed without auto-install feature.

* Add integration test for when auto-install feature is disabled.

* Add auto-install feature testing to CI.

* cargo fmt.

Co-authored-by: Jane Lusby <[email protected]>
Co-authored-by: Jane Lusby <[email protected]>
  • Loading branch information
3 people authored Mar 25, 2022
1 parent f4ebce4 commit 769e26e
Show file tree
Hide file tree
Showing 16 changed files with 177 additions and 4 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ jobs:
- --no-default-features
- --features track-caller
- --features pyo3
- --features auto-install
- --all-features
steps:
- uses: actions/checkout@v1
Expand All @@ -59,6 +60,7 @@ jobs:
- # default
- --no-default-features
- --features track-caller
- --features auto-install
# skip `--features pyo3` and `--all-features` because pyo3 doesn't support this msrv
steps:
- uses: actions/checkout@v1
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ readme = "README.md"
categories = ["rust-patterns"]

[features]
default = ["track-caller"]
default = ["auto-install", "track-caller"]
auto-install = []
track-caller = []

[dev-dependencies]
Expand Down
3 changes: 3 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,9 @@ impl Report {
/// #
/// # const REDACTED_CONTENT: () = ();
/// #
/// # #[cfg(not(feature = "auto-install"))]
/// # eyre::set_hook(Box::new(eyre::DefaultHandler::default_with)).unwrap();
/// #
/// # let error: Report = eyre!("...");
/// # let root_cause = &error;
/// #
Expand Down
48 changes: 46 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@
//! #
//! # const REDACTED_CONTENT: () = ();
//! #
//! # #[cfg(not(feature = "auto-install"))]
//! # eyre::set_hook(Box::new(eyre::DefaultHandler::default_with)).unwrap();
//! #
//! # let error: Report = eyre!("...");
//! # let root_cause = &error;
//! #
Expand Down Expand Up @@ -276,6 +279,9 @@
//! ```rust
//! use eyre::{eyre, Result};
//!
//! # #[cfg(not(feature = "auto-install"))]
//! # eyre::set_hook(Box::new(eyre::DefaultHandler::default_with)).unwrap();
//! #
//! let opt: Option<()> = None;
//! let result: Result<()> = opt.ok_or_else(|| eyre!("new error message"));
//! ```
Expand Down Expand Up @@ -445,7 +451,7 @@ type ErrorHook =
static HOOK: OnceCell<ErrorHook> = OnceCell::new();

/// Error indicating that `set_hook` was unable to install the provided ErrorHook
#[derive(Debug)]
#[derive(Debug, Clone, Copy)]
pub struct InstallError;

impl core::fmt::Display for InstallError {
Expand Down Expand Up @@ -559,6 +565,13 @@ pub fn set_hook(hook: ErrorHook) -> Result<(), InstallError> {
#[cfg_attr(track_caller, track_caller)]
#[cfg_attr(not(track_caller), allow(unused_mut))]
fn capture_handler(error: &(dyn StdError + 'static)) -> Box<dyn EyreHandler> {
#[cfg(not(feature = "auto-install"))]
let hook = HOOK
.get()
.expect("a handler must always be installed if the `auto-install` feature is disabled")
.as_ref();

#[cfg(feature = "auto-install")]
let hook = HOOK
.get_or_init(|| Box::new(DefaultHandler::default_with))
.as_ref();
Expand Down Expand Up @@ -699,8 +712,35 @@ pub struct DefaultHandler {
}

impl DefaultHandler {
/// Manual hook which constructs `DefaultHandler`s.
///
/// # Details
///
/// When supplied to the `set_hook` function, `default_with` will cause `eyre::Report` to use
/// `DefaultHandler` as the error report handler.
///
/// If the `auto-install` feature is enabled, and a user-provided hook for constructing
/// `EyreHandlers` was not installed using `set_hook`, `DefaultHandler::default_with`
/// is automatically installed as the hook.
///
/// # Example
///
/// ```rust,should_panic
/// use eyre::{DefaultHandler, eyre, InstallError, Result, set_hook};
///
/// fn main() -> Result<()> {
/// install_default().expect("default handler inexplicably already installed");
/// Err(eyre!("hello from default error city!"))
/// }
///
/// fn install_default() -> Result<(), InstallError> {
/// set_hook(Box::new(DefaultHandler::default_with))
/// }
///
/// ```
#[allow(unused_variables)]
fn default_with(error: &(dyn StdError + 'static)) -> Box<dyn EyreHandler> {
#[cfg_attr(not(feature = "auto-install"), allow(dead_code))]
pub fn default_with(error: &(dyn StdError + 'static)) -> Box<dyn EyreHandler> {
let backtrace = backtrace_if_absent!(error);

Box::new(Self {
Expand Down Expand Up @@ -973,6 +1013,8 @@ pub type Result<T, E = Report> = core::result::Result<T, E>;
/// }
///
/// fn main() {
/// # #[cfg(not(feature = "auto-install"))]
/// # eyre::set_hook(Box::new(eyre::DefaultHandler::default_with)).unwrap();
/// let err = do_it().unwrap_err();
/// if let Some(e) = err.downcast_ref::<SuspiciousError>() {
/// // If helper() returned SuspiciousError, this downcast will
Expand Down Expand Up @@ -1013,6 +1055,8 @@ pub type Result<T, E = Report> = core::result::Result<T, E>;
/// }
///
/// fn main() {
/// # #[cfg(not(feature = "auto-install"))]
/// # eyre::set_hook(Box::new(eyre::DefaultHandler::default_with)).unwrap();
/// let err = do_it().unwrap_err();
/// if let Some(e) = err.downcast_ref::<HelperFailed>() {
/// // If helper failed, this downcast will succeed because
Expand Down
17 changes: 16 additions & 1 deletion tests/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use eyre::{bail, Result};
#![allow(dead_code)]

use eyre::{bail, set_hook, DefaultHandler, InstallError, Result};
use once_cell::sync::OnceCell;
use std::io;

pub fn bail_literal() -> Result<()> {
Expand All @@ -12,3 +15,15 @@ pub fn bail_fmt() -> Result<()> {
pub fn bail_error() -> Result<()> {
bail!(io::Error::new(io::ErrorKind::Other, "oh no!"));
}

// Tests are multithreaded- use OnceCell to install hook once if auto-install
// feature is disabled.
pub fn maybe_install_handler() -> Result<(), InstallError> {
static INSTALLER: OnceCell<Result<(), InstallError>> = OnceCell::new();

if cfg!(not(feature = "auto-install")) {
*INSTALLER.get_or_init(|| set_hook(Box::new(DefaultHandler::default_with)))
} else {
Ok(())
}
}
11 changes: 11 additions & 0 deletions tests/test_boxed.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
mod common;

use self::common::maybe_install_handler;
use eyre::{eyre, Report};
use std::error::Error as StdError;
use std::io;
Expand All @@ -11,6 +14,8 @@ struct MyError {

#[test]
fn test_boxed_str() {
maybe_install_handler().unwrap();

let error = Box::<dyn StdError + Send + Sync>::from("oh no!");
let error: Report = eyre!(error);
assert_eq!("oh no!", error.to_string());
Expand All @@ -25,6 +30,8 @@ fn test_boxed_str() {

#[test]
fn test_boxed_thiserror() {
maybe_install_handler().unwrap();

let error = MyError {
source: io::Error::new(io::ErrorKind::Other, "oh no!"),
};
Expand All @@ -34,13 +41,17 @@ fn test_boxed_thiserror() {

#[test]
fn test_boxed_eyre() {
maybe_install_handler().unwrap();

let error: Report = eyre!("oh no!").wrap_err("it failed");
let error = eyre!(error);
assert_eq!("oh no!", error.source().unwrap().to_string());
}

#[test]
fn test_boxed_sources() {
maybe_install_handler().unwrap();

let error = MyError {
source: io::Error::new(io::ErrorKind::Other, "oh no!"),
};
Expand Down
9 changes: 9 additions & 0 deletions tests/test_chain.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
mod common;

use self::common::maybe_install_handler;
use eyre::{eyre, Report};

fn error() -> Report {
Expand All @@ -6,6 +9,8 @@ fn error() -> Report {

#[test]
fn test_iter() {
maybe_install_handler().unwrap();

let e = error();
let mut chain = e.chain();
assert_eq!("3", chain.next().unwrap().to_string());
Expand All @@ -18,6 +23,8 @@ fn test_iter() {

#[test]
fn test_rev() {
maybe_install_handler().unwrap();

let e = error();
let mut chain = e.chain().rev();
assert_eq!("0", chain.next().unwrap().to_string());
Expand All @@ -30,6 +37,8 @@ fn test_rev() {

#[test]
fn test_len() {
maybe_install_handler().unwrap();

let e = error();
let mut chain = e.chain();
assert_eq!(4, chain.len());
Expand Down
12 changes: 12 additions & 0 deletions tests/test_context.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod common;
mod drop;

use crate::common::maybe_install_handler;
use crate::drop::{DetectDrop, Flag};
use eyre::{Report, Result, WrapErr};
use std::fmt::{self, Display};
Expand Down Expand Up @@ -89,6 +91,8 @@ fn make_chain() -> (Report, Dropped) {

#[test]
fn test_downcast_ref() {
maybe_install_handler().unwrap();

let (err, dropped) = make_chain();

assert!(!err.is::<String>());
Expand All @@ -113,6 +117,8 @@ fn test_downcast_ref() {

#[test]
fn test_downcast_high() {
maybe_install_handler().unwrap();

let (err, dropped) = make_chain();

let err = err.downcast::<HighLevel>().unwrap();
Expand All @@ -125,6 +131,8 @@ fn test_downcast_high() {

#[test]
fn test_downcast_mid() {
maybe_install_handler().unwrap();

let (err, dropped) = make_chain();

let err = err.downcast::<MidLevel>().unwrap();
Expand All @@ -137,6 +145,8 @@ fn test_downcast_mid() {

#[test]
fn test_downcast_low() {
maybe_install_handler().unwrap();

let (err, dropped) = make_chain();

let err = err.downcast::<LowLevel>().unwrap();
Expand All @@ -149,6 +159,8 @@ fn test_downcast_low() {

#[test]
fn test_unsuccessful_downcast() {
maybe_install_handler().unwrap();

let (err, dropped) = make_chain();

let err = err.downcast::<String>().unwrap_err();
Expand Down
6 changes: 6 additions & 0 deletions tests/test_context_access.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
mod common;

use crate::common::maybe_install_handler;

#[test]
fn test_context() {
use eyre::{eyre, Report};

maybe_install_handler().unwrap();

let error: Report = eyre!("oh no!");
let _ = error.context();
}
4 changes: 4 additions & 0 deletions tests/test_convert.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
mod common;
mod drop;

use self::common::maybe_install_handler;
use self::drop::{DetectDrop, Flag};
use eyre::{Report, Result};
use std::error::Error as StdError;

#[test]
fn test_convert() {
maybe_install_handler().unwrap();

let has_dropped = Flag::new();
let error: Report = Report::new(DetectDrop::new(&has_dropped));
let box_dyn = Box::<dyn StdError + Send + Sync>::from(error);
Expand Down
12 changes: 12 additions & 0 deletions tests/test_downcast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use std::io;

#[test]
fn test_downcast() {
maybe_install_handler().unwrap();

#[cfg(not(eyre_no_fmt_arguments_as_str))]
assert_eq!(
"oh no!",
Expand Down Expand Up @@ -38,6 +40,8 @@ fn test_downcast() {

#[test]
fn test_downcast_ref() {
maybe_install_handler().unwrap();

#[cfg(not(eyre_no_fmt_arguments_as_str))]
assert_eq!(
"oh no!",
Expand Down Expand Up @@ -69,6 +73,8 @@ fn test_downcast_ref() {

#[test]
fn test_downcast_mut() {
maybe_install_handler().unwrap();

#[cfg(not(eyre_no_fmt_arguments_as_str))]
assert_eq!(
"oh no!",
Expand Down Expand Up @@ -100,6 +106,8 @@ fn test_downcast_mut() {

#[test]
fn test_drop() {
maybe_install_handler().unwrap();

let has_dropped = Flag::new();
let error: Report = Report::new(DetectDrop::new(&has_dropped));
drop(error.downcast::<DetectDrop>().unwrap());
Expand All @@ -108,6 +116,8 @@ fn test_drop() {

#[test]
fn test_large_alignment() {
maybe_install_handler().unwrap();

#[repr(align(64))]
#[derive(Debug)]
struct LargeAlignedError(&'static str);
Expand All @@ -129,6 +139,8 @@ fn test_large_alignment() {

#[test]
fn test_unsuccessful_downcast() {
maybe_install_handler().unwrap();

let mut error = bail_error().unwrap_err();
assert!(error.downcast_ref::<&str>().is_none());
assert!(error.downcast_mut::<&str>().is_none());
Expand Down
Loading

0 comments on commit 769e26e

Please sign in to comment.