custom_error_core/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2#![cfg_attr(feature = "unstable", feature(allocator_api, try_reserve))]
3
4extern crate alloc;
5
6/// Constructs a custom error type.
7///
8/// # Examples
9///
10/// ### Simple error
11///
12/// For an error with multiple cases you can generate an enum:
13/// ```
14/// use custom_error::custom_error;
15///
16/// custom_error! { pub MyError
17///     Bad      = "Something bad happened",
18///     Terrible = "This is a very serious error!!!"
19/// }
20/// assert_eq!("Something bad happened", MyError::Bad.to_string());
21/// assert_eq!(
22///     "This is a very serious error!!!",
23///     MyError::Terrible.to_string()
24/// );
25/// ```
26///
27/// For an error with a single case you can generate a struct:
28/// ```
29/// use custom_error::custom_error;
30///
31/// custom_error! { pub MyError{} = "Something bad happened" }
32/// assert_eq!("Something bad happened", MyError {}.to_string());
33/// ```
34///
35/// ### Custom error with parameters
36/// ```
37/// use custom_error::custom_error;
38///
39/// custom_error!{SantaError
40///     BadChild{name:String, foolishness:u8} = "{name} has been bad {foolishness} times this year",
41///     TooFar                                = "The location you indicated is too far from the north pole",
42///     InvalidReindeer{legs:u8}              = "The reindeer has {legs} legs"
43/// }
44///
45/// assert_eq!(
46///     "Thomas has been bad 108 times this year",
47///     SantaError::BadChild{
48///         name: "Thomas".into(),
49///         foolishness: 108
50///     }.to_string());
51///
52/// assert_eq!(
53///     "The location you indicated is too far from the north pole",
54///     SantaError::TooFar.to_string()
55/// );
56///
57/// assert_eq!(
58///     "The reindeer has 8 legs",
59///     SantaError::InvalidReindeer{legs:8}.to_string()
60/// );
61/// ```
62///
63///  ### Automatic conversion from other error types
64///
65/// You can add a special field named `source` to your error types.
66///
67/// Use this field to include the lower-level source of the error.
68/// It will be used in the error
69/// [`source()`](https://doc.rust-lang.org/std/error/trait.Error.html#method.source)
70/// method, and automatic conversion from the source error type to your custom error type will be possible
71/// (your error type will implement `From<SourceErrorType>`).
72///
73/// #### limitations
74///  * You cannot have several error cases that contain a single *source* field of the same type:
75///         `custom_error!(E A{source:X} B{source:Y})` is allowed, but
76///         `custom_error!(E A{source:X} B{source:X})` is forbidden.
77///  * If the source field is not the only one, then the automatic conversion
78///    will not be implemented.
79///  * The type of source must be valid for the `'static` lifetime (because of the type signature of
80///    the [`source()`](https://doc.rust-lang.org/std/error/trait.Error.html#method.source) method).
81///    You can still have a field with a non-static type that you will not name `source`,
82///    and manually implement the error conversion from this type to your error type.
83///
84/// ```
85/// use core::{fs::File, io, io::Read, result::Result};
86/// use custom_error::custom_error;
87///
88/// custom_error! {MyError
89///     IO{source: io::Error} = "input/output error",
90///     Unknown               = "unknown error"
91/// }
92///
93/// fn read_file(filename: &str) -> Result<String, MyError> {
94///     let mut res = String::new();
95///     File::open(filename)?.read_to_string(&mut res)?;
96///     Ok(res)
97/// }
98///
99/// assert_eq!(
100///     "input/output error",
101///     read_file("/i'm not a file/").unwrap_err().to_string()
102/// )
103/// ```
104///
105///  ### Custom formatting function for error messages
106///
107/// If the format string syntax is not enough to express your complex error formatting needs,
108/// you can use custom code to generate your error description.
109///
110/// ```
111/// use custom_error::custom_error;
112///
113/// static LANG: &'static str = "FR";
114///
115/// # fn localize(_:&str, _:&str) -> &'static str { "Un problème est survenu" }
116///
117/// custom_error! { pub MyError
118///     Problem      = @{ localize(LANG, "A problem occurred") },
119/// }
120///
121/// assert_eq!("Un problème est survenu", MyError::Problem.to_string());
122/// ```
123///
124/// ```
125/// use std::io::Error;
126/// use std::io::ErrorKind::*;
127/// use custom_error::custom_error;
128///
129/// custom_error! { pub MyError
130///     Io{source: Error} = @{
131///         match source.kind() {
132///             NotFound => "The file does not exist",
133///             TimedOut => "The operation timed out",
134///             _ => "unknown error",
135///         }
136///     },
137/// }
138///
139/// assert_eq!(
140///     "The operation timed out",
141///     MyError::Io {
142///         source: TimedOut.into()
143///     }
144///     .to_string()
145/// );
146/// ```
147///
148/// ### Derive traits for your errors
149/// You can add custom [attributes](https://doc.rust-lang.org/rust-by-example/attribute.html)
150/// at the beginning of the macro invocation. This allows you to derive traits for your error:
151///
152/// ```
153/// use custom_error::custom_error;
154///
155/// custom_error! {
156///     #[derive(PartialEq,PartialOrd)]
157///     ErrLevel Small = "Don't worry", Serious = "Aaargh!!!"
158/// }
159/// assert_ne!(ErrLevel::Small, ErrLevel::Serious);
160/// assert!(ErrLevel::Small < ErrLevel::Serious);
161/// ```
162
163/// We define our own Error trait for no_std.
164#[cfg(not(feature = "std"))]
165mod error;
166
167/// Use the Error trait from `std` by default.
168#[cfg(feature = "std")]
169pub use std::error::Error;
170
171/// Use our own Error trait for `no_std`.
172#[cfg(not(feature = "std"))]
173pub use error::Error;
174
175#[macro_export]
176macro_rules! custom_error {
177    (
178        $( #[$meta_attribute:meta] )* // Attributes, like #[derive(SomeTrait)]
179        $visibility:vis // `pub` marker
180        $errtype:ident // Name of the error type to generate
181        $( < $(
182            $type_param:tt // Optional type parameters for generic error types
183            ),*
184        > )?
185        $(
186            $( #[$field_meta:meta] )* // Meta-attributes for the variant, such as a doc comment
187            $field:ident // Name of an error variant
188            $( { $(
189                $( #[$attr_meta:meta] )* // Meta-attributes for the attribute of the error variant
190                $attr_name:ident // Name of an attribute of the error variant
191                :
192                $attr_type:ty // type of the attribute
193            ),* } )?
194            =
195            $( @{ $($msg_fun:tt)* } )?
196            $($msg:expr)? // The human-readable error message
197         ),*
198         $(,)* // Trailing comma
199    ) => {
200        $( #[$meta_attribute] )*
201        #[derive(Debug)]
202        $visibility enum $errtype $( < $($type_param),* > )* {
203            $(
204                $( #[$field_meta] )*
205                $field
206                $( { $( $( #[$attr_meta] )* $attr_name : $attr_type ),* } )*
207            ),*
208        }
209
210        $crate::add_type_bounds! {
211        ( $($($type_param),*)* )
212        (core::fmt::Debug + core::fmt::Display)
213        { impl <} {> $crate::Error
214            for $errtype $( < $($type_param),* > )*
215        {
216            fn source(&self) -> Option<&(dyn $crate::Error + 'static)>
217            {
218                #[allow(unused_variables, unreachable_code)]
219                match self {$(
220                    $errtype::$field $( { $( $attr_name ),* } )* => {
221                        $( $(
222                            $crate::return_if_source!($attr_name, $attr_name)
223                        );* ;)*
224                        None
225                    }
226                ),*}
227            }
228        }
229        }}
230
231        $crate::impl_error_conversion!{
232            ( $errtype $(< $($type_param),* >)* )
233            $([
234                $field,
235                $($(
236                    $attr_name,
237                    $attr_name,
238                    $attr_type
239                ),*),*
240            ])*
241        }
242
243        $crate::add_type_bounds! {
244        ( $($($type_param),*)* )
245        (alloc::string::ToString)
246        { impl <} {> core::fmt::Display
247            for $errtype $( < $($type_param),* > )*
248        {
249            fn fmt(&self, formatter: &mut core::fmt::Formatter)
250                -> core::fmt::Result
251            {
252                match self {$(
253                    $errtype::$field $( { $( $attr_name ),* } )* => {
254                        $(write!(formatter, "{}", ($($msg_fun)*) )?;)*
255                        $crate::display_message!(formatter, $($($attr_name),*),* | $($msg)*);
256                        Ok(())
257                    }
258                ),*}
259            }
260        }
261        }}
262    };
263
264    // Simple struct error
265    (
266        $( #[$meta_attribute:meta] )* // Attributes, like #[derive(SomeTrait)]
267        $visibility:vis // `pub` marker
268        $errtype:ident // Name of the error type to generate
269        $( < $(
270            $type_param:tt // Optional type parameters for generic error types
271            ),*
272        > )?
273        { $(
274            $( #[$field_meta:meta] )* // Field meta attributes, such as doc comments
275            $field_name:ident // Name of an attribute of the error variant
276            :
277            $field_type:ty // type of the attribute
278        ),* }
279        =
280        $( @{ $($msg_fun:tt)* } )?
281        $($msg:expr)? // The human-readable error message
282        $(,)* // Trailing comma
283    ) => {
284        $( #[$meta_attribute] )*
285        #[derive(Debug)]
286        $visibility struct $errtype $( < $($type_param),* > )* {
287            $(
288                $( #[$field_meta] )*
289                pub $field_name : $field_type
290            ),*
291        }
292
293        $crate::add_type_bounds! {
294        ( $($($type_param),*)* )
295        (core::fmt::Debug + core::fmt::Display)
296        { impl <} {> $crate::Error
297            for $errtype $( < $($type_param),* > )*
298        {
299            #[allow(unused_variables, unreachable_code)]
300            fn source(&self) -> Option<&(dyn $crate::Error + 'static)>
301            {
302                #[allow(unused_variables, unreachable_code)]
303                match self {
304                    $errtype { $( $field_name ),* } => {
305                        $({
306                            $crate::return_if_source!($field_name, $field_name)
307                        });*
308                        None
309                    }
310                }
311            }
312        }
313        }}
314
315        $crate::impl_error_conversion_for_struct!{
316            $errtype $(< $($type_param),* >)*,
317            $( $field_name: $field_type ),*
318        }
319
320        $crate::add_type_bounds! {
321        ( $($($type_param),*)* )
322        (alloc::string::ToString)
323        { impl <} {> core::fmt::Display
324            for $errtype $( < $($type_param),* > )*
325        {
326            fn fmt(&self, formatter: &mut core::fmt::Formatter)
327                -> core::fmt::Result
328            {
329                // make fields accessible with variables, so that we can
330                // use them in custom error msg blocks without self
331                $(
332                    let $field_name = &self.$field_name;
333                )*
334                $(write!(formatter, "{}", ($($msg_fun)*) )?;)*
335                $crate::display_message!(formatter, $($field_name),* | $($msg)*);
336                Ok(())
337            }
338        }
339        }}
340    };
341}
342
343#[doc(hidden)]
344#[macro_export]
345macro_rules! return_if_source {
346    // Return the source if the attribute is called 'source'
347    (source, $attr_name:ident) => {{
348        // Borrow is needed in order to support boxed errors
349        // see: https://github.com/lovasoa/custom_error/issues/20
350        return Some(core::borrow::Borrow::borrow($attr_name));
351    }};
352    // If the attribute has a different name, return nothing
353    ($_attr_name:ident, $_repeat:ident ) => {
354        ()
355    };
356}
357
358#[doc(hidden)]
359#[macro_export]
360macro_rules! impl_error_conversion {
361    ( ( $($prefix:tt)* ) [ $($field_data:tt)* ] $($rest:tt)* ) => {
362        $crate::impl_error_conversion!{$($prefix)*, $($field_data)*}
363        $crate::impl_error_conversion!{ ($($prefix)*) $($rest)*}
364    };
365    ( ( $($prefix:tt)* ) ) => {};
366    // implement From<Source> only when there is a single attribute and it is named 'source'
367    (
368        $errtype:ident $( < $($type_param:tt),* > )*,
369        $field:ident,
370        source,
371        $source:ident,
372        $source_type:ty
373    ) => {
374        impl $( < $($type_param),* > )* From<$source_type>
375        for $errtype $( < $($type_param),* > )* {
376            fn from(source: $source_type) -> Self {
377                $errtype::$field { source }
378            }
379        }
380    };
381    (
382        $_errtype:ident $( < $($_errtype_type_param:tt),* > )*,
383        $_field:ident,
384        $(
385            $_:ident,
386            $_repeated:ident,
387            $_type:ty
388        ),*
389    ) => {}; // If the list of fields is not a single field named 'source', do nothing
390}
391
392#[doc(hidden)]
393#[macro_export]
394macro_rules! impl_error_conversion_for_struct {
395    ( ( $($prefix:tt)* ) ) => {};
396    // implement From<Source> only when there is a single field and it is named 'source'
397    (
398        $errtype:ident, $( < $($type_param:tt),* > )*
399        source: $source_type:ty
400    ) => {
401        impl $( < $($type_param),* > )* From<$source_type>
402        for $errtype $( < $($type_param),* > )* {
403            fn from(source: $source_type) -> Self { $errtype { source } }
404        }
405    };
406    (
407        $_errtype:ident $( < $($_errtype_type_param:tt),* > )*,
408        $( $_field:ident: $_type:ty ),*
409    ) => {}; // If the list of fields is not a single field named 'source', do nothing
410}
411
412#[doc(hidden)]
413#[macro_export]
414macro_rules! display_message {
415    ($formatter:expr, $($attr:ident),* | $msg:expr) => {
416        write!(
417            $formatter,
418            concat!($msg $(, "{", stringify!($attr), ":.0}" )*)
419            $( , $attr = $attr.to_string() )*
420        )?;
421    };
422    ($formatter:expr, $($attr:ident),* | ) => {};
423}
424
425/* This macro, given a list of generic parameters and type
426bounds, adds the type bounds to all generic type parameters
427(and leaves the generic lifetime parameters unchanged) */
428#[doc(hidden)]
429#[macro_export]
430macro_rules! add_type_bounds {
431    (
432        ( $typ:ident $(, $rest:tt)* ) // type parameter
433        ( $($bounds:tt)* )
434        { $($prefix:tt)* }
435        { $($suffix:tt)* }
436    ) => {
437        add_type_bounds!{
438            ( $(, $rest)* )
439            ( $($bounds)* )
440            { $($prefix)* $typ : $($bounds)*}
441            { $($suffix)* }
442        }
443    };
444    (
445        ( $lifetime:tt $(, $rest:tt)* ) // lifetime parameter
446        ( $($bounds:tt)* )
447        { $($prefix:tt)* }
448        { $($suffix:tt)* }
449    ) => {
450        add_type_bounds!{
451            ( $(, $rest)* )
452            ( $($bounds)* )
453            { $($prefix)* $lifetime }
454            { $($suffix)* }
455        }
456    };
457    (
458        ( , $($rest:tt)* ) // add the comma to the prefix
459        ( $($bounds:tt)* )
460        { $($prefix:tt)* }
461        { $($suffix:tt)* }
462    ) => {
463        add_type_bounds!{
464            ( $($rest)* )
465            ( $($bounds)* )
466            { $($prefix)* , }
467            { $($suffix)* }
468        }
469    };
470    (
471        (  ) // no more generic params to consume
472        ( $($bounds:tt)* )
473        { $($prefix:tt)* }
474        { $($suffix:tt)* }
475    ) => {
476        $($prefix)* $($suffix)*
477    }
478}
479
480#[cfg(all(test, feature = "std"))]
481mod tests {
482    use std::error::Error;
483    use std::str::FromStr;
484
485    #[test]
486    fn single_error_case() {
487        custom_error!(MyError Bad="bad");
488        assert_eq!("bad", MyError::Bad.to_string());
489    }
490
491    #[test]
492    fn single_error_struct_case() {
493        custom_error!(MyError {} = "bad");
494        assert_eq!("bad", MyError {}.to_string());
495    }
496
497    #[test]
498    #[allow(dead_code)]
499    fn three_error_cases() {
500        custom_error!(MyError NotPerfect=":/", Bad="bad", Awfull="arghhh");
501        assert_eq!("arghhh", MyError::Awfull.to_string())
502    }
503
504    #[test]
505    fn with_error_data() {
506        custom_error!(MyError
507            Bad                            = "bad",
508            Catastrophic{broken_things:u8} = "{broken_things} things are broken"
509        );
510        assert_eq!("bad", MyError::Bad.to_string());
511        assert_eq!(
512            "9 things are broken",
513            MyError::Catastrophic { broken_things: 9 }.to_string()
514        );
515    }
516
517    #[test]
518    fn with_doc_comments_for_variants() {
519        custom_error! {MyError
520            /// A bad error
521            Bad="bad",
522            /// Terrible error
523            Terrible="terrible"
524        }
525        assert!(MyError::Bad.source().is_none());
526        assert!(MyError::Terrible.source().is_none());
527    }
528
529    #[test]
530    #[allow(dead_code)]
531    fn with_doc_comments_for_fields() {
532        custom_error! {MyError
533            Bad {
534                /// Name of the bad field
535                bad: &'static str
536            } = "bad {bad}",
537            Terrible {
538                /// Name of the terrible field
539                terrible: &'static str
540            } = "bad {terrible}",
541        }
542        assert!(MyError::Bad { bad: "heh" }.source().is_none());
543    }
544
545    #[test]
546    fn enum_with_field_lifetime() {
547        custom_error!(MyError
548            Problem{description: &'static str} = "{description}"
549        );
550        assert_eq!("bad", MyError::Problem { description: "bad" }.to_string());
551    }
552
553    #[test]
554    fn struct_with_field_lifetime() {
555        custom_error!(MyError {description: &'static str} = "{}");
556        assert_eq!("bad", MyError { description: "bad" }.to_string());
557    }
558
559    #[test]
560    fn enum_with_derive() {
561        custom_error! {
562            #[derive(PartialEq, PartialOrd)]
563            #[derive(Clone)]
564            MyError A = "A", B{b:u8} = "B({b})", C = "C"
565        };
566        // PartialEq
567        assert_eq!(MyError::A, MyError::A);
568        assert_eq!(MyError::B { b: 1 }, MyError::B { b: 1 });
569        assert_ne!(MyError::B { b: 0 }, MyError::B { b: 1 });
570        assert_ne!(MyError::A, MyError::B { b: 1 });
571        // PartialOrd
572        assert!(MyError::A < MyError::B { b: 1 });
573        assert!(MyError::B { b: 1 } < MyError::B { b: 2 });
574        assert!(MyError::B { b: 2 } < MyError::C);
575        // Clone
576        assert_eq!(MyError::A.clone(), MyError::A);
577    }
578
579    #[test]
580    fn enum_with_pub_derive_and_doc_comment() {
581        custom_error! {
582           ///Doc comment
583           #[derive(PartialEq, PartialOrd)]
584           pub MyError A = "A"
585        };
586        assert_eq!(MyError::A, MyError::A);
587    }
588
589    #[test]
590    fn enum_with_box_dyn_source() {
591        // See https://github.com/lovasoa/custom_error/issues/20
592        custom_error! {pub MyError
593            Dynamic { source: Box<dyn Error> } = "dynamic",
594        }
595        let err = u8::from_str("x").unwrap_err();
596        assert_eq!(
597            err.to_string(),
598            MyError::Dynamic {
599                source: Box::new(err)
600            }
601            .source()
602            .unwrap()
603            .to_string()
604        );
605    }
606
607    #[test]
608    fn struct_with_box_dyn_source() {
609        custom_error! {pub MyError
610            { source: Box<dyn Error> } = "dynamic",
611        }
612        let err = u8::from_str("x").unwrap_err();
613        assert_eq!(
614            err.to_string(),
615            MyError {
616                source: Box::new(err)
617            }
618            .source()
619            .unwrap()
620            .to_string()
621        );
622    }
623
624    #[test]
625    fn struct_with_public_field() {
626        mod inner {
627            custom_error! {pub MyError {x: &'static str} = "{}"}
628        }
629        assert_eq!("hello", inner::MyError { x: "hello" }.to_string());
630    }
631
632    #[test]
633    fn struct_with_field_documentation() {
634        custom_error! {
635            pub MyError {
636                /// This is a doc comment
637                x: &'static str
638            } = "{}"
639        }
640        assert_eq!("hello", MyError { x: "hello" }.to_string());
641    }
642
643    #[test]
644    fn struct_with_error_data() {
645        custom_error!(MyError { broken_things: u8 } = "{broken_things} things are broken");
646        assert_eq!(
647            "9 things are broken",
648            MyError { broken_things: 9 }.to_string()
649        );
650    }
651
652    #[test]
653    fn struct_with_derive() {
654        custom_error!(
655            #[derive(PartialEq, PartialOrd, Clone, Default)]
656            MyError { x: u8 } = ":("
657        );
658        assert_eq!(MyError { x: 9 }, MyError { x: 9 }); // Has PartialEq
659        assert_eq!(MyError { x: 0 }.clone(), MyError { x: 0 }); // Has Clone
660        assert_eq!(MyError::default(), MyError { x: 0 }); // Has Default
661        assert!(MyError { x: 0 } < MyError { x: 1 }); // Has PartialOrd
662    }
663
664    #[test]
665    fn with_multiple_error_data() {
666        custom_error!(E X{a:u8, b:u8, c:u8} = "{c} {b} {a}");
667
668        assert_eq!("3 2 1", E::X { a: 1, b: 2, c: 3 }.to_string());
669    }
670
671    #[test]
672    fn struct_with_multiple_error_data() {
673        custom_error!(
674            E {
675                a: u8,
676                b: u8,
677                c: u8
678            } = "{c} {b} {a}"
679        );
680
681        assert_eq!("3 2 1", E { a: 1, b: 2, c: 3 }.to_string());
682    }
683
684    #[test]
685    fn source() {
686        use std::{error::Error, io};
687        custom_error!(E A{source: io::Error}="");
688        let source: io::Error = io::ErrorKind::InvalidData.into();
689        assert_eq!(
690            source.to_string(),
691            E::A { source }.source().unwrap().to_string()
692        );
693    }
694
695    #[test]
696    fn struct_source() {
697        use std::{error::Error, io};
698        custom_error!(E { source: io::Error } = "");
699        let source: io::Error = io::ErrorKind::InvalidData.into();
700        assert_eq!(
701            source.to_string(),
702            E { source }.source().unwrap().to_string()
703        );
704    }
705
706    #[test]
707    fn from_source() {
708        use std::io;
709        custom_error!(E A{source: io::Error}="bella vita");
710        let source = io::Error::from(io::ErrorKind::InvalidData);
711        assert_eq!("bella vita", E::from(source).to_string());
712    }
713
714    #[test]
715    fn struct_from_source() {
716        use std::io;
717        custom_error!(E { source: io::Error } = "bella vita");
718        let source = io::Error::from(io::ErrorKind::InvalidData);
719        assert_eq!("bella vita", E::from(source).to_string());
720    }
721
722    #[test]
723    #[allow(dead_code)]
724    fn with_source_and_others() {
725        use std::{error::Error, io};
726        custom_error!(MyError Zero="", One{x:u8}="", Two{x:u8, source:io::Error}="{x}");
727        fn source() -> io::Error {
728            io::ErrorKind::AlreadyExists.into()
729        }
730        let my_err = MyError::Two {
731            x: 42,
732            source: source(),
733        };
734        assert_eq!("42", my_err.to_string());
735        assert_eq!(source().to_string(), my_err.source().unwrap().to_string());
736    }
737
738    #[test]
739    #[allow(dead_code)]
740    fn struct_with_source_and_others() {
741        use std::{error::Error, io};
742        custom_error!(
743            MyError {
744                x: u8,
745                source: io::Error
746            } = "{x}"
747        );
748        fn source() -> io::Error {
749            io::ErrorKind::AlreadyExists.into()
750        }
751        let my_err = MyError {
752            x: 42,
753            source: source(),
754        };
755        assert_eq!("42", my_err.to_string());
756        assert_eq!(source().to_string(), my_err.source().unwrap().to_string());
757    }
758
759    #[test]
760    fn pub_error() {
761        mod my_mod {
762            custom_error! {pub MyError Case1="case1"}
763        }
764        assert_eq!("case1", my_mod::MyError::Case1.to_string());
765    }
766
767    #[test]
768    fn pub_error_struct() {
769        mod my_mod {
770            custom_error! {pub MyError{} = "case1"}
771        }
772        assert_eq!("case1", my_mod::MyError {}.to_string());
773    }
774
775    #[test]
776    fn pub_error_struct_fields() {
777        mod my_mod {
778            custom_error! {pub MyError{x:u8} = "x={x}"}
779        }
780        assert_eq!("x=9", my_mod::MyError { x: 9 }.to_string());
781    }
782
783    #[test]
784    fn generic_error() {
785        custom_error! {MyError<X,Y> E1{x:X,y:Y}="x={x} y={y}", E2="e2"}
786        assert_eq!("x=42 y=42", MyError::E1 { x: 42u8, y: 42u8 }.to_string());
787        assert_eq!("e2", MyError::E2::<u8, u8>.to_string());
788    }
789
790    #[test]
791    fn generic_error_struct() {
792        custom_error! {MyError<X,Y>{x:X,y:Y}="x={x} y={y}"}
793        assert_eq!("x=42 y=42", MyError { x: 42u8, y: 42u8 }.to_string());
794    }
795
796    #[test]
797    fn single_error_case_with_braces() {
798        custom_error! {MyError Bad="bad"}
799        assert_eq!("bad", MyError::Bad.to_string());
800    }
801
802    #[test]
803    fn single_error_struct_case_with_braces() {
804        custom_error! {MyError{} ="bad"}
805        assert_eq!("bad", MyError {}.to_string())
806    }
807
808    #[test]
809    fn trailing_comma() {
810        custom_error! {MyError1 A="a",}
811        custom_error! {MyError2 A="a", B="b",}
812
813        assert_eq!("a", MyError1::A.to_string());
814        assert_eq!("a", MyError2::A.to_string());
815        assert_eq!("b", MyError2::B.to_string());
816    }
817
818    #[test]
819    fn with_custom_formatting() {
820        custom_error! {MyError
821            Complex{a:u8, b:u8} = @{
822                if a+b == 0 {
823                    "zero".to_string()
824                } else {
825                    (a+b).to_string()
826                }
827            },
828            Simple = "simple"
829        }
830
831        assert_eq!("zero", MyError::Complex { a: 0, b: 0 }.to_string());
832        assert_eq!("3", MyError::Complex { a: 2, b: 1 }.to_string());
833        assert_eq!("simple", MyError::Simple.to_string());
834    }
835
836    #[test]
837    fn struct_with_custom_formatting() {
838        custom_error! {MyError{a:u8, b:u8} = @{
839                if a+b == 0 {
840                    "zero".to_string()
841                } else {
842                    (a+b).to_string()
843                }
844            }
845        }
846
847        assert_eq!("zero", MyError { a: 0, b: 0 }.to_string());
848        assert_eq!("3", MyError { a: 2, b: 1 }.to_string());
849    }
850
851    #[test]
852    fn custom_format_source() {
853        use std::io;
854
855        custom_error! {MyError
856            Io{source:io::Error} = @{format!("IO Error occurred: {:?}", source.kind())}
857        }
858
859        assert_eq!(
860            "IO Error occurred: Interrupted",
861            MyError::Io {
862                source: io::ErrorKind::Interrupted.into()
863            }
864            .to_string()
865        )
866    }
867
868    #[test]
869    fn struct_custom_format_source() {
870        use std::io;
871
872        custom_error! {MyError{source:io::Error} = @{format!("IO Error occurred: {:?}", source.kind())} }
873
874        assert_eq!(
875            "IO Error occurred: Interrupted",
876            MyError {
877                source: io::ErrorKind::Interrupted.into()
878            }
879            .to_string()
880        )
881    }
882
883    #[test]
884    fn lifetime_source_param() {
885        #[derive(Debug)]
886        struct SourceError<'my_lifetime> {
887            x: &'my_lifetime str,
888        }
889        impl<'a> std::fmt::Display for SourceError<'a> {
890            fn fmt(&self, _: &mut std::fmt::Formatter) -> std::fmt::Result {
891                Ok(())
892            }
893        }
894        impl<'a> std::error::Error for SourceError<'a> {}
895
896        custom_error! { MyError<'source_lifetime>
897            Sourced { lifetimed : SourceError<'source_lifetime> } = @{ lifetimed.x },
898            Other { source: std::fmt::Error } = "other error"
899        }
900
901        let sourced = MyError::Sourced {
902            lifetimed: SourceError {
903                x: "I am the source",
904            },
905        };
906        assert_eq!("I am the source", sourced.to_string());
907        let other_err: MyError = std::fmt::Error.into();
908        assert_eq!("other error", other_err.to_string());
909    }
910
911    #[test]
912    fn struct_lifetime_source_param() {
913        #[derive(Debug)]
914        struct SourceError<'my_lifetime> {
915            x: &'my_lifetime str,
916        }
917        impl<'a> std::fmt::Display for SourceError<'a> {
918            fn fmt(&self, _: &mut std::fmt::Formatter) -> std::fmt::Result {
919                Ok(())
920            }
921        }
922        impl<'a> std::error::Error for SourceError<'a> {}
923
924        custom_error! { MyError<'source_lifetime>{
925            lifetimed : SourceError<'source_lifetime>
926        } = @{ lifetimed.x },}
927
928        let sourced = MyError {
929            lifetimed: SourceError {
930                x: "I am the source",
931            },
932        };
933        assert_eq!("I am the source", sourced.to_string());
934    }
935
936    #[test]
937    fn lifetime_param_and_type_param() {
938        #[derive(Debug)]
939        struct MyType<'a, T> {
940            data: &'a str,
941            _y: T,
942        }
943        custom_error! { MyError<'a,T>
944            X { d: MyType<'a,T> } = @{ format!("error x: {}", d.data) },
945            Y { d: T } = "error y"
946        }
947        let err = MyError::X {
948            d: MyType {
949                data: "hello",
950                _y: 42i8,
951            },
952        };
953        assert_eq!("error x: hello", err.to_string());
954        let err_y = MyError::Y {
955            d: String::from("my string"),
956        };
957        assert_eq!("error y", err_y.to_string());
958    }
959
960    #[test]
961    fn struct_lifetime_param_and_type_param() {
962        #[derive(Debug)]
963        struct MyType<'a, T> {
964            data: &'a str,
965            _y: T,
966        }
967        custom_error! { MyError<'a,T> {
968            d: MyType<'a,T>
969        } = @{ format!("error x: {}", d.data) } }
970        let err = MyError {
971            d: MyType {
972                data: "hello",
973                _y: 42i8,
974            },
975        };
976        assert_eq!("error x: hello", err.to_string());
977    }
978}