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
extern crate syn;
use TokenStream;
use ;
/// Create fully qualified errors with the "error" attribute.
///
/// A fully qualified error means:
/// - std::error::Error is implemented
/// - std::fmt::Debug is implemented
/// - std::fmt::Display is implemented
/// Also, it's possible to generate implementations for std::convert::From for structs and enum variants with a single field.
///
/// The attribute is applicable for struct definitions and enum definitions. If it's used anywhere else,
/// the attribute panics. The only exception are enum variants if the enum definition holds the error attribute.
///
/// # structs
/// ## general usage
/// Add the attribute to the struct definition like this
/// ``` text
/// #[error(message = "Something went wrong!", impl_from)]
/// struct MyError {
/// faulty_value: usize
/// }
/// ```
/// which will create an equivalent implementation to this
/// ``` text
/// use std::fmt::Formatter;
///
/// #[derive(Debug)]
/// struct MyError {
/// faulty_value: usize
/// }
///
/// impl std::error::Error for MyError {}
///
/// impl std::fmt::Display for MyError {
/// fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
/// let e = self;
/// write!(f, "Something went wrong!")
/// }
/// }
///
/// impl std::convert::From for MyError {
/// fn from(val: usize) {
/// MyError { faulty_value: val }
/// }
/// }
/// ```
///
/// Generics, lifetimes and any other attributes will be preserved.
///
/// ## the parameter 'message'
/// The parameter 'message' is of type String. It is optional.
///
/// Providing 'message' will cause an implementation of std::fmt::Display for the struct,
/// while omitting it will allow you to implement it by yourself.
///
/// The message will be used to generate a call of the 'write!' macro. If the message has
/// substrings contained in braces '{...}', these parts will be transformed into expressions
/// in the order they appear. for example
/// ```text
/// message = "This is my val: {e.val}"
/// ```
/// will result in
/// ```text
/// write!(f, "This is my val: {}, e.val")
/// ```
///
/// The braces itself will be lost, so expressions with multiple statements must be contained in
/// an additional pair, like "Complex: {{let mut i = 0; i += 1; i}}".
///
/// To access the error struct itself and its fields/methods, you can just use 'self'.
///
/// ## the parameter 'impl_from'
/// The parameter 'impl_from' is of type bool. It is optional.
/// Just writing 'impl_from' is equivalent to 'impl_from = true',
/// omitting it is equivalent to 'impl_from = false'.
///
/// When 'impl_from' is true, an implementation of From for the type of
/// the single field of the struct will be created. If the struct has more
/// or less than one field, the attribute panics.
///
/// # enums
/// ## general usage
///
/// Add the attribute to the enum definition like this
/// ``` text
/// #[error(message = "Something went wrong")]
/// enum MyError {
/// Unknown,
/// #[error(message = "Parsing failed in line {line}")]
/// ParsingFailed { line: usize },
/// #[error(message = "Could not read file. Problem: {_0}", impl_from)]
/// ReadFileFailed(std::io::Error)
/// }
/// ```
/// which will create an equivalent implementation to this
/// ``` text
/// use std::fmt::Formatter;
/// use std::io::Error;
///
/// #[derive(Debug)]
/// enum MyError {
/// Unknown,
/// ParsingFailed { line: usize },
/// ReadFileFailed(std::io::Error)
/// }
///
/// impl std::error::Error for MyError {}
///
/// impl std::fmt::Display for MyError {
/// fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
/// match self {
/// e @ MyError::ParsingFailed { line } => write!(f, "Parsing failed in line {}", line),
/// e @ MyError::ReadFileFailed(_0) => write!(f, "Could not read file. Problem: {}", e),
/// _ => write!(f, "Something went wrong")
/// }
/// }
/// }
///
/// impl std::convert::From<std::io::Error> for MyError {
/// fn from(e: std::io::Error) -> Self {
/// MyError::ReadFileFailed(e)
/// }
/// }
/// ```
///
/// ## the parameter 'message'
/// The parameter 'message' is of type String. It is optional and can be used on enums and their variants.
///
/// Providing 'message' will cause an implementation of std::fmt::Display for the enum.
/// If neither the enum nor any of its variants has message set, Display will not be implemented.
/// The created implementation has the same capabilities like enums, so you can use expressions
/// to create better messages.
///
/// ### on enums
/// The value of 'message' on the enum itself will be used to generate a default message for every variant
/// without the 'message' parameter set. Just like structs, you can use self to get the whole enum.
///
/// ### on variants
/// A specific match arm in the Display implementation will be created when 'message' is used on a variant. Based
/// on its kind, the fields of the variant will be exposed and can be used in expressions:
///
/// If the variant uses named fields, all names will be usable just by their name. When using tuple like variants,
/// you can use the index of the field beginning with an underscore, like '_0' (as numbers aren't valid identifiers).
///
///
/// ## the parameter 'impl_from'
/// The parameter 'impl_from' is of type bool. It is optional.
/// Just writing 'impl_from' is equivalent to 'impl_from = true',
/// omitting it is equivalent to 'impl_from = false'.
///
/// Like for structs, it indicates that std::convert::From should be implemented for
/// an enum.
///
/// ### on enums
/// When used on enums, error_generator tries to create From implementations for every variant of the enum.
/// This only works if every variant has only one field.
///
/// ### on variants
/// When used on a variant, error_generator tries to implement From for the type of the variants single field.
/// This fails if the variant has more or less than one field.
///
/// # Important
/// error_generator will not check if the expressions in your Display messages are correct OR your chosen items for the From implementation interfere with other code.
/// This might lead to strange compiler errors due to wrong implementations.