Skip to main content

ai_quick_error/
lib.rs

1#![cfg_attr(not(test), no_std)]
2#![warn(missing_docs, rust_2018_idioms)]
3//! A macro which makes errors easy to write
4//!
5//! Minimum type is like this:
6//!
7//! ```rust
8//! #[macro_use] extern crate ai_quick_error;
9//! # fn main() {}
10//!
11//! ai_quick_error! {
12//!     #[derive(Debug)]
13//!     pub enum SomeError {
14//!         Variant1 {}
15//!     }
16//! }
17//! ```
18//! Both ``pub`` and non-public types may be declared, and all meta attributes
19//! (such as ``#[derive(Debug)]``) are forwarded as is. The `Debug` must be
20//! implemented (but you may do that yourself if you like). The documentation
21//! comments ``/// something`` (as well as other meta attrbiutes) on variants
22//! are allowed.
23//!
24//! # Allowed Syntax
25//!
26//! You may add arbitrary parameters to any struct variant:
27//!
28//! ```rust
29//! # #[macro_use] extern crate ai_quick_error;
30//! # fn main() {}
31//! #
32//! ai_quick_error! {
33//!     #[derive(Debug)]
34//!     pub enum SomeError {
35//!         /// IO Error
36//!         Io(err: std::io::Error) {}
37//!         /// Utf8 Error
38//!         Utf8(err: std::str::Utf8Error) {}
39//!     }
40//! }
41//! ```
42//!
43//! Note unlike in normal Enum declarations you declare names of fields (which
44//! are omitted from type). How they can be used is outlined below.
45//!
46//! Now you might have noticed trailing braces `{}`. They are used to define
47//! implementations. By default:
48//!
49//! * `Error::source()` returns None (even if type wraps some value)
50//! * `Display` outputs debug representation
51//! * No `From` implementations are defined
52//!
53//! ```rust
54//! # #[macro_use] extern crate ai_quick_error;
55//! # fn main() {}
56//! #
57//! ai_quick_error! {
58//!     #[derive(Debug)]
59//!     pub enum SomeError {
60//!         Io(err: std::io::Error) {
61//!             display("{}", err)
62//!         }
63//!         Utf8(err: std::str::Utf8Error) {
64//!             display("utf8 error")
65//!         }
66//!     }
67//! }
68//! ```
69//!
70//! To change `source` method to return some error, add `source(value)`, for
71//! example:
72//!
73//! ```rust
74//! # #[macro_use] extern crate ai_quick_error;
75//! # fn main() {}
76//! #
77//! ai_quick_error! {
78//!     #[derive(Debug)]
79//!     pub enum SomeError {
80//!         Io(err: std::io::Error) {
81//!             source(err)
82//!         }
83//!         Utf8(err: std::str::Utf8Error) {
84//!             display("utf8 error")
85//!         }
86//!         Other(err: Box<std::error::Error>) {
87//!             source(&**err)
88//!         }
89//!     }
90//! }
91//! ```
92//! Note you don't need to wrap value in `Some`, its implicit. In case you want
93//! `None` returned just omit the `source`. You can't return `None`
94//! conditionally.
95//!
96//! To change how each clause is `Display`ed add `display(pattern,..args)`,
97//! for example:
98//!
99//! ```rust
100//! # #[macro_use] extern crate ai_quick_error;
101//! # fn main() {}
102//! #
103//! ai_quick_error! {
104//!     #[derive(Debug)]
105//!     pub enum SomeError {
106//!         Io(err: std::io::Error) {
107//!             display("I/O error: {}", err)
108//!         }
109//!         Utf8(err: std::str::Utf8Error) {
110//!             display("Utf8 error, valid up to {}", err.valid_up_to())
111//!         }
112//!     }
113//! }
114//! ```
115//!
116//! If you need a reference to the error when `Display`ing, you can instead use
117//! `display(x) -> (pattern, ..args)`, where `x` sets the name of the reference.
118//!
119//! ```rust
120//! # #[macro_use] extern crate ai_quick_error;
121//! # fn main() {}
122//! #
123//! use std::error::Error; // put methods like `source()` of this trait into scope
124//!
125//! ai_quick_error! {
126//!     #[derive(Debug)]
127//!     pub enum SomeError {
128//!         Io(err: std::io::Error) {
129//!             display(x) -> ("I/O: {}", err)
130//!         }
131//!         Utf8(err: std::str::Utf8Error) {
132//!             display(self_) -> ("UTF-8 error. Valid up to {}", err.valid_up_to())
133//!         }
134//!     }
135//! }
136//! ```
137//!
138//! To convert to the type from any other, use one of the three forms of
139//! `from` clause.
140//!
141//! For example, to convert simple wrapper use bare `from()`:
142//!
143//! ```rust
144//! # #[macro_use] extern crate ai_quick_error;
145//! # fn main() {}
146//! #
147//! ai_quick_error! {
148//!     #[derive(Debug)]
149//!     pub enum SomeError {
150//!         Io(err: std::io::Error) {
151//!             from()
152//!         }
153//!     }
154//! }
155//! ```
156//!
157//! This implements ``From<io::Error>``.
158//!
159//! To convert to singleton enumeration type (discarding the value), use
160//! the `from(type)` form:
161//!
162//! ```rust
163//! # #[macro_use] extern crate ai_quick_error;
164//! # fn main() {}
165//! #
166//! ai_quick_error! {
167//!     #[derive(Debug)]
168//!     pub enum SomeError {
169//!         FormatError {
170//!             from(std::fmt::Error)
171//!         }
172//!     }
173//! }
174//! ```
175//!
176//! And the most powerful form is `from(var: type) -> (arguments...)`. It
177//! might be used to convert to type with multiple arguments or for arbitrary
178//! value conversions:
179//!
180//! ```rust
181//! # #[macro_use] extern crate ai_quick_error;
182//! # fn main() {}
183//! #
184//! ai_quick_error! {
185//!     #[derive(Debug)]
186//!     pub enum SomeError {
187//!         FailedOperation(s: &'static str, errno: i32) {
188//!             from(errno: i32) -> ("os error", errno)
189//!             from(e: std::io::Error) -> ("io error", e.raw_os_error().unwrap())
190//!         }
191//!         /// Converts from both kinds of utf8 errors
192//!         Utf8(err: std::str::Utf8Error) {
193//!             from()
194//!             from(err: std::string::FromUtf8Error) -> (err.utf8_error())
195//!         }
196//!     }
197//! }
198//! ```
199//! # Context
200//!
201//! Since ai-quick-error 1.1 we also have a `context` declaration, which is
202//! similar to (the longest form of) `from`, but allows adding some context to
203//! the error. We need a longer example to demonstrate this:
204//!
205//! ```rust
206//! # #[macro_use] extern crate ai_quick_error;
207//! # use std::io;
208//! # use std::fs::File;
209//! # use std::path::{Path, PathBuf};
210//! #
211//! use ai_quick_error::ResultExt;
212//!
213//! ai_quick_error! {
214//!     #[derive(Debug)]
215//!     pub enum Error {
216//!         File(filename: PathBuf, err: io::Error) {
217//!             context(path: &'a Path, err: io::Error)
218//!                 -> (path.to_path_buf(), err)
219//!         }
220//!     }
221//! }
222//!
223//! fn openfile(path: &Path) -> Result<(), Error> {
224//!     File::open(path).context(path)?;
225//!
226//!     // If we didn't have context, the line above would be written as;
227//!     //
228//!     // File::open(path)
229//!     //     .map_err(|err| Error::File(path.to_path_buf(), err))?;
230//!
231//!     Ok(())
232//! }
233//!
234//! # fn main() {
235//! #     openfile(Path::new("/etc/somefile")).ok();
236//! # }
237//! ```
238//!
239//! Each `context(a: A, b: B)` clause implements
240//! `From<Context<A, B>> for Error`. Which means multiple `context` clauses
241//! are a subject to the normal coherence rules. Unfortunately, we can't
242//! provide full support of generics for the context, but you may either use a
243//! lifetime `'a` for references or `AsRef<Type>` (the latter means `A:
244//! AsRef<Type>`, and `Type` must be concrete). It's also occasionally useful
245//! to use a tuple as a type of the first argument.
246//!
247//! You also need to `use ai_quick_error::ResultExt` extension trait to get
248//! working `.context()` method.
249//!
250//! More info on context in [this article](http://bit.ly/1PsuxDt).
251//!
252//! All forms of `from`, `display`, `source`, and `context`
253//! clauses can be combined and put in arbitrary order. Only `from` and
254//! `context` can be used multiple times in single variant of enumeration.
255//! Docstrings are also okay.  Empty braces can be omitted as of ai_quick_error
256//! 0.1.3.
257//!
258//! # Private Enums
259//!
260//! Since ai-quick-error 1.2.0 we  have a way to make a private enum that is
261//! wrapped by public structure:
262//!
263//! ```rust
264//! #[macro_use] extern crate ai_quick_error;
265//! # fn main() {}
266//!
267//! ai_quick_error! {
268//!     #[derive(Debug)]
269//!     pub enum PubError wraps ErrorEnum {
270//!         Variant1 {}
271//!     }
272//! }
273//! ```
274//!
275//! This generates data structures like this
276//!
277//! ```rust
278//!
279//! pub struct PubError(ErrorEnum);
280//!
281//! enum ErrorEnum {
282//!     Variant1,
283//! }
284//!
285//! ```
286//!
287//! Which in turn allows you to export just `PubError` in your crate and keep
288//! actual enumeration private to the crate. This is useful to keep backwards
289//! compatibility for error types. Currently there is no shorcuts to define
290//! error constructors for the inner type, but we consider adding some in
291//! future versions.
292//!
293//! It's possible to declare internal enum as public too.
294//!
295//!
296
297/// Main macro that does all the work
298#[macro_export]
299macro_rules! ai_quick_error {
300
301    (   $(#[$meta:meta])*
302        pub enum $name:ident { $($chunks:tt)* }
303    ) => {
304        ai_quick_error!(SORT [pub enum $name $(#[$meta])* ]
305            items [] buf []
306            queue [ $($chunks)* ]);
307    };
308    (   $(#[$meta:meta])*
309        enum $name:ident { $($chunks:tt)* }
310    ) => {
311        ai_quick_error!(SORT [enum $name $(#[$meta])* ]
312            items [] buf []
313            queue [ $($chunks)* ]);
314    };
315
316    (   $(#[$meta:meta])*
317        pub enum $name:ident wraps $enum_name:ident { $($chunks:tt)* }
318    ) => {
319        ai_quick_error!(WRAPPER $enum_name [ pub struct ] $name $(#[$meta])*);
320        ai_quick_error!(SORT [enum $enum_name $(#[$meta])* ]
321            items [] buf []
322            queue [ $($chunks)* ]);
323    };
324
325    (   $(#[$meta:meta])*
326        pub enum $name:ident wraps pub $enum_name:ident { $($chunks:tt)* }
327    ) => {
328        ai_quick_error!(WRAPPER $enum_name [ pub struct ] $name $(#[$meta])*);
329        ai_quick_error!(SORT [pub enum $enum_name $(#[$meta])* ]
330            items [] buf []
331            queue [ $($chunks)* ]);
332    };
333    (   $(#[$meta:meta])*
334        enum $name:ident wraps $enum_name:ident { $($chunks:tt)* }
335    ) => {
336        ai_quick_error!(WRAPPER $enum_name [ struct ] $name $(#[$meta])*);
337        ai_quick_error!(SORT [enum $enum_name $(#[$meta])* ]
338            items [] buf []
339            queue [ $($chunks)* ]);
340    };
341
342    (   $(#[$meta:meta])*
343        enum $name:ident wraps pub $enum_name:ident { $($chunks:tt)* }
344    ) => {
345        ai_quick_error!(WRAPPER $enum_name [ struct ] $name $(#[$meta])*);
346        ai_quick_error!(SORT [pub enum $enum_name $(#[$meta])* ]
347            items [] buf []
348            queue [ $($chunks)* ]);
349    };
350
351
352    (
353        WRAPPER $internal:ident [ $($strdef:tt)* ] $strname:ident
354        $(#[$meta:meta])*
355    ) => {
356        $(#[$meta])*
357        $($strdef)* $strname ( $internal );
358
359        impl ::core::fmt::Display for $strname {
360            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>)
361                -> ::core::fmt::Result
362            {
363                ::core::fmt::Display::fmt(&self.0, f)
364            }
365        }
366
367        impl From<$internal> for $strname {
368            fn from(err: $internal) -> Self {
369                $strname(err)
370            }
371        }
372
373        impl ::core::error::Error for $strname {
374            fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
375                self.0.source()
376            }
377        }
378    };
379
380    // Queue is empty, can do the work
381    (SORT [enum $name:ident $( #[$meta:meta] )*]
382        items [$($( #[$imeta:meta] )*
383                  => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*]
384                                {$( $ifuncs:tt )*} )* ]
385        buf [ ]
386        queue [ ]
387    ) => {
388        ai_quick_error!(ENUM_DEFINITION [enum $name $( #[$meta] )*]
389            body []
390            queue [$($( #[$imeta] )*
391                      => $iitem: $imode [$( $ivar: $ityp ),*] )*]
392        );
393        ai_quick_error!(IMPLEMENTATIONS $name {$(
394           $iitem: $imode [$(#[$imeta])*] [$( $ivar: $ityp ),*] {$( $ifuncs )*}
395           )*});
396        $(
397            ai_quick_error!(ERROR_CHECK $imode $($ifuncs)*);
398        )*
399    };
400    (SORT [pub enum $name:ident $( #[$meta:meta] )*]
401        items [$($( #[$imeta:meta] )*
402                  => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*]
403                                {$( $ifuncs:tt )*} )* ]
404        buf [ ]
405        queue [ ]
406    ) => {
407        ai_quick_error!(ENUM_DEFINITION [pub enum $name $( #[$meta] )*]
408            body []
409            queue [$($( #[$imeta] )*
410                      => $iitem: $imode [$( $ivar: $ityp ),*] )*]
411        );
412        ai_quick_error!(IMPLEMENTATIONS $name {$(
413           $iitem: $imode [$(#[$imeta])*] [$( $ivar: $ityp ),*] {$( $ifuncs )*}
414           )*});
415        $(
416            ai_quick_error!(ERROR_CHECK $imode $($ifuncs)*);
417        )*
418    };
419    // Add meta to buffer
420    (SORT [$( $def:tt )*]
421        items [$($( #[$imeta:meta] )*
422                  => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*]
423                                {$( $ifuncs:tt )*} )* ]
424        buf [$( #[$bmeta:meta] )*]
425        queue [ #[$qmeta:meta] $( $tail:tt )*]
426    ) => {
427        ai_quick_error!(SORT [$( $def )*]
428            items [$( $(#[$imeta])* => $iitem: $imode [$( $ivar:$ityp ),*] {$( $ifuncs )*} )*]
429            buf [$( #[$bmeta] )* #[$qmeta] ]
430            queue [$( $tail )*]);
431    };
432    // Add ident to buffer
433    (SORT [$( $def:tt )*]
434        items [$($( #[$imeta:meta] )*
435                  => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*]
436                                {$( $ifuncs:tt )*} )* ]
437        buf [$( #[$bmeta:meta] )*]
438        queue [ $qitem:ident $( $tail:tt )*]
439    ) => {
440        ai_quick_error!(SORT [$( $def )*]
441            items [$( $(#[$imeta])*
442                      => $iitem: $imode [$( $ivar:$ityp ),*] {$( $ifuncs )*} )*]
443            buf [$(#[$bmeta])* => $qitem : UNIT [ ] ]
444            queue [$( $tail )*]);
445    };
446    // Flush buffer on meta after ident
447    (SORT [$( $def:tt )*]
448        items [$($( #[$imeta:meta] )*
449                  => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*]
450                                {$( $ifuncs:tt )*} )* ]
451        buf [$( #[$bmeta:meta] )*
452            => $bitem:ident: $bmode:tt [$( $bvar:ident: $btyp:ty ),*] ]
453        queue [ #[$qmeta:meta] $( $tail:tt )*]
454    ) => {
455        ai_quick_error!(SORT [$( $def )*]
456            items [$($( #[$imeta:meta] )*
457                      => $iitem: $imode [$( $ivar:$ityp ),*] {$( $ifuncs )*} )*
458                     $bitem: $bmode [$( $bvar:$btyp ),*] {} ]
459            buf [ #[$qmeta] ]
460            queue [$( $tail )*]);
461    };
462    // Add tuple enum-variant
463    (SORT [$( $def:tt )*]
464        items [$($( #[$imeta:meta] )*
465                  => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*]
466                                {$( $ifuncs:tt )*} )* ]
467        buf [$( #[$bmeta:meta] )* => $bitem:ident: UNIT [ ] ]
468        queue [($( $qvar:ident: $qtyp:ty ),+) $( $tail:tt )*]
469    ) => {
470        ai_quick_error!(SORT [$( $def )*]
471            items [$( $(#[$imeta])* => $iitem: $imode [$( $ivar:$ityp ),*] {$( $ifuncs )*} )*]
472            buf [$( #[$bmeta] )* => $bitem: TUPLE [$( $qvar:$qtyp ),+] ]
473            queue [$( $tail )*]
474        );
475    };
476    // Add struct enum-variant - e.g. { descr: &'static str }
477    (SORT [$( $def:tt )*]
478        items [$($( #[$imeta:meta] )*
479                  => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*]
480                                {$( $ifuncs:tt )*} )* ]
481        buf [$( #[$bmeta:meta] )* => $bitem:ident: UNIT [ ] ]
482        queue [{ $( $qvar:ident: $qtyp:ty ),+} $( $tail:tt )*]
483    ) => {
484        ai_quick_error!(SORT [$( $def )*]
485            items [$( $(#[$imeta])* => $iitem: $imode [$( $ivar:$ityp ),*] {$( $ifuncs )*} )*]
486            buf [$( #[$bmeta] )* => $bitem: STRUCT [$( $qvar:$qtyp ),+] ]
487            queue [$( $tail )*]);
488    };
489    // Add struct enum-variant, with excess comma - e.g. { descr: &'static str, }
490    (SORT [$( $def:tt )*]
491        items [$($( #[$imeta:meta] )*
492                  => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*]
493                                {$( $ifuncs:tt )*} )* ]
494        buf [$( #[$bmeta:meta] )* => $bitem:ident: UNIT [ ] ]
495        queue [{$( $qvar:ident: $qtyp:ty ),+ ,} $( $tail:tt )*]
496    ) => {
497        ai_quick_error!(SORT [$( $def )*]
498            items [$( $(#[$imeta])* => $iitem: $imode [$( $ivar:$ityp ),*] {$( $ifuncs )*} )*]
499            buf [$( #[$bmeta] )* => $bitem: STRUCT [$( $qvar:$qtyp ),+] ]
500            queue [$( $tail )*]);
501    };
502    // Add braces and flush always on braces
503    (SORT [$( $def:tt )*]
504        items [$($( #[$imeta:meta] )*
505                  => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*]
506                                {$( $ifuncs:tt )*} )* ]
507        buf [$( #[$bmeta:meta] )*
508                 => $bitem:ident: $bmode:tt [$( $bvar:ident: $btyp:ty ),*] ]
509        queue [ {$( $qfuncs:tt )*} $( $tail:tt )*]
510    ) => {
511        ai_quick_error!(SORT [$( $def )*]
512            items [$( $(#[$imeta])* => $iitem: $imode [$( $ivar:$ityp ),*] {$( $ifuncs )*} )*
513                      $(#[$bmeta])* => $bitem: $bmode [$( $bvar:$btyp ),*] {$( $qfuncs )*} ]
514            buf [ ]
515            queue [$( $tail )*]);
516    };
517    // Flush buffer on double ident
518    (SORT [$( $def:tt )*]
519        items [$($( #[$imeta:meta] )*
520                  => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*]
521                                {$( $ifuncs:tt )*} )* ]
522        buf [$( #[$bmeta:meta] )*
523                 => $bitem:ident: $bmode:tt [$( $bvar:ident: $btyp:ty ),*] ]
524        queue [ $qitem:ident $( $tail:tt )*]
525    ) => {
526        ai_quick_error!(SORT [$( $def )*]
527            items [$( $(#[$imeta])* => $iitem: $imode [$( $ivar:$ityp ),*] {$( $ifuncs )*} )*
528                     $(#[$bmeta])* => $bitem: $bmode [$( $bvar:$btyp ),*] {} ]
529            buf [ => $qitem : UNIT [ ] ]
530            queue [$( $tail )*]);
531    };
532    // Flush buffer on end
533    (SORT [$( $def:tt )*]
534        items [$($( #[$imeta:meta] )*
535                  => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*]
536                                {$( $ifuncs:tt )*} )* ]
537        buf [$( #[$bmeta:meta] )*
538            => $bitem:ident: $bmode:tt [$( $bvar:ident: $btyp:ty ),*] ]
539        queue [ ]
540    ) => {
541        ai_quick_error!(SORT [$( $def )*]
542            items [$( $(#[$imeta])* => $iitem: $imode [$( $ivar:$ityp ),*] {$( $ifuncs )*} )*
543                     $(#[$bmeta])* => $bitem: $bmode [$( $bvar:$btyp ),*] {} ]
544            buf [ ]
545            queue [ ]);
546    };
547    // Public enum (Queue Empty)
548    (ENUM_DEFINITION [pub enum $name:ident $( #[$meta:meta] )*]
549        body [$($( #[$imeta:meta] )*
550            => $iitem:ident ($(($( $ttyp:ty ),+))*) {$({$( $svar:ident: $styp:ty ),*})*} )* ]
551        queue [ ]
552    ) => {
553        #[allow(unknown_lints)]  // no unused_doc_comments in older rust
554        #[allow(renamed_and_removed_lints)]
555        #[allow(unused_doc_comment)]
556        #[allow(unused_doc_comments)]
557        $(#[$meta])*
558        pub enum $name {
559            $(
560                $(#[$imeta])*
561                $iitem $(($( $ttyp ),+))* $({$( $svar: $styp ),*})*,
562            )*
563        }
564    };
565    // Private enum (Queue Empty)
566    (ENUM_DEFINITION [enum $name:ident $( #[$meta:meta] )*]
567        body [$($( #[$imeta:meta] )*
568            => $iitem:ident ($(($( $ttyp:ty ),+))*) {$({$( $svar:ident: $styp:ty ),*})*} )* ]
569        queue [ ]
570    ) => {
571        #[allow(unknown_lints)]  // no unused_doc_comments in older rust
572        #[allow(renamed_and_removed_lints)]
573        #[allow(unused_doc_comment)]
574        #[allow(unused_doc_comments)]
575        $(#[$meta])*
576        enum $name {
577            $(
578                $(#[$imeta])*
579                $iitem $(($( $ttyp ),+))* $({$( $svar: $styp ),*})*,
580            )*
581        }
582    };
583    // Unit variant
584    (ENUM_DEFINITION [$( $def:tt )*]
585        body [$($( #[$imeta:meta] )*
586            => $iitem:ident ($(($( $ttyp:ty ),+))*) {$({$( $svar:ident: $styp:ty ),*})*} )* ]
587        queue [$( #[$qmeta:meta] )*
588            => $qitem:ident: UNIT [ ] $( $queue:tt )*]
589    ) => {
590        ai_quick_error!(ENUM_DEFINITION [ $($def)* ]
591            body [$($( #[$imeta] )* => $iitem ($(($( $ttyp ),+))*) {$({$( $svar: $styp ),*})*} )*
592                    $( #[$qmeta] )* => $qitem () {} ]
593            queue [ $($queue)* ]
594        );
595    };
596    // Tuple variant
597    (ENUM_DEFINITION [$( $def:tt )*]
598        body [$($( #[$imeta:meta] )*
599            => $iitem:ident ($(($( $ttyp:ty ),+))*) {$({$( $svar:ident: $styp:ty ),*})*} )* ]
600        queue [$( #[$qmeta:meta] )*
601            => $qitem:ident: TUPLE [$( $qvar:ident: $qtyp:ty ),+] $( $queue:tt )*]
602    ) => {
603        ai_quick_error!(ENUM_DEFINITION [ $($def)* ]
604            body [$($( #[$imeta] )* => $iitem ($(($( $ttyp ),+))*) {$({$( $svar: $styp ),*})*} )*
605                    $( #[$qmeta] )* => $qitem (($( $qtyp ),+)) {} ]
606            queue [ $($queue)* ]
607        );
608    };
609    // Struct variant
610    (ENUM_DEFINITION [$( $def:tt )*]
611        body [$($( #[$imeta:meta] )*
612            => $iitem:ident ($(($( $ttyp:ty ),+))*) {$({$( $svar:ident: $styp:ty ),*})*} )* ]
613        queue [$( #[$qmeta:meta] )*
614            => $qitem:ident: STRUCT [$( $qvar:ident: $qtyp:ty ),*] $( $queue:tt )*]
615    ) => {
616        ai_quick_error!(ENUM_DEFINITION [ $($def)* ]
617            body [$($( #[$imeta] )* => $iitem ($(($( $ttyp ),+))*) {$({$( $svar: $styp ),*})*} )*
618                    $( #[$qmeta] )* => $qitem () {{$( $qvar: $qtyp ),*}} ]
619            queue [ $($queue)* ]
620        );
621    };
622    (IMPLEMENTATIONS
623        $name:ident {$(
624            $item:ident: $imode:tt [$(#[$imeta:meta])*] [$( $var:ident: $typ:ty ),*] {$( $funcs:tt )*}
625        )*}
626    ) => {
627        #[allow(unused_variables)]
628        #[allow(unknown_lints)]  // no unused_doc_comments in older rust
629        #[allow(renamed_and_removed_lints)]
630        #[allow(unused_doc_comment)]
631        #[allow(unused_doc_comments)]
632        impl ::core::fmt::Display for $name {
633            fn fmt(&self, fmt: &mut ::core::fmt::Formatter<'_>)
634                -> ::core::fmt::Result
635            {
636                match *self {
637                    $(
638                        $(#[$imeta])*
639                        ai_quick_error!(ITEM_PATTERN
640                            $name $item: $imode [$( ref $var ),*]
641                        ) => {
642                            let display_fn = ai_quick_error!(FIND_DISPLAY_IMPL
643                                $name $item: $imode
644                                {$( $funcs )*});
645
646                            display_fn(self, fmt)
647                        }
648                    )*
649                }
650            }
651        }
652        #[allow(unused_variables)]
653        #[allow(unknown_lints)]  // no unused_doc_comments in older rust
654        #[allow(renamed_and_removed_lints)]
655        #[allow(unused_doc_comment)]
656        #[allow(unused_doc_comments)]
657        impl ::core::error::Error for $name {
658            fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
659                match *self {
660                    $(
661                        $(#[$imeta])*
662                        ai_quick_error!(ITEM_PATTERN
663                            $name $item: $imode [$( ref $var ),*]
664                        ) => {
665                            ai_quick_error!(FIND_SOURCE_IMPL
666                                $item: $imode [$( $var ),*]
667                                {$( $funcs )*})
668                        }
669                    )*
670                }
671            }
672        }
673        $(
674            ai_quick_error!(FIND_FROM_IMPL
675                $name $item: $imode [$( $var:$typ ),*]
676                {$( $funcs )*});
677        )*
678        $(
679            ai_quick_error!(FIND_CONTEXT_IMPL
680                $name $item: $imode [$( $var:$typ ),*]
681                {$( $funcs )*});
682        )*
683    };
684    (FIND_DISPLAY_IMPL $name:ident $item:ident: $imode:tt
685        { display($self_:tt) -> ($( $exprs:tt )*) $( $tail:tt )*}
686    ) => {
687        |ai_quick_error!(IDENT $self_): &$name, f: &mut ::core::fmt::Formatter<'_>| { write!(f, $( $exprs )*) }
688    };
689    (FIND_DISPLAY_IMPL $name:ident $item:ident: $imode:tt
690        { display($pattern:expr) $( $tail:tt )*}
691    ) => {
692        |_, f: &mut ::core::fmt::Formatter<'_>| { write!(f, $pattern) }
693    };
694    (FIND_DISPLAY_IMPL $name:ident $item:ident: $imode:tt
695        { display($pattern:expr, $( $exprs:tt )*) $( $tail:tt )*}
696    ) => {
697        |_, f: &mut ::core::fmt::Formatter<'_>| { write!(f, $pattern, $( $exprs )*) }
698    };
699    (FIND_DISPLAY_IMPL $name:ident $item:ident: $imode:tt
700        { $t:tt $( $tail:tt )*}
701    ) => {
702        ai_quick_error!(FIND_DISPLAY_IMPL
703            $name $item: $imode
704            {$( $tail )*})
705    };
706    (FIND_DISPLAY_IMPL $name:ident $item:ident: $imode:tt
707        { }
708    ) => {
709        |self_: &$name, f: &mut ::core::fmt::Formatter<'_>| {
710            write!(f, "{:?}", self_)
711        }
712    };
713    (FIND_SOURCE_IMPL $item:ident: $imode:tt
714        [$( $var:ident ),*]
715        { source($expr:expr) $( $tail:tt )*}
716    ) => {
717        Some($expr)
718    };
719    (FIND_SOURCE_IMPL $item:ident: $imode:tt
720        [$( $var:ident ),*]
721        { $t:tt $( $tail:tt )*}
722    ) => {
723        ai_quick_error!(FIND_SOURCE_IMPL
724            $item: $imode [$( $var ),*]
725            { $($tail)* })
726    };
727    (FIND_SOURCE_IMPL $item:ident: $imode:tt
728        [$( $var:ident ),*]
729        { }
730    ) => {
731        None
732    };
733    // ----------------------------- FROM IMPL --------------------------
734    (FIND_FROM_IMPL $name:ident $item:ident: $imode:tt
735        [$( $var:ident: $typ:ty ),*]
736        { from() $( $tail:tt )*}
737    ) => {
738        $(
739            impl From<$typ> for $name {
740                fn from($var: $typ) -> $name {
741                    $name::$item($var)
742                }
743            }
744        )*
745        ai_quick_error!(FIND_FROM_IMPL
746            $name $item: $imode [$( $var:$typ ),*]
747            {$( $tail )*});
748    };
749    (FIND_FROM_IMPL $name:ident $item:ident: UNIT
750        [ ]
751        { from($ftyp:ty) $( $tail:tt )*}
752    ) => {
753        impl From<$ftyp> for $name {
754            fn from(_discarded_error: $ftyp) -> $name {
755                $name::$item
756            }
757        }
758        ai_quick_error!(FIND_FROM_IMPL
759            $name $item: UNIT [  ]
760            {$( $tail )*});
761    };
762    (FIND_FROM_IMPL $name:ident $item:ident: TUPLE
763        [$( $var:ident: $typ:ty ),*]
764        { from($fvar:ident: $ftyp:ty) -> ($( $texpr:expr ),*) $( $tail:tt )*}
765    ) => {
766        impl From<$ftyp> for $name {
767            fn from($fvar: $ftyp) -> $name {
768                $name::$item($( $texpr ),*)
769            }
770        }
771        ai_quick_error!(FIND_FROM_IMPL
772            $name $item: TUPLE [$( $var:$typ ),*]
773            { $($tail)* });
774    };
775    (FIND_FROM_IMPL $name:ident $item:ident: STRUCT
776        [$( $var:ident: $typ:ty ),*]
777        { from($fvar:ident: $ftyp:ty) -> {$( $tvar:ident: $texpr:expr ),*} $( $tail:tt )*}
778    ) => {
779        impl From<$ftyp> for $name {
780            fn from($fvar: $ftyp) -> $name {
781                $name::$item {
782                    $( $tvar: $texpr ),*
783                }
784            }
785        }
786        ai_quick_error!(FIND_FROM_IMPL
787            $name $item: STRUCT [$( $var:$typ ),*]
788            { $($tail)* });
789    };
790    (FIND_FROM_IMPL $name:ident $item:ident: $imode:tt
791        [$( $var:ident: $typ:ty ),*]
792        { $t:tt $( $tail:tt )*}
793    ) => {
794        ai_quick_error!(FIND_FROM_IMPL
795            $name $item: $imode [$( $var:$typ ),*]
796            {$( $tail )*}
797        );
798    };
799    (FIND_FROM_IMPL $name:ident $item:ident: $imode:tt
800        [$( $var:ident: $typ:ty ),*]
801        { }
802    ) => {
803    };
804    // ----------------------------- CONTEXT IMPL --------------------------
805    (FIND_CONTEXT_IMPL $name:ident $item:ident: TUPLE
806        [$( $var:ident: $typ:ty ),*]
807        { context($cvar:ident: AsRef<$ctyp:ty>, $fvar:ident: $ftyp:ty)
808            -> ($( $texpr:expr ),*) $( $tail:tt )* }
809    ) => {
810        impl<T: AsRef<$ctyp>> From<$crate::Context<T, $ftyp>> for $name {
811            fn from(
812                $crate::Context($cvar, $fvar): $crate::Context<T, $ftyp>)
813                -> $name
814            {
815                $name::$item($( $texpr ),*)
816            }
817        }
818        ai_quick_error!(FIND_CONTEXT_IMPL
819            $name $item: TUPLE [$( $var:$typ ),*]
820            { $($tail)* });
821    };
822    (FIND_CONTEXT_IMPL $name:ident $item:ident: TUPLE
823        [$( $var:ident: $typ:ty ),*]
824        { context($cvar:ident: $ctyp:ty, $fvar:ident: $ftyp:ty)
825            -> ($( $texpr:expr ),*) $( $tail:tt )* }
826    ) => {
827        impl<'a> From<$crate::Context<$ctyp, $ftyp>> for $name {
828            fn from(
829                $crate::Context($cvar, $fvar): $crate::Context<$ctyp, $ftyp>)
830                -> $name
831            {
832                $name::$item($( $texpr ),*)
833            }
834        }
835        ai_quick_error!(FIND_CONTEXT_IMPL
836            $name $item: TUPLE [$( $var:$typ ),*]
837            { $($tail)* });
838    };
839    (FIND_CONTEXT_IMPL $name:ident $item:ident: STRUCT
840        [$( $var:ident: $typ:ty ),*]
841        { context($cvar:ident: AsRef<$ctyp:ty>, $fvar:ident: $ftyp:ty)
842            -> {$( $tvar:ident: $texpr:expr ),*} $( $tail:tt )* }
843    ) => {
844        impl<T: AsRef<$ctyp>> From<$crate::Context<T, $ftyp>> for $name {
845            fn from(
846                $crate::Context($cvar, $fvar): $crate::Context<$ctyp, $ftyp>)
847                -> $name
848            {
849                $name::$item {
850                    $( $tvar: $texpr ),*
851                }
852            }
853        }
854        ai_quick_error!(FIND_CONTEXT_IMPL
855            $name $item: STRUCT [$( $var:$typ ),*]
856            { $($tail)* });
857    };
858    (FIND_CONTEXT_IMPL $name:ident $item:ident: STRUCT
859        [$( $var:ident: $typ:ty ),*]
860        { context($cvar:ident: $ctyp:ty, $fvar:ident: $ftyp:ty)
861            -> {$( $tvar:ident: $texpr:expr ),*} $( $tail:tt )* }
862    ) => {
863        impl<'a> From<$crate::Context<$ctyp, $ftyp>> for $name {
864            fn from(
865                $crate::Context($cvar, $fvar): $crate::Context<$ctyp, $ftyp>)
866                -> $name
867            {
868                $name::$item {
869                    $( $tvar: $texpr ),*
870                }
871            }
872        }
873        ai_quick_error!(FIND_CONTEXT_IMPL
874            $name $item: STRUCT [$( $var:$typ ),*]
875            { $($tail)* });
876    };
877    (FIND_CONTEXT_IMPL $name:ident $item:ident: $imode:tt
878        [$( $var:ident: $typ:ty ),*]
879        { $t:tt $( $tail:tt )*}
880    ) => {
881        ai_quick_error!(FIND_CONTEXT_IMPL
882            $name $item: $imode [$( $var:$typ ),*]
883            {$( $tail )*}
884        );
885    };
886    (FIND_CONTEXT_IMPL $name:ident $item:ident: $imode:tt
887        [$( $var:ident: $typ:ty ),*]
888        { }
889    ) => {
890    };
891    // ----------------------------- ITEM IMPL --------------------------
892    (ITEM_BODY $(#[$imeta:meta])* $item:ident: UNIT
893    ) => { };
894    (ITEM_BODY $(#[$imeta:meta])* $item:ident: TUPLE
895        [$( $typ:ty ),*]
896    ) => {
897        ($( $typ ),*)
898    };
899    (ITEM_BODY $(#[$imeta:meta])* $item:ident: STRUCT
900        [$( $var:ident: $typ:ty ),*]
901    ) => {
902        {$( $var:$typ ),*}
903    };
904    (ITEM_PATTERN $name:ident $item:ident: UNIT []
905    ) => {
906        $name::$item
907    };
908    (ITEM_PATTERN $name:ident $item:ident: TUPLE
909        [$( ref $var:ident ),*]
910    ) => {
911        $name::$item ($( ref $var ),*)
912    };
913    (ITEM_PATTERN $name:ident $item:ident: STRUCT
914        [$( ref $var:ident ),*]
915    ) => {
916        $name::$item {$( ref $var ),*}
917    };
918    // This one should match all allowed sequences in "funcs" but not match
919    // anything else.
920    // This is to contrast FIND_* clauses which just find stuff they need and
921    // skip everything else completely
922    (ERROR_CHECK $imode:tt display($self_:tt) -> ($( $exprs:tt )*) $( $tail:tt )*)
923    => { ai_quick_error!(ERROR_CHECK $imode $($tail)*); };
924    (ERROR_CHECK $imode:tt display($pattern: expr) $( $tail:tt )*)
925    => { ai_quick_error!(ERROR_CHECK $imode $($tail)*); };
926    (ERROR_CHECK $imode:tt display($pattern: expr, $( $exprs:tt )*) $( $tail:tt )*)
927    => { ai_quick_error!(ERROR_CHECK $imode $($tail)*); };
928    (ERROR_CHECK $imode:tt source($expr:expr) $($tail:tt)*)
929    => { ai_quick_error!(ERROR_CHECK $imode $($tail)*); };
930    (ERROR_CHECK $imode:tt from() $($tail:tt)*)
931    => { ai_quick_error!(ERROR_CHECK $imode $($tail)*); };
932    (ERROR_CHECK $imode:tt from($ftyp:ty) $($tail:tt)*)
933    => { ai_quick_error!(ERROR_CHECK $imode $($tail)*); };
934    (ERROR_CHECK TUPLE from($fvar:ident: $ftyp:ty) -> ($( $e:expr ),*) $( $tail:tt )*)
935    => { ai_quick_error!(ERROR_CHECK TUPLE $($tail)*); };
936    (ERROR_CHECK STRUCT from($fvar:ident: $ftyp:ty) -> {$( $v:ident: $e:expr ),*} $( $tail:tt )*)
937    => { ai_quick_error!(ERROR_CHECK STRUCT $($tail)*); };
938
939    (ERROR_CHECK TUPLE context($cvar:ident: $ctyp:ty, $fvar:ident: $ftyp:ty)
940        -> ($( $e:expr ),*) $( $tail:tt )*)
941    => { ai_quick_error!(ERROR_CHECK TUPLE $($tail)*); };
942    (ERROR_CHECK STRUCT context($cvar:ident: $ctyp:ty, $fvar:ident: $ftyp:ty)
943        -> {$( $v:ident: $e:expr ),*} $( $tail:tt )*)
944    => { ai_quick_error!(ERROR_CHECK STRUCT $($tail)*); };
945
946    (ERROR_CHECK $imode:tt ) => {};
947    // Utility functions
948    (IDENT $ident:ident) => { $ident }
949}
950
951/// Generic context type
952///
953/// Used mostly as a transport for `ResultExt::context` method
954#[derive(Debug)]
955pub struct Context<X, E>(pub X, pub E);
956
957/// Result extension trait adding a `context` method
958pub trait ResultExt<T, E> {
959    /// The method is use to add context information to current operation
960    ///
961    /// The context data is then used in error constructor to store additional
962    /// information within error. For example, you may add a filename as a
963    /// context for file operation. See crate documentation for the actual
964    /// example.
965    fn context<X>(self, x: X) -> Result<T, Context<X, E>>;
966}
967
968impl<T, E> ResultExt<T, E> for Result<T, E> {
969    fn context<X>(self, x: X) -> Result<T, Context<X, E>> {
970        self.map_err(|e| Context(x, e))
971    }
972}
973
974#[cfg(test)]
975mod test {
976    use std::error::Error;
977    use std::num::{ParseFloatError, ParseIntError};
978    use std::path::{Path, PathBuf};
979    use std::str::Utf8Error;
980    use std::string::FromUtf8Error;
981
982    use super::ResultExt;
983
984    ai_quick_error! {
985        #[derive(Debug)]
986        pub enum Bare {
987            One
988            Two
989        }
990    }
991
992    #[test]
993    fn bare_item_direct() {
994        assert_eq!(format!("{}", Bare::One), "One".to_string());
995        assert_eq!(format!("{:?}", Bare::One), "One".to_string());
996        assert!(Bare::One.source().is_none());
997    }
998
999    #[test]
1000    fn bare_item_trait() {
1001        let err: &dyn Error = &Bare::Two;
1002        assert_eq!(format!("{}", err), "Two".to_string());
1003        assert_eq!(format!("{:?}", err), "Two".to_string());
1004        assert!(err.source().is_none());
1005    }
1006
1007    ai_quick_error! {
1008        #[derive(Debug)]
1009        pub enum Wrapper wraps Wrapped {
1010            One
1011            Two(s: String) {
1012                display("two: {}", s)
1013                from()
1014            }
1015        }
1016    }
1017
1018    #[test]
1019    fn wrapper() {
1020        assert_eq!(
1021            format!("{}", Wrapper::from(Wrapped::One)),
1022            "One".to_string()
1023        );
1024        assert_eq!(
1025            format!("{}", Wrapper::from(Wrapped::from(String::from("hello")))),
1026            "two: hello".to_string()
1027        );
1028        assert_eq!(
1029            format!("{:?}", Wrapper::from(Wrapped::One)),
1030            "Wrapper(One)".to_string()
1031        );
1032    }
1033
1034    ai_quick_error! {
1035        #[derive(Debug, PartialEq)]
1036        pub enum TupleWrapper {
1037            /// ParseFloat Error
1038            ParseFloatError(err: ParseFloatError) {
1039                from()
1040                display("parse float error: {err}", err=err)
1041                source(err)
1042            }
1043            Other(descr: &'static str) {
1044                display("Error: {}", descr)
1045            }
1046            /// FromUtf8 Error
1047            FromUtf8Error(err: Utf8Error, source: Vec<u8>) {
1048                source(err)
1049                display(me) -> ("{desc} at index {pos}: {err}", desc="utf8 error", pos=err.valid_up_to(), err=err)
1050                from(err: FromUtf8Error) -> (err.utf8_error().clone(), err.into_bytes())
1051            }
1052            Discard {
1053                from(&'static str)
1054            }
1055            Singleton {
1056                display("Just a string")
1057            }
1058        }
1059    }
1060
1061    #[test]
1062    fn tuple_wrapper_err() {
1063        let source = "one and a half times pi".parse::<f32>().unwrap_err();
1064        let err = TupleWrapper::ParseFloatError(source.clone());
1065        assert_eq!(format!("{}", err), format!("parse float error: {}", source));
1066        assert_eq!(
1067            format!("{:?}", err),
1068            format!("ParseFloatError({:?})", source)
1069        );
1070        assert_eq!(
1071            format!("{:?}", err.source().unwrap()),
1072            format!("{:?}", source)
1073        );
1074    }
1075
1076    #[test]
1077    fn tuple_wrapper_trait_str() {
1078        let desc = "hello";
1079        let err: &dyn Error = &TupleWrapper::Other(desc);
1080        assert_eq!(format!("{}", err), format!("Error: {}", desc));
1081        assert_eq!(format!("{:?}", err), format!("Other({:?})", desc));
1082        assert!(err.source().is_none());
1083    }
1084
1085    #[test]
1086    fn tuple_wrapper_trait_two_fields() {
1087        let invalid_utf8: Vec<u8> = vec![0, 159, 146, 150];
1088        let source = String::from_utf8(invalid_utf8.clone())
1089            .unwrap_err()
1090            .utf8_error();
1091        let err: &dyn Error = &TupleWrapper::FromUtf8Error(source.clone(), invalid_utf8.clone());
1092        assert_eq!(
1093            format!("{}", err),
1094            format!(
1095                "{desc} at index {pos}: {source}",
1096                desc = "utf8 error",
1097                pos = source.valid_up_to(),
1098                source = source
1099            )
1100        );
1101        assert_eq!(
1102            format!("{:?}", err),
1103            format!("FromUtf8Error({:?}, {:?})", source, invalid_utf8)
1104        );
1105        assert_eq!(
1106            format!("{:?}", err.source().unwrap()),
1107            format!("{:?}", source)
1108        );
1109    }
1110
1111    #[test]
1112    fn tuple_wrapper_from() {
1113        let source = "one and a half times pi".parse::<f32>().unwrap_err();
1114        let err = TupleWrapper::ParseFloatError(source.clone());
1115        let err_from: TupleWrapper = From::from(source);
1116        assert_eq!(err_from, err);
1117    }
1118
1119    #[test]
1120    fn tuple_wrapper_custom_from() {
1121        let invalid_utf8: Vec<u8> = vec![0, 159, 146, 150];
1122        let source = String::from_utf8(invalid_utf8.clone()).unwrap_err();
1123        let err = TupleWrapper::FromUtf8Error(source.utf8_error().clone(), invalid_utf8);
1124        let err_from: TupleWrapper = From::from(source);
1125        assert_eq!(err_from, err);
1126    }
1127
1128    #[test]
1129    fn tuple_wrapper_discard() {
1130        let err: TupleWrapper = From::from("hello");
1131        assert_eq!(format!("{}", err), format!("Discard"));
1132        assert_eq!(format!("{:?}", err), format!("Discard"));
1133        assert!(err.source().is_none());
1134    }
1135
1136    #[test]
1137    fn tuple_wrapper_singleton() {
1138        let err: TupleWrapper = TupleWrapper::Singleton;
1139        assert_eq!(format!("{}", err), format!("Just a string"));
1140        assert_eq!(format!("{:?}", err), format!("Singleton"));
1141        assert!(err.source().is_none());
1142    }
1143
1144    ai_quick_error! {
1145        #[derive(Debug, PartialEq)]
1146        pub enum StructWrapper {
1147            // Utf8 Error
1148            Utf8Error{ err: Utf8Error, hint: Option<&'static str> } {
1149                source(err)
1150                display(me) -> ("{desc} at index {pos}: {err}", desc="utf8 error", pos=err.valid_up_to(), err=err)
1151                from(err: Utf8Error) -> { err: err, hint: None }
1152            }
1153            // Utf8 Error
1154            ExcessComma { descr: &'static str, } {
1155                display("Error: {}", descr)
1156            }
1157        }
1158    }
1159
1160    #[test]
1161    fn struct_wrapper_err() {
1162        let invalid_utf8: Vec<u8> = vec![0, 159, 146, 150];
1163        let source = String::from_utf8(invalid_utf8.clone())
1164            .unwrap_err()
1165            .utf8_error();
1166        let err: &dyn Error = &StructWrapper::Utf8Error {
1167            err: source.clone(),
1168            hint: Some("nonsense"),
1169        };
1170        assert_eq!(
1171            format!("{}", err),
1172            format!(
1173                "{desc} at index {pos}: {source}",
1174                desc = "utf8 error",
1175                pos = source.valid_up_to(),
1176                source = source
1177            )
1178        );
1179        assert_eq!(
1180            format!("{:?}", err),
1181            format!(
1182                "Utf8Error {{ err: {:?}, hint: {:?} }}",
1183                source,
1184                Some("nonsense")
1185            )
1186        );
1187        assert_eq!(
1188            format!("{:?}", err.source().unwrap()),
1189            format!("{:?}", source)
1190        );
1191    }
1192
1193    #[test]
1194    fn struct_wrapper_struct_from() {
1195        let invalid_utf8: Vec<u8> = vec![0, 159, 146, 150];
1196        let source = String::from_utf8(invalid_utf8.clone())
1197            .unwrap_err()
1198            .utf8_error();
1199        let err = StructWrapper::Utf8Error {
1200            err: source.clone(),
1201            hint: None,
1202        };
1203        let err_from: StructWrapper = From::from(source);
1204        assert_eq!(err_from, err);
1205    }
1206
1207    #[test]
1208    fn struct_wrapper_excess_comma() {
1209        let descr = "hello";
1210        let err = StructWrapper::ExcessComma { descr: descr };
1211        assert_eq!(format!("{}", err), format!("Error: {}", descr));
1212        assert_eq!(
1213            format!("{:?}", err),
1214            format!("ExcessComma {{ descr: {:?} }}", descr)
1215        );
1216        assert!(err.source().is_none());
1217    }
1218
1219    ai_quick_error! {
1220        #[derive(Debug)]
1221        pub enum ContextErr {
1222            Float(src: String, err: ParseFloatError) {
1223                context(s: &'a str, e: ParseFloatError) -> (s.to_string(), e)
1224                display("Float error {:?}: {}", src, err)
1225            }
1226            Int { src: String, err: ParseIntError } {
1227                context(s: &'a str, e: ParseIntError)
1228                    -> {src: s.to_string(), err: e}
1229                display("Int error {:?}: {}", src, err)
1230            }
1231            Utf8(path: PathBuf, err: Utf8Error) {
1232                context(p: AsRef<Path>, e: Utf8Error)
1233                    -> (p.as_ref().to_path_buf(), e)
1234                display("Path error at {:?}: {}", path, err)
1235            }
1236            Utf8Str(s: String, err: ::std::io::Error) {
1237                context(s: AsRef<str>, e: ::std::io::Error)
1238                    -> (s.as_ref().to_string(), e)
1239                display("Str error {:?}: {}", s, err)
1240            }
1241        }
1242    }
1243
1244    #[test]
1245    fn parse_float_error() {
1246        fn parse_float(s: &str) -> Result<f32, ContextErr> {
1247            Ok(s.parse().context(s)?)
1248        }
1249        assert_eq!(
1250            format!("{}", parse_float("12ab").unwrap_err()),
1251            r#"Float error "12ab": invalid float literal"#
1252        );
1253    }
1254
1255    #[test]
1256    fn parse_int_error() {
1257        fn parse_int(s: &str) -> Result<i32, ContextErr> {
1258            Ok(s.parse().context(s)?)
1259        }
1260        assert_eq!(
1261            format!("{}", parse_int("12.5").unwrap_err()),
1262            r#"Int error "12.5": invalid digit found in string"#
1263        );
1264    }
1265
1266    #[test]
1267    fn debug_context() {
1268        fn parse_int(s: &str) -> i32 {
1269            s.parse().context(s).unwrap()
1270        }
1271        assert_eq!(parse_int("12"), 12);
1272        assert_eq!(
1273            format!("{:?}", "x".parse::<i32>().context("x")),
1274            r#"Err(Context("x", ParseIntError { kind: InvalidDigit }))"#
1275        );
1276    }
1277
1278    #[test]
1279    fn path_context() {
1280        fn parse_utf<P: AsRef<Path>>(s: &[u8], p: P) -> Result<(), ContextErr> {
1281            ::std::str::from_utf8(s).context(p)?;
1282            Ok(())
1283        }
1284        let etext = parse_utf(b"a\x80\x80", "/etc").unwrap_err().to_string();
1285        assert!(etext.starts_with("Path error at \"/etc\": invalid utf-8"));
1286        let etext = parse_utf(b"\x80\x80", PathBuf::from("/tmp"))
1287            .unwrap_err()
1288            .to_string();
1289        assert!(etext.starts_with("Path error at \"/tmp\": invalid utf-8"));
1290    }
1291
1292    #[test]
1293    fn conditional_compilation() {
1294        ai_quick_error! {
1295            #[allow(dead_code)]
1296            #[derive(Debug)]
1297            pub enum Test {
1298                #[cfg(feature = "foo")]
1299                Variant
1300            }
1301        }
1302    }
1303
1304    #[test]
1305    #[allow(deprecated)]
1306    fn cause_struct_wrapper_err() {
1307        let invalid_utf8: Vec<u8> = vec![0, 159, 146, 150];
1308        let cause = String::from_utf8(invalid_utf8.clone())
1309            .unwrap_err()
1310            .utf8_error();
1311        let err: &dyn Error = &StructWrapper::Utf8Error {
1312            err: cause.clone(),
1313            hint: Some("nonsense"),
1314        };
1315        assert_eq!(
1316            format!("{}", err),
1317            format!(
1318                "{desc} at index {pos}: {cause}",
1319                desc = "utf8 error",
1320                pos = cause.valid_up_to(),
1321                cause = cause
1322            )
1323        );
1324        assert_eq!(
1325            format!("{:?}", err),
1326            format!(
1327                "Utf8Error {{ err: {:?}, hint: {:?} }}",
1328                cause,
1329                Some("nonsense")
1330            )
1331        );
1332        assert_eq!(
1333            format!("{:?}", err.cause().unwrap()),
1334            format!("{:?}", cause)
1335        );
1336    }
1337}