custom_error/
lib.rs

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