BoxedStash

Struct BoxedStash 

Source
pub struct BoxedStash { /* private fields */ }
Expand description

An ErrorStash that produces an ErrorList containing boxed, potentially heterogeneous error values.

BoxedErrorList is designed to be a simple output type for ErrorStash when you want to collect multiple potential error types and report them together.

An ErrorStash that produces BoxedErrorList values can be created using the [BoxedErrorList::new_stash] function.

The individual errors within a BoxedErrorList will be instances of Box<dyn Error + Send + Sync + 'static>, allowing most types that implements the Error trait to be stored. These values themselves implement the Error trait, and can be downcast back to their original types if needed.

§Terminal methods

Methods that can return a BoxedErrorList (i.e. inside a Result or Option) are considered terminal methods, as they consume the collected errors to produce the wrapper error. In BoxedStash, these methods not only consume all collected errors, they also reset the stash’s summary line to its default value.

Typically, this doesn’t matter, because these methods are normally called with the ? operator, causing the calling function to return and the stash to likely no longer be used if any errors ocurred. However, if you do call a terminal method without propagating the error immediately (i.e. without using ?), be aware after the call the stash will be empty and the summary line reset- if you intend to continue using the stash, you may need to configure any custom summary line again.

The terminal methods on BoxedStash are:

§Compatibility

The two error types encountered when using BoxedStash are the wrapper error type, BoxedErrorList, and the child error type, BoxedError. Both of these implement std::error::Error + Send + Sync + 'static, making them compatible with other popular error handling crates, including anyhow, thiserror, and eyre.

§anyhow example

use anyhow::{Error, anyhow};

let mut stash = BoxedStash::new();
stash.set_summary("Found {count} errors:");
stash.push("a string error");
stash.push(anyhow!("an anyhow error"));

let wrapper = stash.to_error().unwrap();
let wrapper_as_anyhow: anyhow::Error = wrapper.into();
let wrapper_back: BoxedErrorList = wrapper_as_anyhow.downcast().unwrap();

§thiserror example

use thiserror::Error;

#[derive(Debug, Error)]
enum CustomChildError {
  #[error("Value is too big: {0}")]
  TooBig(usize),
  #[error("Value is too small: {0}")]
  TooSmall(usize),
}

#[derive(Debug, Error)]
#[error(transparent)]
struct ValidationError(#[from] BoxedErrorList);

let error = BoxedStash::new()
    .set_summary("Found {count} errors:")
    .push("a string error")
    .fail_now(CustomChildError::TooBig(100))
    .unwrap_err();
let result: Result<String, ValidationError> = Err(ValidationError(error));
let expected: &str = "
Found 2 errors:
- a string error
- Value is too big: 100
";
assert_eq!(expected.trim(), result.unwrap_err().to_string().trim());

The Rust standard library also implements From<&str> and From<String> for Box<dyn Error + Send + Sync + 'static>, so &str and String values can be passed directly to BoxedStash::push and BoxedStash::push_all, like this:

use errorstash::{ErrorStash, BoxedStash, BoxedError, StashableResult, StashErrorsIter};
use anyhow::{Error, bail};

let mut stash = BoxedStash::new();
stash.set_summary("Found {count} errors:");
stash.push("the first error");
let error2: String = "another string error".to_string();
stash.push(error2);

fn fail_if_negative(x: i32) -> Result<i32, anyhow::Error> {
   if x < 0 {
     bail!("Negative value: {}", x);
   }
   Ok(x)
}

let values: Vec<i32> = vec![10, -3, 5, -1].into_iter()
 .map(fail_if_negative)
 .stash_errors(&mut stash)
 .collect();

assert_eq!(vec![10, 5], values);

let wrapper = stash.to_error().unwrap();
assert_eq!(4, wrapper.len());

let expected: &str = "
Found 4 errors:
- the first error
- another string error
- Negative value: -3
- Negative value: -1
";

assert_eq!(expected.trim(), wrapper.to_string().trim());

§Formatting

The std::fmt::Display implementation for BoxedErrorList prints them in this format:

<summary line>
- <error 1>
- <error 2>
- <error 3>

Implementations§

Source§

impl BoxedStash

Source

pub fn new() -> Self

Creates a new BoxedStash with the default summary line.

Source

pub fn with_summary(summary: &'static str) -> Self

Creates a new BoxedStash with the given summary line.

Source

pub fn set_summary(&mut self, summary: &'static str) -> &mut Self

Sets a custom summary line for the wrapper error.

If the summary string contains the {count} placeholder, it will be replaced with the number of errors when formatted.

Source

pub fn set_summary_with( &mut self, summary_func: impl Fn() -> String + Send + Sync + 'static, ) -> &mut Self

Sets a custom summary line generator closure for the wrapper error.

The closure will be called when the summary is formatted. If the returned string contains the {count} placeholder, it will be replaced with the number of errors when formatted.

The provided closure may capture local state and must be Send + Sync so that the resulting BoxedStash remains thread-safe.

Source

pub fn push(&mut self, err: impl Into<BoxedError>) -> &mut Self

Adds a child error to the stash.

Source

pub fn push_all<T, It>(&mut self, errors: It) -> &mut Self
where It: IntoIterator<Item = T>, T: Into<BoxedError>,

Adds multiple child errors to the stash.

Source

pub fn check(&mut self, condition: bool, e: impl Into<BoxedError>) -> &mut Self

If the condition is false, adds error e to the stash. Otherwise, does nothing.

If you want to return immediately if the condition is false, chain a call to [fail_unless_empty] after this method. For example:

let mut stash = BoxedStash::new();
let value = 42;
stash.check(value > 100, "value must be greater than 100")
     .fail_unless_empty()?;
Source

pub fn fail_now( &mut self, e: impl Into<BoxedError>, ) -> Result<(), BoxedErrorList>

Adds an error and immediately returns Err(W) with all collected errors.

Trait Implementations§

Source§

impl Debug for BoxedStash

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for BoxedStash

Source§

fn default() -> BoxedStash

Returns the “default value” for a type. Read more
Source§

impl ErrorStash<Box<dyn Error + Sync + Send>, ErrorList<Box<dyn Error + Sync + Send>>> for BoxedStash

Source§

fn to_result<T>(self, closure: impl FnOnce() -> T) -> Result<T, BoxedErrorList>

If no errors have been collected, calls the closure and returns Ok(result). Otherwise, consumes the collected errors and returns Err(W).
Source§

fn to_error(self) -> Option<BoxedErrorList>

If no errors have been collected, returns None. Otherwise, consumes the collected errors and returns Some(W).
Source§

fn is_empty(&self) -> bool

Returns true if no errors have been collected. Read more
Source§

fn len(&self) -> usize

Returns the number of collected errors. Read more
Source§

fn ok(&self) -> Option<()>

If no errors have been collected, returns Some(()). Otherwise, returns None. Read more
Source§

fn check_fmt(&mut self, condition: bool, args: Arguments<'_>) -> &mut Self
where E: From<String>,

If the condition is false, adds a formatted error to the stash. Otherwise, does nothing. Read more
Source§

fn fail_unless_empty(&mut self) -> Result<(), W>

If no errors have been collected, returns Ok(()). Otherwise, consumes the collected errors and returns Err(W). Read more
Source§

impl<T> Extend<T> for BoxedStash
where T: Into<BoxedError>,

Source§

fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I)

Extends a collection with the contents of an iterator. Read more
Source§

fn extend_one(&mut self, item: A)

🔬This is a nightly-only experimental API. (extend_one)
Extends a collection with exactly one element.
Source§

fn extend_reserve(&mut self, additional: usize)

🔬This is a nightly-only experimental API. (extend_one)
Reserves capacity in a collection for the given number of additional elements. Read more
Source§

impl IntoIterator for BoxedStash

Source§

type Item = Box<dyn Error + Sync + Send>

The type of the elements being iterated over.
Source§

type IntoIter = IntoIter<Box<dyn Error + Sync + Send>>

Which kind of iterator are we turning this into?
Source§

fn into_iter(self) -> Self::IntoIter

Creates an iterator from a value. Read more
Source§

impl<T, FE> StashableResult<T, Box<dyn Error + Sync + Send>, ErrorList<Box<dyn Error + Sync + Send>>, BoxedStash> for Result<T, FE>
where FE: Into<BoxedError> + Any,

Lets you stash errors from a Result<T, FE> into a BoxedStash.

This implementation allows stashing errors from any error type FE that can be converted into a BoxedError using the Into trait.

If the result’s error type FE is itself an ErrorList<BoxedError> (i.e. the error type produced by another BoxedStash), its errors will be unpacked and individually added to the target BoxedStash (rather than nesting the entire ErrorList as a single error). In this case, summary message from the source ErrorList will be discarded, and only its child errors retained.

Note that this implementation leverages the Into<BoxedError> trait to auto-convert compatible error types into BoxedError. This behavior differs from the StashableResult implementation for TypedStash, which requires the error type to match exactly.

Source§

fn or_stash(self, stash: &mut BoxedStash) -> Option<T>

Consumes this Result, returning Some(T) if this result is ok, or collecting the error into the provided ErrorStash and returning None if this result is an error.

If the error type FE is an ErrorList<BoxedError>, its individual errors will be unpacked and added to the stash, rather than nesting the entire ErrorList as a single error.

Source§

fn or_fail(self, stash: &mut BoxedStash) -> Result<T, ErrorList<BoxedError>>

Consumes this Result, returning Ok(T) if this result is ok, or collecting the error into the provided ErrorStash and returning the aggregated errors in a Err(W) if this result is an error.

If the error type FE is an ErrorList<BoxedError>, its individual errors will be unpacked and added to the stash, rather than nesting the entire ErrorList as a single error.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.