#![no_std]
use core::fmt;
#[macro_export]
macro_rules! strum {
(
$(#[$enum_meta:meta])*
$enum_vis:vis enum $enum_name:ident {
$(
$(#[$variant_meta:meta])*
$variant_name:ident = $string:literal $(| $alias:literal)* $(= $discriminant:expr)?
),* $(,)?
}
$($rest:tt)*
) => {
$crate::strum! {@tail
{
[$(#[$enum_meta])*]
[$enum_vis]
$enum_name
[$([$(#[$variant_meta])*] $variant_name [$string $(| $alias)*] [$($discriminant)?])*]
[$($string)*]
}
$($rest)*
}
};
(@tail
{ $metas:tt $vis:tt $enum_name:ident $variants:tt [$($string:literal)*] }
) => {
$crate::strum! {@define $metas $vis $enum_name $variants []
[$crate::ParseError]
[$crate::ParseError({
const ALL: &'static [&'static ::core::primitive::str] = &[$($string),*];
&ALL
})]
}
};
(@tail
{ $metas:tt $vis:tt $enum_name:ident $variants:tt [$($string:literal)*] }
$(#[$const_meta:meta])* $const_vis:vis const $const_name:ident;
) => {
$crate::strum! {@define $metas $vis $enum_name $variants
[[$(#[$const_meta])*] $const_vis const $const_name]
[$crate::ParseError]
[$crate::ParseError({
const ALL: &'static [&'static ::core::primitive::str] = &[$($string),*];
&ALL
})]
}
};
(@tail
{ $metas:tt $vis:tt $enum_name:ident $variants:tt [$($string:literal)*] }
throws $(#[$error_meta:meta])* $error_name:ident $(;)?
) => {
$crate::strum! {@define $metas $vis $enum_name $variants []
[$error_name]
[$error_name]
}
$crate::strum! {@error [$(#[$error_meta])*] $vis $enum_name $error_name [$($string)*]}
};
(@tail
{ $metas:tt $vis:tt $enum_name:ident $variants:tt [$($string:literal)*] }
$(#[$const_meta:meta])* $const_vis:vis const $const_name:ident;
throws $(#[$error_meta:meta])* $error_name:ident $(;)?
) => {
$crate::strum! {@define $metas $vis $enum_name $variants
[[$(#[$const_meta])*] $const_vis const $const_name]
[$error_name]
[$error_name]
}
$crate::strum! {@error [$(#[$error_meta])*] $vis $enum_name $error_name [$($string)*]}
};
(@define
[$(#[$enum_meta:meta])*]
[$enum_vis:vis]
$enum_name:ident
[$(
[$(#[$variant_meta:meta])*]
$variant_name:ident
[$string:literal $(| $alias:literal)*]
[$($discriminant:expr)?]
)*]
$konst:tt
[$error_ty:ty]
[$error_new:expr]
) => {
$(#[$enum_meta])*
$enum_vis enum $enum_name {
$(
$(#[$variant_meta])*
#[doc = ::core::concat!(" String representation: `", $string, "`")]
$variant_name $(= $discriminant)?,
)*
}
$crate::strum! {@konst $enum_name $konst [$($variant_name)*]}
const _: () = {
use ::core;
impl core::fmt::Display for $enum_name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
fn as_str(e: &$enum_name) -> &core::primitive::str {
match *e {
$($enum_name::$variant_name => $string),*
}
}
core::fmt::Formatter::write_str(f, as_str(self))
}
}
impl core::str::FromStr for $enum_name {
type Err = $error_ty;
fn from_str(s: &core::primitive::str) -> core::result::Result<Self, Self::Err> {
match s {
$(
$string $(| $alias )* => core::result::Result::Ok(Self::$variant_name),
)*
_ => core::result::Result::Err($error_new)
}
}
}
};
};
(@konst $enum_name:ident [] $variant_names:tt) => {};
(@konst $enum_name:ident [[] $vis:vis const $konst:ident] $variant_names:tt) => {
$crate::strum! {@konst $enum_name
[[#[doc = " Every variant of this enum, in declaration order."]] $vis const $konst]
$variant_names
}
};
(@konst $enum_name:ident [[$(#[$const_meta:meta])+] $vis:vis const $konst:ident] [$($variant_name:ident)*]) => {
impl $enum_name {
$(#[$const_meta])+
$vis const $konst: [Self; <[Self]>::len(&[$(Self::$variant_name),*])] =
[$(Self::$variant_name),*];
}
};
(@error
[$(#[$error_meta:meta])*]
[$vis:vis]
$enum_name:ident
$error_name:ident
[$($string:literal)*]
) => {
$(#[$error_meta])*
#[doc = ::core::concat!(" Error returned when parsing [`", ::core::stringify!($enum_name), "`] from a string.")]
$vis struct $error_name;
const _: () = {
use ::core;
impl core::fmt::Display for $error_name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
const ALL: &'static [&'static core::primitive::str] = &[$($string),*];
core::fmt::Display::fmt(&$crate::ParseError(&ALL), f)
}
}
impl core::fmt::Debug for $error_name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut f = core::fmt::Formatter::debug_tuple(f, core::stringify!($error_name));
core::fmt::DebugTuple::field(&mut f, &core::format_args!("{}", self));
core::fmt::DebugTuple::finish(&mut f)
}
}
impl core::error::Error for $error_name {}
};
};
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ParseError(#[doc(hidden)] pub &'static &'static [&'static str]);
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
[] => f.write_str("Uninhabited type is impossible to parse"),
[first] => f.write_fmt(format_args!("Expected string `{first}`")),
[first, second] => f.write_fmt(format_args!("Expected `{first}` or `{second}`")),
[first, rest @ .., last] => {
f.write_fmt(format_args!("Expected one of `{first}`"))?;
for it in rest {
f.write_fmt(format_args!(", `{it}`"))?
}
f.write_fmt(format_args!(", or `{last}`"))
}
}
}
}
impl fmt::Debug for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("ParseError")
.field(&format_args!("{self}"))
.finish()
}
}
impl core::error::Error for ParseError {}