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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
//! Derive macro for the `error2` crate.
//!
//! This crate provides the `#[derive(Error2)]` procedural macro for automatically
//! implementing error types with backtrace support.
//!
//! # Usage
//!
//! ```
//! use error2::prelude::*;
//!
//! #[derive(Debug, Error2)]
//! pub enum MyError {
//! #[error2(display("IO error"))]
//! Io {
//! source: std::io::Error,
//! backtrace: Backtrace,
//! },
//! }
//! ```
//!
//! See the main `error2` crate documentation for complete usage information.
use TokenStream;
use ;
/// Derives the `Error2` trait for an error type.
///
/// This macro automatically implements:
/// - `std::error::Error`
/// - `error2::Error2`
/// - `Display` (only if `#[error2(display(...))]` is specified)
///
/// It also generates helper structs (named `{Type}2` or `{Variant}2`) for type conversion.
///
/// # Attributes
///
/// ## Type-Level Attributes
///
/// Applied to the struct or enum definition:
///
/// ### `display`
///
/// Specifies the display format for the error message. Only applicable to structs.
/// If omitted, no `Display` implementation is generated, allowing custom implementation.
///
/// ```
/// # use error2::prelude::*;
/// #[derive(Debug, Error2)]
/// #[error2(display("Failed to process: {item}"))]
/// struct ProcessError {
/// item: String,
/// backtrace: Backtrace,
/// }
/// ```
///
/// ### `vis`
///
/// Controls the visibility of generated helper structs and their fields.
/// Default is inherited from the error type.
///
/// ```
/// # use error2::prelude::*;
/// #[derive(Debug, Error2)]
/// #[error2(vis(pub(crate)), display("error: {code}"))]
/// pub struct MyError {
/// code: i32,
/// backtrace: Backtrace,
/// }
/// // Generates:
/// // pub(crate) struct MyError2<T: Into<i32>> {
/// // pub(crate) code: T
/// // }
/// ```
///
/// ### `module`
///
/// Puts generated code into a submodule with the same name as the type (in snake_case).
///
/// ```
/// # use error2::prelude::*;
/// # mod test {
/// # use error2::prelude::*;
/// #[derive(Debug, Error2)]
/// #[error2(module, display("my error"))]
/// pub struct MyError {
/// backtrace: Backtrace,
/// }
///
/// #[derive(Debug, Error2)]
/// #[error2(module, display("read config error"))]
/// pub struct ReadConfigError {
/// backtrace: Backtrace,
/// }
///
/// // Generates:
/// // mod my_error { pub(super) struct MyError2; }
/// // mod read_config_error { pub(super) struct ReadConfigError2; }
///
/// // Usage - helper structs are in their respective modules:
/// # fn test() -> Result<(), MyError> {
/// my_error::MyError2.fail()?;
/// # Ok(())
/// # }
/// #
/// # fn test2() -> Result<(), ReadConfigError> {
/// read_config_error::ReadConfigError2.fail()?;
/// # Ok(())
/// # }
/// # }
/// ```
///
/// ## Variant-Level Attributes
///
/// Applied to enum variants:
///
/// ### `display`
///
/// Specifies the display format for this variant. If omitted, no `Display`
/// implementation is generated for this variant.
///
/// ```
/// # use error2::prelude::*;
/// #[derive(Debug, Error2)]
/// pub enum AppError {
/// #[error2(display("IO error at {path}"))]
/// Io {
/// path: String,
/// source: std::io::Error,
/// backtrace: Backtrace,
/// },
///
/// #[error2(display("Not found: {item}"))]
/// NotFound { item: String, backtrace: Backtrace },
/// }
/// ```
///
/// # Generated Helper Structs
///
/// The macro generates helper structs for type conversion, named by appending `2`:
///
/// **For structs:**
/// ```
/// # use error2::prelude::*;
/// # use std::fmt;
/// #[derive(Debug, Error2)]
/// #[error2(display("my error"))]
/// struct MyError {
/// backtrace: Backtrace,
/// }
///
/// // Generates: struct MyError2;
/// ```
///
/// **For enum variants:**
/// ```
/// # use error2::prelude::*;
/// #[derive(Debug, Error2)]
/// enum AppError {
/// #[error2(display("file error"))]
/// FileError {
/// source: std::io::Error,
/// backtrace: Backtrace,
/// },
/// }
/// // Generates: struct FileError2;
/// ```
///
/// These helper structs contain only the non-`source` and non-`backtrace` fields.
/// **All fields are generic with `Into` trait bounds**, allowing automatic type conversion:
///
/// ```
/// # use error2::prelude::*;
/// #[derive(Debug, Error2)]
/// #[error2(display("read error: {path}"))]
/// struct ReadError {
/// path: String,
/// source: std::io::Error,
/// backtrace: Backtrace,
/// }
/// // Generates (assuming ReadError has inherited visibility):
/// // struct ReadError2<T: Into<String>> { path: T }
///
/// # fn example() -> Result<(), ReadError> {
/// // No need to call .into() - automatic conversion:
/// std::fs::read_to_string("file.txt").context(ReadError2 { path: "file.txt" })?; // &str -> String
/// //
/// # Ok(())
/// # }
/// ```
///
/// For expensive conversions, use `.with_context()` for lazy evaluation:
///
/// ```
/// # use error2::prelude::*;
/// # use std::path::Path;
/// # #[derive(Debug, Error2)]
/// # #[error2(display("read error: {path}"))]
/// # struct ReadError {
/// # path: String,
/// # source: std::io::Error,
/// # backtrace: Backtrace,
/// # }
/// # fn example(path: &Path) -> Result<(), ReadError> {
/// // Only converts on error:
/// std::fs::read(path).with_context(|| ReadError2 {
/// path: path.display().to_string(),
/// })?;
/// # Ok(())
/// # }
/// ```
///
/// # Boxing Large Source Errors
///
/// To avoid large `Result<T, E>` types, you can wrap the source error in a `Box` or other wrapper:
///
/// ```
/// # use error2::prelude::*;
/// use std::io;
///
/// #[derive(Debug, Error2)]
/// #[error2(display("IO error: {path}"))]
/// struct IoError {
/// path: String,
/// // Box the source to keep Result size small
/// source: Box<io::Error>,
/// backtrace: Backtrace,
/// }
///
/// # fn example(path: &str) -> Result<(), IoError> {
/// // io::Error is automatically boxed via Into trait
/// std::fs::read_to_string(path).context(IoError2 { path })?;
/// # Ok(())
/// # }
/// ```
///
/// This works because the helper struct implements `Into<Box<E>>` for `E`.
/// Any wrapper type that implements `E: Into<Wrapper<E>>` can be used.
///
/// # Display Implementation
///
/// **Important:** The `Display` trait is only implemented when `display` attribute is present.
/// This allows you to provide custom `Display` implementations:
///
/// ```
/// # use error2::prelude::*;
/// use std::fmt;
///
/// #[derive(Debug, Error2)]
/// struct MyError {
/// code: i32,
/// backtrace: Backtrace,
/// }
///
/// // Custom Display implementation (no display attribute needed)
/// impl fmt::Display for MyError {
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
/// write!(f, "Error code: {:#x}", self.code)
/// }
/// }
/// ```