macro_rules! generate_code {
{
$(
$(#[$dim_meta:meta])*
$field:ident : $type:ident {
$(
$variant:ident $({ $($var_field:ident : $var_type:ty),+ })? => $lit:literal
),+ $(,)?
}
)+
} => {
$(
$(#[$dim_meta])*
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[allow(missing_docs)]
pub enum $type {
$(
$variant $({ $($var_field: $var_type),+ })?
),+
}
generate_code!(@enum_try_from $type [] [$(($lit $variant $({$($var_field)+})?))+]);
generate_code!(@enum_display $type [] [$(($lit $variant $({$($var_field)+})?))+]);
)+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Specifier {
$(
$(#[$dim_meta])*
pub $field: $type
),+
}
generate_code!(@fn_format_value
$(
[$field $type $([$lit $variant $([$($var_field)+])?])+]
)+
);
impl Default for Specifier {
fn default() -> Self {
Self {
$(
$field: generate_code!(@first_variant $type $($variant)+)
),+
}
}
}
impl fmt::Display for Specifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
concat!($("{", stringify!($field), "}"),+),
$(
$field=self.$field
),+
)
}
}
};
(@enum_try_from
$type:ident [$($munched:tt)*] [($lit:literal $variant:ident) $($tail:tt)*]
) => {
generate_code!(@enum_try_from $type [$($munched)* ($lit $variant)] [$($tail)*]);
};
(@enum_try_from
$type:ident [$($munched:tt)*] [($lit:literal $variant:ident $_:tt) $($tail:tt)*]
) => {
};
(@enum_try_from
$type:ident [$(($lit:literal $variant:ident))+] []
) => {
impl TryFrom<&str> for $type {
type Error = ();
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
$($lit => Ok($type::$variant),)+
_ => Err(())
}
}
}
};
(@enum_display
$type:ident [$($munched:tt)*] [($lit:literal $variant:ident) $($tail:tt)*]
) => {
generate_code!(@enum_display $type [$($munched)* ($lit $variant)] [$($tail)*]);
};
(@enum_display
$type:ident [$($munched:tt)*] [($lit:literal $variant:ident $_:tt) $($tail:tt)*]
) => {
};
(@enum_display
$type:ident [$(($lit:literal $variant:ident))+] []
) => {
impl fmt::Display for $type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
$($type::$variant => write!(f, $lit),)+
}
}
}
};
(@first_variant $type:ident $first:ident $($rest:ident)*) => { $type::$first };
(@fn_format_value $($dim:tt)+) => {
pub fn format_value<V>(specifier: &Specifier, value: &V, f: &mut fmt::Formatter) -> fmt::Result
where
V: fmt::Display
+ fmt::Debug
+ fmt::Octal
+ fmt::LowerHex
+ fmt::UpperHex
+ fmt::Binary
+ fmt::LowerExp
+ fmt::UpperExp,
{
generate_code!(@matcher (specifier, value, f, "", []) $($dim)+)
}
};
(@matcher ($spec:ident, $val:ident, $out:ident, $prefix:expr, $named_args:tt) $head_dim:tt $($tail_dim:tt)+) => {
generate_code!(@matcher_branch ($spec, $val, $out, $prefix, $named_args) $head_dim [$($tail_dim)+])
};
(@matcher ($spec:ident, $val:ident, $out:ident, $prefix:expr, $named_args:tt) $last_dim:tt) => {
generate_code!(@matcher_leaf ($spec, $val, $out, $prefix, $named_args) $last_dim)
};
(@matcher_branch
($spec:ident, $val:ident, $out:ident, $prefix:expr, $named_args:tt)
[$field:ident $type:ident $([$lit:literal $variant:ident $([$($var_field:ident)+])?])+]
$tail:tt
) => {
match $spec.$field {
$(
$type::$variant $({ $($var_field),+ })? => generate_code!(
@matcher_tail
($spec, $val, $out, concat!($prefix, $lit))
$named_args
[$($($var_field)+)?]
$tail
)
),+
}
};
(@matcher_tail ($spec:ident, $val:ident, $out:ident, $prefix:expr) [$($lhs_arg:ident)*] [$($rhs_arg:ident)*] [$($dim:tt)+]) => {
generate_code!(@matcher ($spec, $val, $out, $prefix, [$($lhs_arg)* $($rhs_arg)*]) $($dim)+)
};
(@matcher_leaf
($spec:ident, $val:ident, $out:ident, $prefix:expr, $named_args:tt)
[$field:ident $type:ident $([$lit:literal $variant:ident $([$($var_field:ident)+])?])+]
) => {
match $spec.$field {
$(
$type::$variant $({ $($var_field),+ })? => generate_code!(
@matcher_concat_args
($spec, $val, $out, concat!($prefix, $lit))
$named_args
[$($($var_field)+)?]
)
),+
}
};
(@matcher_concat_args ($spec:ident, $val:ident, $out:ident, $format_str:expr) [$($lhs_arg:ident)*] [$($rhs_arg:ident)*]) => {
generate_code!(@matcher_write ($spec, $val, $out, $format_str) [$($lhs_arg)* $($rhs_arg)*])
};
(@matcher_write ($spec:ident, $val:ident, $out:ident, $format_str:expr) [$($named_arg:ident)*]) => {
write!(
$out,
concat!("{:", $format_str, "}"),
$val,
$($named_arg = $named_arg),*
)
};
}