macro_rules! unit_errors {
    ($($name:ident = $description:tt,)*) => {
        $(
            #[derive(Debug)]
            pub struct $name;

            impl $crate::std::fmt::Display for $name {
                fn fmt(&self, f: &mut $crate::std::fmt::Formatter) ->
                    $crate::std::result::Result<(), $crate::std::fmt::Error> {
                    write!(f, "{}", $description)
                }
            }

            impl $crate::std::error::Error for $name {
                fn description(&self) -> &str {
                    $description
                }
            }
        )*
    };
    ($($name:ident = $description:tt),*) => {
        unit_errors!($($name = $description,)*);
    };
}

macro_rules! aggregate_errors {
    ($($name:ident { $($other_name:ident($other_ty:ty)),* })*) => {
        $(
            #[derive(Debug)]
            pub enum $name {
                $($other_name($other_ty),)*
            }

            impl $crate::std::fmt::Display for $name {
                fn fmt(&self, f: &mut $crate::std::fmt::Formatter) ->
                    $crate::std::result::Result<(), $crate::std::fmt::Error> {
                    match self {
                        $(&$name::$other_name(ref other) => other.fmt(f)),*
                    }
                }
            }

            impl $crate::std::error::Error for $name {
                fn description(&self) -> &str {
                    match self {
                        $(&$name::$other_name(ref other) => other.description()),*
                    }
                }

                fn cause(&self) -> $crate::std::option::Option<&$crate::std::error::Error> {
                    match self {
                        $(&$name::$other_name(ref other) => other.cause()),*
                    }
                }
            }

            $(
                impl $crate::std::convert::From<$other_ty> for $name {
                    fn from(other: $other_ty) -> Self {
                        $name::$other_name(other)
                    }
                }
            )*
        )*
    };
    ($($name:ident { $($other_name:ident($other_ty:ty),)* })*) => {
        aggregate_errors!($($name { $($other_name($other_ty)),* })*);
    };
}
