error_type/
lib.rs

1/*!
2
3This crate provides the `error_type!` macro, which is designed to produce a fresh, reasonably well-featured error type from a concise definition.
4
5A simple example of the usage is given below:
6
7```rust
8#[macro_use] extern crate error_type;
9
10use std::borrow::Cow;
11use std::error::Error;
12use std::io;
13
14error_type! {
15    #[derive(Debug)]
16    pub enum LibError {
17        Io(std::io::Error) {
18            cause;
19        },
20        Kaboom(Kaboom) {
21            disp (e, fmt) write!(fmt, "{:?}", e);
22            desc (_e) "kaboom!";
23        },
24        Message(Cow<'static, str>) {
25            desc (e) &**e;
26            from (s: &'static str) s.into();
27            from (s: String) s.into();
28        },
29        Other(Box<Error>) {
30            desc (e) e.description();
31            cause (e) Some(&**e);
32        }
33    }
34}
35
36#[derive(Debug)]
37pub struct Kaboom;
38# fn main() {}
39```
40
41The expansion of the above includes the following:
42
43- The `LibError` enumeration (plus the provided `#[derive(Debug)]` annotation), with `Io`, `Message` and `Other` variants.
44
45- An implicit `impl From<Payload> for LibError` for each variant's payload type.
46
47- An implicit `impl Display for LibError`, using the existing `Display` implementation for each variant's payload type.
48
49- An implicit `impl Error for LibError`.
50
51- For the `Io` variant:
52
53  - An implicit `description`, forwarded to the existing definition for `std::io::Error`.
54
55  - An automatic `cause`, forwarded to the existing definition for `std::io::Error`.
56
57    **Note**: the automatic `cause` returns the result of `std::io::Error::cause`, *not* the payload itself.  This macro considers the payload to *be* the error, not the underlying cause.
58
59- For the `Kaboom` variant:
60
61  - An explicit `Display` override, since `Kaboom` does not, itself, implement it.
62
63  - An explicit `description`, which just returns a string literal.
64
65- For the `Message` variant:
66
67  - An explicit `description`, which just returns the contents of the `Cow<'static, str>`.
68
69  - An implicit `cause`, which just returns `None`.
70
71  - An explicit `From<&'static str>` conversion.
72
73  - An explicit `From<String>` conversion.
74
75- For the `Other` variant:
76
77  - An explicit `description` which forwards to the existing definition for the boxed `Error`.
78
79  - An explicit `cause` which returns the boxed error *itself* as the cause.  This is distinct from the behaviour of an *automatic* `cause`.
80
81## FAQ
82
83* *Can I use unitary variants; ones without a payload?*
84
85  No, not as yet.  Maybe if there's demand.
86
87* *Can I use tuple variants with more than one element?*
88
89  No.  This would likely be rather inconvenient to implement, due to the way the various parts of the implementation are constructed.  Not impossible, though.
90
91* *Can I use struct variants?*
92
93  No, for much the same reason as tuple variants.
94
95* *Can I have fields common to all variants; i.e. have the enum wrapped in a struct?*
96
97  No.  It would be nice, but I'm not sure how to go about that.  You can always use the expansion of `error_type!` in a custom structure for the added information.
98
99*/
100
101#[doc(hidden)]
102#[macro_export]
103macro_rules! error_type_as_item {
104    ($i:item) => {$i};
105}
106
107#[doc(hidden)]
108#[macro_export]
109macro_rules! error_type_var_body_emit {
110    /*
111    Nothing left.
112    */
113    (
114        $err_name:ident, $var_name:ident, $var_ty:ty, $edi_tr:ident, $ede_tr:ident, $ec_tr:ident
115    ) => {
116        // Done.
117    };
118
119    /*
120    disp () clause.
121    */
122    (
123        $err_name:ident, $var_name:ident, $var_ty:ty, $edi_tr:ident, $ede_tr:ident, $ec_tr:ident,
124        disp ()
125        $($tail:tt)*
126    ) => {
127        impl<'a> $edi_tr for (&'a $err_name, &'a $var_ty) {
128            fn error_fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
129                ::std::fmt::Display::fmt(self.1, fmt)
130            }
131        }
132
133        error_type_var_body_emit! {
134            $err_name, $var_name, $var_ty, $edi_tr, $ede_tr, $ec_tr $($tail)*
135        }
136    };
137
138    /*
139    disp ((arg, fmt) expr) clause.
140    */
141    (
142        $err_name:ident, $var_name:ident, $var_ty:ty, $edi_tr:ident, $ede_tr:ident, $ec_tr:ident,
143        disp (($disp_arg:ident, $disp_fmt:ident) $disp_expr:expr)
144        $($tail:tt)*
145    ) => {
146        impl<'a> $edi_tr for (&'a $err_name, &'a $var_ty) {
147            fn error_fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
148                let $disp_arg = self.1;
149                let $disp_fmt = fmt;
150                $disp_expr
151            }
152        }
153
154        error_type_var_body_emit! {
155            $err_name, $var_name, $var_ty, $edi_tr, $ede_tr, $ec_tr $($tail)*
156        }
157    };
158
159    /*
160    desc () clause.
161    */
162    (
163        $err_name:ident, $var_name:ident, $var_ty:ty, $edi_tr:ident, $ede_tr:ident, $ec_tr:ident,
164        desc ()
165        $($tail:tt)*
166    ) => {
167        impl<'a> $ede_tr<'a> for (&'a $err_name, &'a $var_ty) {
168            fn error_desc(&self) -> &'a str {
169                ::std::error::Error::description(self.1)
170            }
171        }
172
173        error_type_var_body_emit! {
174            $err_name, $var_name, $var_ty, $edi_tr, $ede_tr, $ec_tr $($tail)*
175        }
176    };
177
178    /*
179    desc ((arg) expr) clause.
180    */
181    (
182        $err_name:ident, $var_name:ident, $var_ty:ty, $edi_tr:ident, $ede_tr:ident, $ec_tr:ident,
183        desc (($desc_arg:ident) $desc_expr:expr)
184        $($tail:tt)*
185    ) => {
186        impl<'a> $ede_tr<'a> for (&'a $err_name, &'a $var_ty) {
187            fn error_desc(&self) -> &'a str {
188                let $desc_arg = self.1;
189                $desc_expr
190            }
191        }
192        
193        error_type_var_body_emit! {
194            $err_name, $var_name, $var_ty, $edi_tr, $ede_tr, $ec_tr $($tail)*
195        }
196    };
197
198    /*
199    cause () clause.
200    */
201    (
202        $err_name:ident, $var_name:ident, $var_ty:ty, $edi_tr:ident, $ede_tr:ident, $ec_tr:ident,
203        cause ()
204        $($tail:tt)*
205    ) => {
206        impl<'a> $ec_tr<'a> for (&'a $err_name, &'a $var_ty) {
207            fn error_cause(&self) -> ::std::option::Option<&'a ::std::error::Error> {
208                None
209            }
210        }
211
212        error_type_var_body_emit! {
213            $err_name, $var_name, $var_ty, $edi_tr, $ede_tr, $ec_tr $($tail)*
214        }
215    };
216
217    /*
218    cause ((arg) expr) clause.
219    */
220    (
221        $err_name:ident, $var_name:ident, $var_ty:ty, $edi_tr:ident, $ede_tr:ident, $ec_tr:ident,
222        cause (($cl_arg:ident) $cl_expr:expr)
223        $($tail:tt)*
224    ) => {
225        impl<'a> $ec_tr<'a> for (&'a $err_name, &'a $var_ty) {
226            fn error_cause(&self) -> ::std::option::Option<&'a ::std::error::Error> {
227                let $cl_arg = self.1;
228                $cl_expr
229            }
230        }
231        
232        error_type_var_body_emit! {
233            $err_name, $var_name, $var_ty, $edi_tr, $ede_tr, $ec_tr $($tail)*
234        }
235    };
236
237    /*
238    from ((arg: ty) expr) clause.
239    */
240    (
241        $err_name:ident, $var_name:ident, $var_ty:ty, $edi_tr:ident, $ede_tr:ident, $ec_tr:ident,
242        from ($(($cl_arg:ident: $cl_ty:ty) $cl_expr:expr);*)
243        $($tail:tt)*
244    ) => {
245        $(
246            impl ::std::convert::From<$cl_ty> for $err_name {
247                fn from($cl_arg: $cl_ty) -> $err_name {
248                    $err_name::$var_name($cl_expr)
249                }
250            }
251        )*
252
253        error_type_var_body_emit! {
254            $err_name, $var_name, $var_ty, $edi_tr, $ede_tr, $ec_tr $($tail)*
255        }
256    };
257}
258
259#[doc(hidden)]
260#[macro_export]
261macro_rules! error_type_var_body {
262    /*
263    Base case: no more clauses.
264    */
265    (
266        $err_name:ident, $var_name:ident, $var_ty:ty, $edi_tr:ident, $ede_tr:ident, $ec_tr:ident,
267        $disp:tt, $desc:tt, $cause:tt, $from:tt; {}
268    ) => {
269        error_type_var_body_emit! {
270            $err_name, $var_name, $var_ty, $edi_tr, $ede_tr, $ec_tr,
271            disp $disp, desc $desc, cause $cause, from $from
272        }
273    };
274
275    /*
276    disp (arg, fmt) expr;
277    */
278    (
279        $err_name:ident, $var_name:ident, $var_ty:ty, $edi_tr:ident, $ede_tr:ident, $ec_tr:ident,
280        $disp:tt, $desc:tt, $cause:tt, $from:tt; {
281            disp ($cl_arg:ident, $cl_fmt:ident) $cl_body:expr;
282            $($tail:tt)*
283        }
284    ) => {
285        error_type_var_body! {
286            $err_name, $var_name, $var_ty, $edi_tr, $ede_tr, $ec_tr,
287            (($cl_arg, $cl_fmt) $cl_body), $desc, $cause, $from;
288            {$($tail)*}
289        }
290    };
291
292    /*
293    desc (arg) expr;
294    */
295    (
296        $err_name:ident, $var_name:ident, $var_ty:ty, $edi_tr:ident, $ede_tr:ident, $ec_tr:ident,
297        $disp:tt, $desc:tt, $cause:tt, $from:tt; {
298            desc ($cl_arg:ident) $cl_body:expr;
299            $($tail:tt)*
300        }
301    ) => {
302        error_type_var_body! {
303            $err_name, $var_name, $var_ty, $edi_tr, $ede_tr, $ec_tr,
304            $disp, (($cl_arg) $cl_body), $cause, $from;
305            {$($tail)*}
306        }
307    };
308
309    /*
310    cause (arg) expr;
311    */
312    (
313        $err_name:ident, $var_name:ident, $var_ty:ty, $edi_tr:ident, $ede_tr:ident, $ec_tr:ident,
314        $disp:tt, $desc:tt, $cause:tt, $from:tt; {
315            cause ($cl_arg:ident) $cl_body:expr;
316            $($tail:tt)*
317        }
318    ) => {
319        error_type_var_body! {
320            $err_name, $var_name, $var_ty, $edi_tr, $ede_tr, $ec_tr,
321            $disp, $desc, (($cl_arg) $cl_body), $from;
322            {$($tail)*}
323        }
324    };
325
326    /*
327    cause;
328    */
329    (
330        $err_name:ident, $var_name:ident, $var_ty:ty, $edi_tr:ident, $ede_tr:ident, $ec_tr:ident,
331        $disp:tt, $desc:tt, $cause:tt, $from:tt; {
332            cause;
333            $($tail:tt)*
334        }
335    ) => {
336        error_type_var_body! {
337            $err_name, $var_name, $var_ty, $edi_tr, $ede_tr, $ec_tr,
338            $disp, $desc, ((e) ::std::error::Error::cause(e)), $from;
339            {$($tail)*}
340        }
341    };
342
343    /*
344    from (arg: Ty) expr; (first)
345    */
346    (
347        $err_name:ident, $var_name:ident, $var_ty:ty, $edi_tr:ident, $ede_tr:ident, $ec_tr:ident,
348        $disp:tt, $desc:tt, $cause:tt, (); {
349            from ($cl_arg:ident: $cl_ty:ty) $cl_body:expr;
350            $($tail:tt)*
351        }
352    ) => {
353        error_type_var_body! {
354            $err_name, $var_name, $var_ty, $edi_tr, $ede_tr, $ec_tr,
355            $disp, $desc, $cause, (($cl_arg: $cl_ty) $cl_body);
356            {$($tail)*}
357        }
358    };
359
360    /*
361    from (arg: Ty) expr; (not first)
362    */
363    (
364        $err_name:ident, $var_name:ident, $var_ty:ty, $edi_tr:ident, $ede_tr:ident, $ec_tr:ident,
365        $disp:tt, $desc:tt, $cause:tt, ($($from:tt)*); {
366            from ($cl_arg:ident: $cl_ty:ty) $cl_body:expr;
367            $($tail:tt)*
368        }
369    ) => {
370        error_type_var_body! {
371            $err_name, $var_name, $var_ty, $edi_tr, $ede_tr, $ec_tr,
372            $disp, $desc, $cause, (($cl_arg: $cl_ty) $cl_body; $($from)*);
373            {$($tail)*}
374        }
375    };
376}
377
378#[doc(hidden)]
379#[macro_export]
380macro_rules! error_type_impl {
381    (
382        $(#[$($derive_tts:tt)*])*
383        enum $err_name:ident {
384            $($var_name:ident($var_ty:ty) $var_body:tt),+
385            $(,)*
386        }
387    ) => {
388        $(
389            impl ::std::convert::From<$var_ty> for $err_name {
390                fn from(value: $var_ty) -> $err_name {
391                    $err_name::$var_name(value)
392                }
393            }
394        )+
395        
396        impl ::std::fmt::Display for $err_name {
397            fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
398                match *self {
399                    $(
400                        $err_name::$var_name(ref v) => (self, v).error_fmt(fmt)
401                    ),+
402                }
403            }
404        }
405
406        pub trait ErrorDisplay {
407            fn error_fmt(&self, &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error>;
408        }
409
410        pub trait ErrorDescription<'a> {
411            fn error_desc(&self) -> &'a str;
412        }
413
414        pub trait ErrorCause<'a> {
415            fn error_cause(&self) -> ::std::option::Option<&'a ::std::error::Error>;
416        }
417        
418        impl ::std::error::Error for $err_name {
419            fn description(&self) -> &str {
420                use self::ErrorDescription;
421                match *self {
422                    $(
423                        $err_name::$var_name(ref v) => (self, v).error_desc()
424                    ),+
425                }
426            }
427            
428            fn cause(&self) -> ::std::option::Option<&::std::error::Error> {
429                use self::ErrorCause;
430                match *self {
431                    $(
432                        $err_name::$var_name(ref v) => (self, v).error_cause()
433                    ),+
434                }
435            }
436        }
437        
438        $(
439            error_type_var_body! {
440                $err_name, $var_name, $var_ty,
441                ErrorDisplay, ErrorDescription, ErrorCause,
442                (), (), (), ();
443                $var_body
444            }
445        )+
446    };
447}
448
449/**
450Constructs a reasonably well-featured error type from a concise description.
451
452For details, see the crate documentation.
453*/
454#[macro_export]
455macro_rules! error_type {
456    (
457        $(#[$($derive_tts:tt)*])*
458        pub enum $err_name:ident {
459            $($var_name:ident($var_ty:ty) $var_body:tt),+
460            $(,)*
461        }
462    ) => {
463        error_type_as_item! {
464            $(#[$($derive_tts)*])*
465            pub enum $err_name {
466                $($var_name($var_ty)),+
467            }
468        }
469        
470        error_type_impl! {
471            $(#[$($derive_tts)*])*
472            enum $err_name {
473                $($var_name($var_ty) $var_body),+
474            }
475        }
476    };
477
478    (
479        $(#[$($derive_tts:tt)*])*
480        enum $err_name:ident {
481            $($var_name:ident($var_ty:ty) $var_body:tt),+
482            $(,)*
483        }
484    ) => {
485        error_type_as_item! {
486            $(#[$($derive_tts)*])*
487            enum $err_name {
488                $($var_name($var_ty)),+
489            }
490        }
491        
492        error_type_impl! {
493            $(#[$($derive_tts)*])*
494            enum $err_name {
495                $($var_name($var_ty) $var_body),+
496            }
497        }
498    };
499}