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
//! [](https://crates.io/crates/error-stack-macros2)
//! [](https://crates.io/crates/error-stack-macros2)
//! [](https://github.com/LuisFerLCC/error-stack-macros2/actions/workflows/test.yml)
//! [](https://github.com/LuisFerLCC/error-stack-macros2/blob/master/.github/CODE_OF_CONDUCT.md)
//!
//! Community-made procedural macros for [`error-stack`].
//!
//! # Example
//!
//! Here is the same example shown in the [`error-stack`] `README`, modified to
//! use the macros provided by this crate:
//!
//! ```rust
//! use error_stack::{Report, ResultExt};
//! use error_stack_macros2::Error;
//!
//! #[derive(Debug, Error)]
//! #[display("invalid experiment description")]
//! struct ParseExperimentError;
//!
//! fn parse_experiment(
//! description: &str
//! ) -> Result<(u64, u64), Report<ParseExperimentError>> {
//! let value = description
//! .parse::<u64>()
//! .attach_with(|| {
//! format!("{description:?} could not be parsed as experiment")
//! })
//! .change_context(ParseExperimentError)?;
//!
//! Ok((value, 2 * value))
//! }
//!
//! #[derive(Debug, Error)]
//! #[display("experiment error: could not run experiment")]
//! struct ExperimentError;
//!
//! fn start_experiments(
//! experiment_ids: &[usize],
//! experiment_descriptions: &[&str],
//! ) -> Result<Vec<u64>, Report<ExperimentError>> {
//! let experiments = experiment_ids
//! .iter()
//! .map(|exp_id| {
//! let description = experiment_descriptions
//! .get(*exp_id)
//! .ok_or_else(|| {
//! Report::new(ExperimentError)
//! .attach(format!(
//! "experiment {exp_id} has no valid description")
//! )
//! })?;
//!
//! let experiment = parse_experiment(description)
//! .attach(format!("experiment {exp_id} could not be parsed"))
//! .change_context(ExperimentError)?;
//!
//! Ok(move || experiment.0 * experiment.1)
//! })
//! .collect::<Result<Vec<_>, Report<ExperimentError>>>()
//! .attach("unable to set up experiments")?;
//!
//! Ok(experiments.iter().map(|experiment| experiment()).collect())
//! }
//!
//! let experiment_ids = &[0, 2];
//! let experiment_descriptions = &["10", "20", "3o"];
//! let err = start_experiments(experiment_ids, experiment_descriptions)
//! .unwrap_err();
//!
//! assert_eq!(err.to_string(), "experiment error: could not run experiment");
//! ```
//!
//! # Support
//!
//! Need help using `error-stack-macros2`? Don't hesitate to reach out on
//! [GitHub Discussions](https://github.com/LuisFerLCC/error-stack-macros2/discussions/categories/q-a)!
//!
//! # Links
//!
//! - [Documentation]
//! - [GitHub](https://github.com/LuisFerLCC/error-stack-macros2)
//! - [crates.io](https://crates.io/crates/error-stack-macros2)
//!
//! # Contributing
//!
//! Before creating an issue, please consider the following:
//!
//! - Refer to the [documentation] to
//! make sure the error is actually a bug and not a mistake of your own.
//! - Make sure the issue hasn't already been reported or suggested.
//! - Please report any security vulnerabilities privately through
//! [Security Advisories](https://github.com/LuisFerLCC/error-stack-macros2/security/advisories/new).
//! - After following these steps, you can file an issue using one of our
//! [templates](https://github.com/LuisFerLCC/error-stack-macros2/issues/new/choose).
//! Please make sure to follow our
//! [Code of Conduct](https://github.com/LuisFerLCC/error-stack-macros2/blob/master/.github/CODE_OF_CONDUCT.md).
//! - If you wish to [submit a pull request](https://github.com/LuisFerLCC/error-stack-macros2/compare)
//! alongside your issue, please follow our
//! [contribution guidelines](https://github.com/LuisFerLCC/error-stack-macros2/blob/master/.github/CONTRIBUTING.md).
//!
//! # Disclaimer
//!
//! This crate is not affiliated with the official [`error-stack`] crate or its
//! maintainers.
//!
//! [`error-stack`]: https://crates.io/crates/error-stack
//! [documentation]: https://docs.rs/error-stack-macros2
use TokenStream;
use quote;
use parse_macro_input;
use ErrorStackDeriveInput;
/// Derive macro for the [`Error`] trait that implements the best practices for
/// [`error-stack`].
///
/// # Overview
/// This derive macro allows you to automatically implement the required
/// [`Display`] and [`Error`] traits for custom types that you want to use as
/// context types in [`error-stack`] [`Report`]s without all the boilerplate.
///
/// The macro has a `display` attribute, which specifies a formatting string to
/// print a value of the given type or enum variant.
///
/// # Examples
///
/// ## Unit struct (recommended)
///
/// ```
/// use error_stack_macros2::Error;
///
/// #[derive(Debug, Error)]
/// #[display("invalid card string")]
/// struct ParseCardError;
/// ```
///
/// ## Enum
///
/// ```
/// use error_stack_macros2::Error;
///
/// #[derive(Debug, Error)]
/// #[display("credit card error")] // optional default
/// enum CreditCardError {
/// #[display("credit card not found")]
/// InvalidInput(String),
///
/// #[display("failed to retrieve credit card")]
/// Other,
/// }
/// ```
///
/// ## Field interpolation (discouraged)
///
/// ```
/// use error_stack_macros2::Error;
///
/// #[derive(Debug, Error)]
/// #[display("invalid card string: {0:?}")]
/// struct ParseCardError(String);
///
/// let err = ParseCardError("1234567".to_string());
/// assert_eq!(err.to_string(), "invalid card string: \"1234567\"");
/// ```
///
/// # This may look familiar...
///
/// This derive macro is heavily inspired by the popular [`thiserror`] crate. In
/// fact, you **can** use the [`thiserror`] crate to derive the same traits for
/// your types. However, [`error-stack`] is very opinionated about how context
/// types should be designed and used, and this derive macro enforces those
/// best practices, whereas [`thiserror`] is more flexible and designed for
/// general use cases.
///
/// Also, due to this macro's more simple and restricted design, it can
/// potentially be more efficient than [`thiserror`] in terms of compile time
/// and generated code size.
///
/// [`Error`]: core::error::Error
/// [`error-stack`]: https://crates.io/crates/error-stack
/// [`Report`]: https://docs.rs/error-stack/latest/error_stack/struct.Report.html
/// [`Display`]: core::fmt::Display
/// [`thiserror`]: https://crates.io/crates/thiserror