macro_rules! wire_enum {
(
$(#[$enum_meta:meta])*
$vis:vis enum $Enum:ident {
$(
$(#[$var_meta:meta])*
$Variant:ident = $disc:literal => $wire:literal
),+ $(,)?
}
error $Error:ident($label:literal);
numeric: $numeric_fn:ident($repr:ty);
tests: $tests_mod:ident;
) => {
wire_enum! {
$(#[$enum_meta])*
$vis enum $Enum {
$(
$(#[$var_meta])*
$Variant = $disc => $wire
),+
}
error $Error($label);
numeric: $numeric_fn($repr);
}
wire_enum! { @tests $Enum, $Error, $label, $tests_mod, $($Variant => $wire),+ }
};
(
@tests $Enum:ident, $Error:ident, $label:literal, $tests_mod:ident,
$($Variant:ident => $wire:literal),+
) => {
#[cfg(test)]
mod $tests_mod {
use super::*;
#[test]
fn display_and_from_str_roundtrip() {
$(
assert_eq!($Enum::$Variant.to_string(), $wire);
assert_eq!($wire.parse::<$Enum>(), Ok($Enum::$Variant));
)+
}
#[test]
fn from_str_rejects_wrong_case() {
$({
let wire: &str = $wire;
let lower = wire.to_ascii_lowercase();
if lower != wire {
assert!(
lower.parse::<$Enum>().is_err(),
concat!(
stringify!($Enum),
" must reject lowercased \"",
$wire,
"\"",
),
);
}
let upper = wire.to_ascii_uppercase();
if upper != wire {
assert!(
upper.parse::<$Enum>().is_err(),
concat!(
stringify!($Enum),
" must reject uppercased \"",
$wire,
"\"",
),
);
}
})+
}
#[test]
fn from_str_rejects_unknown() {
let err = "__wire_enum_bogus_sentinel__".parse::<$Enum>().unwrap_err();
assert_eq!(err.0, "__wire_enum_bogus_sentinel__");
assert!(err.to_string().contains($label));
}
}
};
(
$(#[$enum_meta:meta])*
$vis:vis enum $Enum:ident {
$(
$(#[$var_meta:meta])*
$Variant:ident = $disc:literal => $wire:literal
),+ $(,)?
}
error $Error:ident($label:literal);
numeric: $numeric_fn:ident($repr:ty);
) => {
wire_enum! {
$(#[$enum_meta])*
$vis enum $Enum {
$(
$(#[$var_meta])*
$Variant = $disc => $wire
),+
}
error $Error($label);
}
impl $Enum {
pub fn $numeric_fn(n: $repr) -> Option<Self> {
match n {
$( $disc => Some(Self::$Variant), )+
_ => None,
}
}
pub fn as_number(&self) -> $repr {
*self as $repr
}
}
};
(
$(#[$enum_meta:meta])*
$vis:vis enum $Enum:ident {
$(
$(#[$var_meta:meta])*
$Variant:ident $(= $disc:literal)? => $wire:literal
),+ $(,)?
}
error $Error:ident($label:literal);
tests: $tests_mod:ident;
) => {
wire_enum! {
$(#[$enum_meta])*
$vis enum $Enum {
$(
$(#[$var_meta])*
$Variant $(= $disc)? => $wire
),+
}
error $Error($label);
}
wire_enum! { @tests $Enum, $Error, $label, $tests_mod, $($Variant => $wire),+ }
};
(
$(#[$enum_meta:meta])*
$vis:vis enum $Enum:ident {
$(
$(#[$var_meta:meta])*
$Variant:ident $(= $disc:literal)? => $wire:literal
),+ $(,)?
}
error $Error:ident($label:literal);
) => {
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
$(#[$enum_meta])*
#[non_exhaustive]
#[allow(missing_docs)]
$vis enum $Enum {
$(
$(#[$var_meta])*
$Variant $(= $disc)?,
)+
}
impl $Enum {
pub const fn as_str(&self) -> &'static str {
match self {
$( Self::$Variant => $wire, )+
}
}
}
impl ::std::fmt::Display for $Enum {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
f.write_str(self.as_str())
}
}
#[doc = concat!("Error returned when parsing an invalid ", $label, " string.")]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct $Error(pub String);
impl ::std::fmt::Display for $Error {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, concat!("unknown ", $label, ": {}"), self.0)
}
}
impl ::std::error::Error for $Error {}
impl ::std::str::FromStr for $Enum {
type Err = $Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
$( $wire => Ok(Self::$Variant), )+
_ => Err($Error(s.to_string())),
}
}
}
};
}