1 #![allow(clippy::uninlined_format_args)]
2 
3 use std::fmt::{self, Display};
4 use thiserror::Error;
5 
assert<T: Display>(expected: &str, value: T)6 fn assert<T: Display>(expected: &str, value: T) {
7     assert_eq!(expected, value.to_string());
8 }
9 
10 #[test]
test_braced()11 fn test_braced() {
12     #[derive(Error, Debug)]
13     #[error("braced error: {msg}")]
14     struct Error {
15         msg: String,
16     }
17 
18     let msg = "T".to_owned();
19     assert("braced error: T", Error { msg });
20 }
21 
22 #[test]
test_braced_unused()23 fn test_braced_unused() {
24     #[derive(Error, Debug)]
25     #[error("braced error")]
26     struct Error {
27         extra: usize,
28     }
29 
30     assert("braced error", Error { extra: 0 });
31 }
32 
33 #[test]
test_tuple()34 fn test_tuple() {
35     #[derive(Error, Debug)]
36     #[error("tuple error: {0}")]
37     struct Error(usize);
38 
39     assert("tuple error: 0", Error(0));
40 }
41 
42 #[test]
test_unit()43 fn test_unit() {
44     #[derive(Error, Debug)]
45     #[error("unit error")]
46     struct Error;
47 
48     assert("unit error", Error);
49 }
50 
51 #[test]
test_enum()52 fn test_enum() {
53     #[derive(Error, Debug)]
54     enum Error {
55         #[error("braced error: {id}")]
56         Braced { id: usize },
57         #[error("tuple error: {0}")]
58         Tuple(usize),
59         #[error("unit error")]
60         Unit,
61     }
62 
63     assert("braced error: 0", Error::Braced { id: 0 });
64     assert("tuple error: 0", Error::Tuple(0));
65     assert("unit error", Error::Unit);
66 }
67 
68 #[test]
test_constants()69 fn test_constants() {
70     #[derive(Error, Debug)]
71     #[error("{MSG}: {id:?} (code {CODE:?})")]
72     struct Error {
73         id: &'static str,
74     }
75 
76     const MSG: &str = "failed to do";
77     const CODE: usize = 9;
78 
79     assert("failed to do: \"\" (code 9)", Error { id: "" });
80 }
81 
82 #[test]
test_inherit()83 fn test_inherit() {
84     #[derive(Error, Debug)]
85     #[error("{0}")]
86     enum Error {
87         Some(&'static str),
88         #[error("other error")]
89         Other(&'static str),
90     }
91 
92     assert("some error", Error::Some("some error"));
93     assert("other error", Error::Other("..."));
94 }
95 
96 #[test]
test_brace_escape()97 fn test_brace_escape() {
98     #[derive(Error, Debug)]
99     #[error("fn main() {{}}")]
100     struct Error;
101 
102     assert("fn main() {}", Error);
103 }
104 
105 #[test]
test_expr()106 fn test_expr() {
107     #[derive(Error, Debug)]
108     #[error("1 + 1 = {}", 1 + 1)]
109     struct Error;
110     assert("1 + 1 = 2", Error);
111 }
112 
113 #[test]
test_nested()114 fn test_nested() {
115     #[derive(Error, Debug)]
116     #[error("!bool = {}", not(.0))]
117     struct Error(bool);
118 
119     #[allow(clippy::trivially_copy_pass_by_ref)]
120     fn not(bool: &bool) -> bool {
121         !*bool
122     }
123 
124     assert("!bool = false", Error(true));
125 }
126 
127 #[test]
test_match()128 fn test_match() {
129     #[derive(Error, Debug)]
130     #[error("{}: {0}", match .1 {
131         Some(n) => format!("error occurred with {}", n),
132         None => "there was an empty error".to_owned(),
133     })]
134     struct Error(String, Option<usize>);
135 
136     assert(
137         "error occurred with 1: ...",
138         Error("...".to_owned(), Some(1)),
139     );
140     assert(
141         "there was an empty error: ...",
142         Error("...".to_owned(), None),
143     );
144 }
145 
146 #[test]
test_nested_display()147 fn test_nested_display() {
148     // Same behavior as the one in `test_match`, but without String allocations.
149     #[derive(Error, Debug)]
150     #[error("{}", {
151         struct Msg<'a>(&'a String, &'a Option<usize>);
152         impl<'a> Display for Msg<'a> {
153             fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
154                 match self.1 {
155                     Some(n) => write!(formatter, "error occurred with {}", n),
156                     None => write!(formatter, "there was an empty error"),
157                 }?;
158                 write!(formatter, ": {}", self.0)
159             }
160         }
161         Msg(.0, .1)
162     })]
163     struct Error(String, Option<usize>);
164 
165     assert(
166         "error occurred with 1: ...",
167         Error("...".to_owned(), Some(1)),
168     );
169     assert(
170         "there was an empty error: ...",
171         Error("...".to_owned(), None),
172     );
173 }
174 
175 #[test]
test_void()176 fn test_void() {
177     #[allow(clippy::empty_enum)]
178     #[derive(Error, Debug)]
179     #[error("...")]
180     pub enum Error {}
181 
182     let _: Error;
183 }
184 
185 #[test]
test_mixed()186 fn test_mixed() {
187     #[derive(Error, Debug)]
188     #[error("a={a} :: b={} :: c={c} :: d={d}", 1, c = 2, d = 3)]
189     struct Error {
190         a: usize,
191         d: usize,
192     }
193 
194     assert("a=0 :: b=1 :: c=2 :: d=3", Error { a: 0, d: 0 });
195 }
196 
197 #[test]
test_ints()198 fn test_ints() {
199     #[derive(Error, Debug)]
200     enum Error {
201         #[error("error {0}")]
202         Tuple(usize, usize),
203         #[error("error {0}", '?')]
204         Struct { v: usize },
205     }
206 
207     assert("error 9", Error::Tuple(9, 0));
208     assert("error ?", Error::Struct { v: 0 });
209 }
210 
211 #[test]
test_trailing_comma()212 fn test_trailing_comma() {
213     #[derive(Error, Debug)]
214     #[error(
215         "error {0}",
216     )]
217     #[rustfmt::skip]
218     struct Error(char);
219 
220     assert("error ?", Error('?'));
221 }
222 
223 #[test]
test_field()224 fn test_field() {
225     #[derive(Debug)]
226     struct Inner {
227         data: usize,
228     }
229 
230     #[derive(Error, Debug)]
231     #[error("{}", .0.data)]
232     struct Error(Inner);
233 
234     assert("0", Error(Inner { data: 0 }));
235 }
236 
237 #[test]
test_macro_rules()238 fn test_macro_rules() {
239     // Regression test for https://github.com/dtolnay/thiserror/issues/86
240 
241     macro_rules! decl_error {
242         ($variant:ident($value:ident)) => {
243             #[derive(Debug, Error)]
244             pub enum Error0 {
245                 #[error("{0:?}")]
246                 $variant($value),
247             }
248 
249             #[derive(Debug, Error)]
250             #[error("{0:?}")]
251             pub enum Error1 {
252                 $variant($value),
253             }
254         };
255     }
256 
257     decl_error!(Repro(u8));
258 
259     assert("0", Error0::Repro(0));
260     assert("0", Error1::Repro(0));
261 }
262 
263 #[test]
test_raw()264 fn test_raw() {
265     #[derive(Error, Debug)]
266     #[error("braced raw error: {r#fn}")]
267     struct Error {
268         r#fn: &'static str,
269     }
270 
271     assert("braced raw error: T", Error { r#fn: "T" });
272 }
273 
274 #[test]
test_raw_enum()275 fn test_raw_enum() {
276     #[derive(Error, Debug)]
277     enum Error {
278         #[error("braced raw error: {r#fn}")]
279         Braced { r#fn: &'static str },
280     }
281 
282     assert("braced raw error: T", Error::Braced { r#fn: "T" });
283 }
284 
285 #[test]
test_raw_conflict()286 fn test_raw_conflict() {
287     #[derive(Error, Debug)]
288     enum Error {
289         #[error("braced raw error: {r#func}, {func}", func = "U")]
290         Braced { r#func: &'static str },
291     }
292 
293     assert("braced raw error: T, U", Error::Braced { r#func: "T" });
294 }
295 
296 #[test]
test_keyword()297 fn test_keyword() {
298     #[derive(Error, Debug)]
299     #[error("error: {type}", type = 1)]
300     struct Error;
301 
302     assert("error: 1", Error);
303 }
304