n0_error/lib.rs
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 101 102 103 104 105 106
//! A library for ergonomic errors with call-site location data
//!
//! This crate provides a [`StackError`] trait and [`stack_error`] proc macro to
//! ergonomically work with enum or struct errors.
//!
//! * All errors that use the macro will implement the [`StackError`] trait,
//! which exposes call-site metadata indicating where the error occurred. Its
//! [`source`](StackError::source) method returns an [`ErrorRef`], which is an
//! enum over either a reference to a [`std::error::Error`] or another
//! [`StackError`]. This allows retrieving error locations for the full error
//! chain, as long as all errors are stack errors.
//!
//! * The proc macro can add a `meta` field to structs or enum variants. This
//! field is the source for the call-site location accessed through the
//! `StackError` trait. There is a simple declarative macro [`e!`](e) that
//! provides an ergonomic way to construct errors with a `meta` field without
//! having to spell it out everywhere.
//!
//! * This crate also provides an [`AnyError`] type, which is similar to
//! [`anyhow`](https://docs.rs/anyhow/latest/anyhow/). If constructed from an
//! error that implements `StackError`, the call-site location is preserved.
//! The `AnyError` type is generally recommended for applications or tests,
//! whereas libraries should use concrete errors with the macro.
//!
//! * There are result extensions to convert `StackError`s or `std::error::Error`s
//! to `AnyError` while providing additional context.
//!
//! * While all errors using the derive macro from this crate have a `From`
//! implementation for `AnyError`, regular std errors do not. Unfortunately, a
//! blanket `From` implementation for all std errors would prevent a
//! specialized implementation for stack errors, causing loss of location
//! metadata upon conversion to `AnyError`. Therefore, you need to use the
//! [`std_context`](StdResultExt::std_context) or
//! [`anyerr`](StdResultExt::anyerr) methods to convert results with std errors
//! to [`AnyError`]. You should not use these methods on stack errors; instead,
//! use [`context`](StackResultExt::context) or simply forward with `?`.
//!
//! The call-site metadata in the `meta` field is collected only if the
//! environment variable `RUST_BACKTRACE=1` or `RUST_ERROR_LOCATION=1` is set.
//! Otherwise, it is not collected, as doing so has a small performance overhead.
//!
//! Both [`AnyError`] and all errors that use the
//! [derive macro](derive@StackError) feature the following outputs:
//!
//! * Display impl (`{error}`) prints only the message of the outermost error
//! `failed to process input`
//! * Alternate display impl (`{error:#}`) prints the message for each error in the chain, in a single line
//! `failed to process input: invalid input: wanted 23 but got 13`
//! * Debug impl (`{error:?}`) prints the message and each source, on separate lines.
//! ```text
//! failed to process input
//! Caused by:
//! invalid input
//! wanted 23 but got 13
//! ```
//!
//! If `RUST_BACKTRACE` or `RUST_ERROR_LOCATION` is set, this will also print the call site of each error.
//! ```text
//! failed to process input (examples/basic.rs:61:17)
//! Caused by:
//! invalid input (examples/basic.rs:36:5)
//! wanted 23 but got 13 (examples/basic.rs:48:13)
//! ```
//! * Alternate debug impl `{error:#?}`: An output similar to how the `#[derive(Debug)]` output looks.
//!
//! ### Feature flags
//!
//! * `anyhow` (off by default): Enables `From<anyhow::Error> for AnyError`
//!
//! ## Example
//!
//! ```rust
#![doc = include_str!("../examples/basic.rs")]
//! ```
#![deny(missing_docs, rustdoc::broken_intra_doc_links)]
pub use n0_error_macros::{StackError, stack_error};
mod any;
mod error;
mod ext;
mod macros;
mod meta;
#[cfg(test)]
mod tests;
pub use self::{any::*, error::*, ext::*, macros::*, meta::*};
/// `Result` type alias where the error type defaults to [`AnyError`].
pub type Result<T = (), E = AnyError> = std::result::Result<T, E>;
/// Returns a result with the error type set to [`AnyError`].
///
/// Equivalent to `Ok::<_, AnyError>(value)`.
#[allow(non_snake_case)]
pub fn Ok<T>(value: T) -> Result<T, AnyError> {
std::result::Result::Ok(value)
}
/// Ensures we can use the macros within this crate as well.
extern crate self as n0_error;
/// Ensure the code in the README compiles
#[cfg(doctest)]
#[doc = include_str!("../README.md")]
mod readme_doctest {}