#[doc = crate::_tags!(construction error)]
#[doc = crate::_doc_location!("code/error")]
#[macro_export]
#[cfg_attr(cargo_primary_package, doc(hidden))]
macro_rules! _define_error {
(
// Defines a standalone error struct with optional tuple or field variants.
//
// # Arguments
// - `first_attributes` : Optional attributes applied first
// - `struct_vis` : Struct visibility (e.g., `pub`, `pub(crate)`)
individual:
$(#[$first_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),+ $(,)? })? $($(#[$last_attributes:meta])*,)?
$(+location: $($location:literal)+ ,)?
$(+tag: $($tag:expr)+ ,)?
$DOC_NAME:ident = $doc_str:literal,
$self:ident + $fmt:ident => $display_expr:expr
$(,)?
) => {
$crate::CONST! { pub(crate) $DOC_NAME = $doc_str; }
$( $(#[doc = $tag])+ )?
#[doc = $crate::_tags!(error)] #[doc = $DOC_NAME!()]
$(#[doc = $crate::_doc_location![$($location)?]])? $(#[$first_attributes])*
$($(#[$last_attributes])*)?
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
$struct_vis struct $struct_name
$(( $($e_vis $e_ty),+ ) )? $(; $($_a)?)? // tuple-struct↓
$({ $( $(#[$f_attr])* $f_vis $f_name: $f_ty),+ })?
impl $crate::Error for $struct_name {}
impl $crate::Display for $struct_name {
fn fmt(&$self, $fmt: &mut $crate::Formatter<'_>) -> $crate::FmtResult<()> {
$display_expr
}
}
};
(
composite: fmt($fmt:ident)
$(+location: $($location:literal)+ ,)?
$(+tag: $tag:expr ,)?
$(#[$enum_attr:meta])*
$vis:vis enum $composite_error_name:ident { $(
$(+tag: $tag_variant:expr ,)?
$(#[$variant_attr:meta])*
$DOC_VARIANT:ident:
$(+const$($_c:lifetime)?)?
$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 = $tag])?
#[doc = $crate::_tags!(error_composite)] $(#[$enum_attr])*
$(#[doc = $crate::_doc_location![$($location)?]])? #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
$vis enum $composite_error_name { $(
$(#[doc = $tag_variant])?
#[doc = $crate::_tags!(error)] #[doc = $DOC_VARIANT!()]
$(#[$variant_attr])*
$variant_name
$(( $($e_ty),+ ))? $({ $($(#[$f_attr])* $f_name: $f_ty),+ })? ),+ }
impl $crate::Error for $composite_error_name {}
impl $crate::Display for $composite_error_name {
fn fmt(&self, $fmt: &mut $crate::Formatter<'_>) -> $crate::FmtResult<()> {
match self { $(
$(#[$variant_attr])*
#[allow(unused_doc_comments, reason = "repeated here for feature-gating")]
$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::define_error! { $(+const$($_c)?)?
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] ),+ })? }
)+
};
(
$(+const$($_c:lifetime)?)?
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),+ })? $(,)? ) => {
$crate::paste! {
impl $for_composite {
$( #[doc = "*const*"] $($_c)?)?
#[doc = "method equivalent to `From<" $from_individual "> for " $for_composite "`."]
#[allow(dead_code, reason = "seldomly used")]
pub $(const$($_c)?)? fn
[<from_ $from_individual:snake:lower>]($fn_arg: $from_individual)
-> $for_composite {
$for_composite::$variant_name
$(( $($e_expr),+ ))? $({ $($f_name: $f_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)
}
}
}
};
}
#[doc(inline)]
pub use _define_error as define_error;
#[cfg(test)]
mod tests {
use super::define_error;
#[test]
fn define_error() {
define_error! { individual: pub struct UnitStruct;
+tag: "tag",
DOC_UNIT_STRUCT = "docstring", self+f => write!(f, "display"),
}
define_error! { individual: pub struct SingleElement(pub(crate) Option<u8>);
DOC_SINGLE_ELEMENT = "docstring", self+f => write!(f, "display"),
}
define_error! { individual: pub struct MultipleElements(pub i32, u32,);
DOC_MULTI_ELEMENT = "docstring", self+f => write!(f, "display")
}
define_error! { individual: pub struct StructFields {
#[doc = "field1"] pub f1: bool,
#[doc = "field2"] f2: Option<char>
}
DOC_STRUCT_FIELDS = "docstring", self+f => write!(f, "display")
}
define_error! { composite: fmt(f)
pub enum CompositeSuperset {
+tag: "tag",
DOC_UNIT_STRUCT: +const 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: +const SuperStruct { f1: bool, f2: Option<char> }
=> StructFields { f1: *f1, f2: *f2 },
}
}
define_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: +const SubStruct { f1: bool, f2: Option<char> }
=> StructFields { f1: *f1, f2: *f2 },
}
}
define_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 })
];
assert![CompositeSubset::try_from(CompositeSuperset::SuperUnit).is_err()];
}
}