macro_rules! impl_error {
(
individual:
$(#[$attributes:meta])*
$struct_vis:vis struct $struct_name:ident
$(( $($e_vis:vis $e_ty:ty),+ $(,)? ))? $(;$($_a:lifetime)?)? $({ $($(#[$f_attr:meta])* $f_vis:vis $f_name:ident: $f_ty:ty),+ $(,)? })? $DOC_NAME:ident = $doc_str:literal,
$self:ident + $fmt:ident => $display_expr:expr
$(,)?
) => {
$(#[$attributes])*
$crate::CONST! { pub(crate) $DOC_NAME = $doc_str; }
$(#[$attributes])*
#[doc = crate::TAG_ERROR!()]
#[doc = $DOC_NAME!()]
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)]
$struct_vis struct $struct_name
$(( $($e_vis $e_ty),+ ) )? $(; $($_a)?)? $({ $( $(#[$f_attr])* $f_vis $f_name: $f_ty),+ })? $(#[$attributes])*
impl $crate::Display for $struct_name {
fn fmt(&$self, $fmt: &mut $crate::Formatter<'_>) -> $crate::FmtResult<()> {
$display_expr
}
}
$(#[$attributes])*
impl $crate::Error for $struct_name {}
$(#[$attributes])*
impl $crate::ExtError for $struct_name {
type Kind = ();
fn error_eq(&self, other: &Self) -> bool { self == other }
fn error_kind(&self) -> Self::Kind {}
}
};
(
composite: fmt($fmt:ident)
$(#[$enum_attr:meta])*
$vis:vis enum $composite_error_name:ident { $(
$(#[$variant_attr:meta])*
$DOC_VARIANT:ident:
$variant_name:ident
$(( $($e_name:ident| $e_numb:literal: $e_ty:ty),+ ))? $({ $($(#[$f_attr:meta])* $f_name:ident: $f_ty:ty),+ })? => $individual_error_name:ident
$(( $($e_display_expr:expr),+ ))? $({ $($f_display_name:ident: $f_display_exp:expr),+ })? ),+ $(,)? }
) => {
#[doc = crate::TAG_ERROR_COMPOSITE!()]
$(#[$enum_attr])*
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
$vis enum $composite_error_name { $(
$(#[$variant_attr])*
#[doc = $DOC_VARIANT!()]
$variant_name
$(( $($e_ty),+ ))? $({ $($(#[$f_attr])* $f_name: $f_ty),+ })? ),+ }
impl $crate::Error for $composite_error_name {}
impl $crate::ExtError for $composite_error_name {
type Kind = ();
fn error_eq(&self, other: &Self) -> bool { self == other }
fn error_kind(&self) -> Self::Kind {}
}
impl $crate::Display for $composite_error_name {
fn fmt(&self, $fmt: &mut $crate::Formatter<'_>) -> $crate::FmtResult<()> {
match self { $(
$(#[$variant_attr])*
$composite_error_name::$variant_name
$(( $($e_name),+ ))? $({ $($f_name),+ })? =>
$crate::Display::fmt(&$individual_error_name
$(( $($e_display_expr),+ ))? $({ $($f_display_name: $f_display_exp),+})? , $fmt),
)+ }
}
}
$(
$(#[$variant_attr])*
$crate::impl_error! { from(_f): $individual_error_name, for: $composite_error_name
=> $variant_name
$(( $($e_name, $crate::field_of![_f, $e_numb] ),+ ))? $({ $($f_name, $crate::field_of![_f, $f_name] ),+ })? }
)+
};
(
from($fn_arg:ident): $from_individual:ident, for: $for_composite:ident
=> $variant_name:ident
$(( $($e_name:ident, $e_expr:expr),+ ))? $({ $($f_name:ident, $f_expr:expr),+ })? $(,)? ) => {
impl From<$from_individual> for $for_composite {
fn from($fn_arg: $from_individual) -> $for_composite {
$for_composite::$variant_name
$(( $($e_expr),+ ))? $({ $($f_name: $f_expr),+ })? }
}
impl TryFrom<$for_composite> for $from_individual {
type Error = crate::FailedErrorConversion;
fn try_from($fn_arg: $for_composite) -> Result<$from_individual, Self::Error> {
match $fn_arg {
$for_composite::$variant_name
$(( $($e_name),+ ))? $({ $($f_name),+ })? => Ok($from_individual
$(( $($e_name),+ ))? $({ $($f_name),+ })? ),
#[allow(unreachable_patterns)]
_ => Err(crate::FailedErrorConversion)
}
}
}
};
(
composite: from($fn_arg:ident): $from_subset:ident, for: $for_superset:ident { $(
$from_variant:ident
$(( $($from_elem:ident),+ ))? $({ $($from_field:ident),+ })? => $for_variant:ident
$(( $($for_elem:ident),+ ))? $({ $($for_field:ident),+ })? ),+ $(,)? }
) => {
impl From<$from_subset> for $for_superset {
fn from($fn_arg: $from_subset) -> $for_superset { match $fn_arg { $(
$from_subset::$from_variant
$(( $($from_elem),+ ))? $({ $($from_field),+ })? => $for_superset::$for_variant
$(( $($for_elem),+ ))? $({ $($for_field),+ })? ),+ } }
}
impl TryFrom<$for_superset> for $from_subset {
type Error = crate::FailedErrorConversion;
fn try_from($fn_arg: $for_superset)
-> Result<$from_subset, crate::FailedErrorConversion> { match $fn_arg { $(
$for_superset::$for_variant
$(( $($for_elem),+ ))? $({ $($for_field),+ })? => Ok($from_subset::$from_variant
$(( $($from_elem),+ ))? $({ $($from_field),+ })? ),)+
_ => Err(crate::FailedErrorConversion)
}
}
}
};
}
pub(crate) use impl_error;
#[cfg(test)]
mod tests {
use super::impl_error;
#[test]
fn impl_error() {
impl_error! { individual: pub struct UnitStruct;
DOC_UNIT_STRUCT = "docstring", self+f => write!(f, "display"),
}
impl_error! { individual: pub struct SingleElement(pub(crate) Option<u8>);
DOC_SINGLE_ELEMENT = "docstring", self+f => write!(f, "display"),
}
impl_error! { individual: pub struct MultipleElements(pub i32, u32,);
DOC_MULTI_ELEMENT = "docstring", self+f => write!(f, "display")
}
impl_error! { individual: pub struct StructFields {
#[doc = "field1"] pub f1: bool,
#[doc = "field2"] f2: Option<char>
}
DOC_STRUCT_FIELDS = "docstring", self+f => write!(f, "display")
}
impl_error! { composite: fmt(f)
pub enum CompositeSuperset {
DOC_UNIT_STRUCT: SuperUnit => UnitStruct,
DOC_SINGLE_ELEMENT: SuperSingle(i|0: Option<u8>) => SingleElement(*i),
DOC_MULTI_ELEMENT: SuperMultiple(i|0: i32, j|1: u32) => MultipleElements(*i, *j),
DOC_STRUCT_FIELDS: SuperStruct { f1: bool, f2: Option<char> }
=> StructFields { f1: *f1, f2: *f2 },
}
}
impl_error! { composite: fmt(f)
pub enum CompositeSubset {
DOC_SINGLE_ELEMENT: SubSingle(i|0: Option<u8>) => SingleElement(*i),
DOC_MULTI_ELEMENT: SubMultiple(i|0: i32, j|1: u32) => MultipleElements(*i, *j),
DOC_STRUCT_FIELDS: SubStruct { f1: bool, f2: Option<char> }
=> StructFields { f1: *f1, f2: *f2 },
}
}
impl_error! { composite: from(f): CompositeSubset, for: CompositeSuperset {
SubSingle(i) => SuperSingle(i),
SubMultiple(i, j) => SuperMultiple(i, j),
SubStruct { f1, f2 } => SuperStruct { f1, f2 },
}}
assert_eq![CompositeSuperset::SuperUnit, UnitStruct.into()];
assert![UnitStruct::try_from(CompositeSuperset::SuperUnit).is_ok()];
assert![UnitStruct::try_from(CompositeSuperset::SuperSingle(None)).is_err()];
assert_eq![CompositeSuperset::SuperSingle(Some(1)), SingleElement(Some(1)).into()];
assert![SingleElement::try_from(CompositeSuperset::SuperSingle(Some(2))).is_ok()];
assert_eq![CompositeSuperset::SuperMultiple(3, 5), MultipleElements(3, 5).into()];
assert![MultipleElements::try_from(CompositeSuperset::SuperMultiple(7, 13)).is_ok()];
assert_eq![
CompositeSuperset::SuperStruct { f1: true, f2: Some('a') },
StructFields { f1: true, f2: Some('a') }.into()
];
assert![
StructFields::try_from(CompositeSuperset::SuperStruct { f1: false, f2: None }).is_ok()
];
assert_eq![
CompositeSuperset::SuperSingle(Some(1)),
CompositeSubset::SubSingle(Some(1)).into()
];
assert_eq![
CompositeSubset::try_from(CompositeSuperset::SuperSingle(Some(2))),
Ok(CompositeSubset::SubSingle(Some(2)))
];
assert_eq![
CompositeSuperset::SuperMultiple(4, 6),
CompositeSubset::SubMultiple(4, 6).into()
];
assert_eq![
CompositeSubset::try_from(CompositeSuperset::SuperMultiple(5, 7)),
Ok(CompositeSubset::SubMultiple(5, 7))
];
assert_eq![
CompositeSuperset::SuperStruct { f1: true, f2: Some('z') },
CompositeSubset::SubStruct { f1: true, f2: Some('z') }.into()
];
assert_eq![
CompositeSubset::try_from(CompositeSuperset::SuperStruct { f1: true, f2: None }),
Ok(CompositeSubset::SubStruct { f1: true, f2: None })
];
}
}