locate_error/lib.rs
1//! `locate-error` is a fairly minimal procedural macro which simply adds location
2//! information (file, line, column) to where errors occur when the `From<Error> for OtherError`
3//! trait is called on `Error`, typically triggered on `?` when raising errors across functions.
4//! This is intended to be used with `thiserror::Error` when using the `#[from]` attribute.
5//! This is useful with nested types which derive `thiserror::Error` and you want to bubble up
6//! errors recursively, keeping location information for where each intermediate error occurred.
7//! `thiserror` does support `std::backtrace::Backtrace` but as of this writing requires a nightly
8//! compiler.
9//!
10//! The goal is to help users have another resource to add useful context to error messages to
11//! avoid unhelpful messages where the display/debug info of the bottom most error is bubbled up
12//! without context.
13//! ```text
14//! $ cargo run
15//! Error: Program ended
16//! ```
17//! (this was created by a real program)
18//!
19//! An output like the above is created by running the example program below but replacing
20//! `#[locate_from]` with the vanilla `#[from]` attribute and removing the associated `Location`
21//! information.
22//!
23//! This is intended to supplement other crates such as `anyhow` which can provide context for
24//! errors. `snafu` is an alternative which has stable backtrace support.
25//!
26//! # Example Usage
27//! ```text
28//! [dependencies]
29//! locate-error = "0.1"
30//! ```
31//!
32//! Before describing the components, an example gives more context on usage. A typical use with
33//! `thiserror` with nested errors would be:
34//!
35//! ```rust
36//! use locate_error::Locate;
37//! use locate_error::Location;
38//! use locate_error::location;
39//! use thiserror::Error;
40//!
41//!
42//! #[derive(Error, Debug, Locate)]
43//! pub enum OuterError {
44//! #[error("{0} \n\toccurred at {1}")]
45//! MiddleError(#[locate_from] MiddleError, Location),
46//! }
47//!
48//! #[derive(Error, Debug, Locate)]
49//! #[error("{inner_error} \n\toccurred at {location}")]
50//! pub struct MiddleError {
51//! #[locate_from]
52//! inner_error: InnerError,
53//! location: Location,
54//! }
55//!
56//! #[derive(Error, Debug)]
57//! #[error("{message} \n\toccurred at {location}")]
58//! pub struct InnerError {
59//! message: String,
60//! location: Location,
61//! }
62//!
63//! fn main() {
64//! let err: OuterError = raise_middle_error().unwrap_err().into();
65//! println!("{:}", err);
66//! }
67//!
68//! fn raise_middle_error() -> Result<(), MiddleError> {
69//! raise_inner_error()?;
70//! Ok(())
71//! }
72//!
73//! fn raise_inner_error() -> Result<(), InnerError> {
74//! Err(InnerError {
75//! message: format!("Error: Program ended"),
76//! location: location!(),
77//! })
78//! }
79//! ```
80//!
81//! Which outputs
82//! ```text
83//! Exception raised in a local function
84//! occurred at app/src/bin/locate_error.rs:40:19
85//! occurred at app/src/bin/locate_error.rs:33:5
86//! occurred at app/src/bin/locate_error.rs:28:61
87//! ```
88//!
89//! # Components
90//! This crate introduces only a few components:
91//! - The `Location` type which holds a file, column, and line number
92//! - The `Locate` derive macro which uses the `#[locate_from]` attribute to implement the
93//! `From<Inner> for Outer` trait for the modified inner error type.
94//! - The `location` macro which returns a `Location` corresponding to the call site
95//!
96//! Enum variants or structs that use the `#[locate_from]` attribute must also include a field of
97//! type `Location` which will be automatically populated with the location where the `From` trait
98//! is called. Since an additional field is added, `thiserror` attributes such as
99//! `#[error(transparent)]` do not work, so a display message must be provided.
100
101// Enum variants or structs that use the `#[locate_from]` attribute must also include a field of type `Location` which will be automatically populated with the location where the `From` trait is called. Since an additional field is added, `thiserror` attributes such as `#[error(transparent)]` do not work, so a display message must be provided.
102pub use locate_error_core::Location;
103pub use locate_error_core::location;
104pub use locate_error_derive::Locate;