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
//! The primary macro in this crate is `errors`, designed to simplify error handling by allowing developers to define and restrict error types directly within functions. This reduces boilerplate code and improves readability.
//!
//! # Usage
//!
//! ## Basic Example
//!
//! Below is a basic example of how to use the `errors` macro:
//!
//! ```rust
//! # use error_mancer::prelude::*;
//!
//! #[errors(std::io::Error)]
//! fn foo() -> Result<i32, _> {
//! std::fs::File::open("hello.txt")?;
//! Ok(10)
//! }
//!
//! fn bar() {
//! match foo() {
//! Err(FooError::StdIo(_)) => {/* Handle error */},
//! Ok(_) => {/* Handle success */}
//! }
//! }
//! ```
//!
//! This macro automatically generates an enum resembling the following and sets the `Result` error type to it, so developers do not need to manually define it:
//!
//! ```rust,ignore
//! // Auto generated code from `#[errors]`
//! #[derive(Debug)]
//! enum FooError {
//! StdIo(std::io::Error),
//! }
//!
//! impl From<std::io::Error> for FooError { ... }
//! impl Display for FooError { ... }
//! impl Error for FooError {}
//! ```
//!
//! Defining no errors also works, which will generate an enum with no variants, enforcing that no errors are returned. This is useful for functions that are guaranteed not to fail but still require a `Result<...>` return type, such as in trait implementations. It provides extra safety by ensuring that no error paths are possible.
//!
//! ## Enum name
//! You can also explicitly set the enum name by using a Ident instead of `_` in the signature
//! ```rust
//! # use error_mancer::prelude::*;
//!
//! #[errors(std::io::Error)]
//! fn foo() -> Result<i32, MyErrorEnum> {
//! std::fs::File::open("hello.txt")?;
//! Ok(10)
//! }
//!
//! fn bar() {
//! match foo() {
//! Err(MyErrorEnum::StdIo(_)) => {/* ... */},
//! Ok(_) => {/* ... */}
//! }
//! }
//! ```
//!
//! ## Usage in `impl` Blocks
//!
//! To use the macro within an `impl` block, the block must also be annotated:
//!
//! ```rust
//! # use error_mancer::prelude::*;
//! # struct MyStruct;
//!
//! #[errors]
//! impl MyStruct {
//! #[errors(std::io::Error)]
//! fn method(&self) -> Result<(), _> {
//! Ok(())
//! }
//! }
//! ```
//!
//! ## Usage with `anyhow::Result`
//!
//! The macro can also be used without overwriting an error type and is fully compatible with `anyhow::Result` and similar types. This is especially useful for developers who prefer using `anyhow` for general error handling but want to benefit from additional error type restrictions when needed, particularly in trait implementations:
//!
//! ```rust,compile_fail
//! # use error_mancer::prelude::*;
//!
//! #[errors]
//! fn foo() -> anyhow::Result<()> {
//! // ❌ Compiler error: `std::fs::File::open(...)` returns `std::io::Error`,
//! // but `#[errors]` enforces that no errors are allowed.
//! std::fs::File::open("hello.txt")?;
//! Ok(())
//! }
//! ```
//!
//! ## Upcasting types
//! ```rust
//! # use error_mancer::prelude::*;
//! # use thiserror::Error;
//! # #[derive(Error, Debug)]
//! # #[error("1")]
//! # struct Err1;
//! # #[derive(Error, Debug)]
//! # #[error("2")]
//! # struct Err2;
//! # #[derive(Error, Debug)]
//! # #[error("3")]
//! # struct Err3;
//!
//! #[errors(Err1, Err2)]
//! fn foo() -> Result<i32, _> {
//! // ...
//! # todo!()
//! }
//!
//! #[errors(Err1, Err2, Err3)]
//! fn bar() -> Result<i32, _> {
//! let result = foo().into_super_error::<BarError>()?;
//! Ok(result)
//! }
//! ```
//!
//! ## Deriving traits for generated enum
//! You can annotate the function with `#[derive]` to derive traits for the generated enum.
//! Note that the `#[derive]` macro must be used after the `errors` macro. (technically in `impl`
//! blocks the order doesnt matter, but we recommend using `#[derive]` after `errors` for consistency.)
//! ```rust
//! # use error_mancer::prelude::*;
//! # use thiserror::Error;
//! # #[derive(Error, Debug, Clone)]
//! # #[error("1")]
//! # struct Err1;
//!
//! #[errors(Err1)]
//! #[derive(Clone)]
//! fn foo() -> Result<(), _> {
//! Ok(())
//! }
//!
//! fn bar() {
//! let _ = foo().clone();
//! }
//! ```
//!
//! # Specifics and Implementation Details
//!
//! ## Error Type Overwriting
//!
//! The macro looks for a type named `Result` in the root of the return type. If the second generic argument is `_`, it replaces it with the appropriate error type. See the examples below:
//!
//! | Original | Modified |
//! | --------------------------- | ------------------------------------------------ |
//! | `Result<T, _>` | `Result<T, FooError>` |
//! | `Result<T, CustomName>` | `Result<T, CustomName>` |
//! | `Result<T, Box<dyn Error>>` | `Result<T, Box<dyn Error>>` |
//! | `std::result::Result<T, _>` | `std::result::Result<T, FooError>` |
//! | `anyhow::Result<T>` | `anyhow::Result<T>` |
//! | `Vec<Result<T, _>>` | ❌ compiler error, nested types arent replaced |
//!
//! ## Enum Visibility
//!
//! The enum can either be emitted inside the function body, or alongside the function (in which
//! case it takes on the functions visibility) based on the
//! following conditions:
//! * If the second generic argument in `Result` is `_` its emitted outside the function.
//! * If the second generic argument in `Result` is a simple identifier its emitted outside.
//! * Otherwise, the enum is generated inside the function body and cannot be accessed externally.
//!
//! (in the following example the enums are include to illustrate where the macro would
//! generate them.)
//! ```rust,ignore
//! #[errors]
//! fn foo() -> Result<(), _> { ... }
//!
//! // enum FooError {...}
//!
//! #[errors]
//! pub fn bar() -> Result<(), _> { ... }
//!
//! // pub enum FooError {...}
//!
//! #[errors]
//! pub fn baz() -> Result<(), CustomName> { ... }
//!
//! // pub enum CustomName {...}
//!
//! #[errors]
//! pub fn secret() -> anyhow::Result<()> {
//! // enum SecretError { ... }
//! // ...
//! }
//! ```
//!
//! ## Naming Conventions
//!
//! The enum name is derived from the function name, converted to Pascal case using the `case_fold` crate to conform to Rust naming conventions for types and enums. Similarly, variant names are derived from the path segments of the types, with the "Error" suffix removed if present. For example, `std::io::Error` would produce a variant called `StdIo`, while `io::Error` would produce `Io`.
//!
//! ## Display Implementation
//!
//! The `Display` implementation simply delegates to each contained error, ensuring consistent and readable error messages.
//!
//! ## `into_super_error`
//! This function uses the `FlattenInto` trait which is automatically implemented by the macro for
//! its errors, for all target types which implemnt `From<...>` for each of the errors variants. i.e a generated
//! implementation might look like:
//! ```rust
//! # use error_mancer::{FlattenInto, errors};
//! # use thiserror::Error;
//! # #[derive(Error, Debug)]
//! # #[error("1")]
//! # struct Err1;
//! # #[derive(Error, Debug)]
//! # #[error("2")]
//! # struct Err2;
//! # enum OurError {
//! # Err1(Err1),
//! # Err2(Err2)
//! # }
//!
//! impl<T> FlattenInto<T> for OurError
//! where T: From<Err1> + From<Err2> {
//! fn flatten(self) -> T {
//! match self {
//! Self::Err1(err) => T::from(err),
//! Self::Err2(err) => T::from(err),
//! }
//! }
//! }
//! ```
pub use errors;
/// This trait allows a error to be flattened into another one and is automatically implemented by
/// the `#[errors]` macro for all super errors that implement `From<...>` for each of its fields.
/// This trait extends `Result` with an additional method to upcast a error enum.