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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
//! A crate for high level error propogation with software controlled backtraces
//! that are entirely independent of the `RUST_BACKTRACE` system.
//!
//! In Rust development, major crates will often have their own error enums that
//! work well in their own specialized domain, but when orchestrating many
//! domains together we run into issues. `map_err` is very annoying to work
//! with. In `async` call stacks or cases where we can't easily control
//! `RUST_BACKTRACE`, we run into an especially annoying problem
//! where the same kind of error can be returned from multiple places, and we
//! are sometimes forced into `println` debugging to find out where it is
//! actually from. This crate introduces the `StackableErr` trait and a
//! "stackable" error type that allows for both software-defined error
//! backtraces and easily converting errors into the stackable error type.
//!
//! This crate is similar to `eyre`, but has a more efficient internal layout
//! with a `ThinVec` array of `SmallBox`es, works with `no_std`, implements
//! `core::error::Error`, and more.
//!
//! Some partial examples of what using the crate looks like:
//!
//! ```text
//! f.map_err(|e| Error::from_err(e))?;
//! // replace the above with
//! f.stack()?; // uses `#[track_caller]` when an error is being propagated
//! ```
//! ```text
//! let dir = self
//! .path
//! .parent()
//! .stack_err("FileOptions::preacquire() -> empty path")?
//! .to_str()
//! .stack_err("bad OsStr conversion")?;
//! ```
//! ```text
//! // arbitrary things implementing `Display + Send + Sync + 'static` can be stacked
//! f.stack_err(arbitrary)?;
//! // readily swappable with `anyhow` and `eyre` due to extra method, trait, and
//! // struct aliases
//! f.wrap_err(arbitrary)?;
//! f.context(arbitrary)?;
//! ```
//! ```text
//! // The trait is implemented for options so that we don't need `OptionExt` like
//! // `eyre` does
//! option.take()
//! .stack_err("`Struct` has already been taken")?
//! .wait_with_output()
//! .await
//! .stack_err_with(|| {
//! format!("{self:?}.xyz() -> failed when waiting")
//! })?;
//! ```
//! ```text
//! return Err(Error::from_err(format!(
//! "failure of {x:?} to complete"
//! )))
//! // replace the above with
//! bail!("failure of {x:?} to complete")
//! ```
//! ```text
//! // when the error type is already `stacked_errors::Error` you can do this if it is
//! // preferable over `map`
//! return match ... {
//! Ok(ok) => {
//! ...
//! }
//! Err(e) => Err(e.add_err(format!("myfunction(.., host: {host})"))),
//! }
//! ```
//!
//! ```
//! use stacked_errors::{bail, Error, Result, StackableErr};
//!
//! // Note that `Error` uses `ThinVec` internally, which means that it often
//! // takes up only the stack space of a `usize` or the size of the `T` plus
//! // a byte.
//! fn innermost(s: &str) -> Result<u8> {
//! if s == "return error" {
//! bail!("bottom level `StrErr`")
//! }
//! if s == "parse invalid" {
//! // This is the common case where we have some external
//! // crate function that returns a `Result<..., E: Error>`. We
//! // usually call `StackableErr::stack_err` if we want to attach
//! // some message to it right away (it is called with a closure
//! // so that it doesn't have impact on the `Ok` cases). Otherwise, we
//! // just call `StackableErr::stack` so that just the location is
//! // pushed on the stack. We can then use `?` directly.
//!
//! let _: () = ron::from_str("invalid")
//! .stack_err_with(|| format!("parsing error with \"{s}\""))?;
//! }
//! Ok(42)
//! }
//!
//! fn inner(s: &str) -> Result<u16> {
//! // Chainable with other combinators. Use `stack_err` with a message for
//! // propogating up the stack when the error is something that should
//! // have some mid layer information attached for it for quick diagnosis
//! // by the user. Otherwise use just `stack` which will also do error
//! // conversion if necessary, avoiding needing to wrangle with `map_err`.
//!
//! let x = innermost(s)
//! .map(|x| u16::from(x))
//! .stack_err_with(|| format!("error from innermost(\"{s}\")"))?;
//! Ok(x)
//! }
//!
//! fn outer(s: &str) -> Result<u64> {
//! // ...
//!
//! let x = inner(s).stack()?;
//!
//! // ...
//! Ok(u64::from(x))
//! }
//!
//! let res = format!("{:?}", outer("valid"));
//! assert_eq!(res, "Ok(42)");
//!
//! // The line numbers are slightly off because this is a doc test.
//! // In order from outer to the innermost call, it lists the location of the
//! // `stack` call from `outer`, the location of `stack_err` from `inner`,
//! // the associated error message, the location of either the `Error::from`
//! // or `stack_err` from `innermost`, and finally the root error message.
//!
//! // note that debug mode (used when returning errors from the main function)
//! // includes terminal styling
//! println!("{:?}", outer("return error"));
//! let res = format!("{}", outer("return error").unwrap_err());
//! assert_eq!(
//! res,
//! r#"
//! at src/lib.rs 45:22
//! error from innermost("return error") at src/lib.rs 38:10
//! bottom level `StrErr` at src/lib.rs 12:9"#
//! );
//!
//! println!("{:?}", outer("parse invalid"));
//! let res = format!("{}", outer("parse invalid").unwrap_err());
//! assert_eq!(
//! res,
//! r#"
//! at src/lib.rs 45:22
//! error from innermost("parse invalid") at src/lib.rs 38:10
//! parsing error with "parse invalid" at src/lib.rs 24:14
//! 1:1: Expected unit"#
//! );
//! ```
//!
//! ```text
//! // in commonly used functions you may want `_locationless` to avoid adding
//! // on unnecessary information if the location is already being added on
//! return Err(e.add_err_locationless(ErrorKind::TimeoutError)).stack_err_with(|| {
//! format!(
//! "wait_for_ok(num_retries: {num_retries}, delay: {delay:?}) timeout, \
//! last error stack was:"
//! )
//! })
//! ```
extern crate alloc;
pub use ;
pub use ;
pub use *;
pub use StackableErr;
/// A shorthand for [core::result::Result<T, stacked_errors::Error>]
pub type Result<T> = Result;
/// used by the macros