#![deny(warnings)]
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(not(feature = "std"))] extern crate core as std;
use std::fmt;
#[doc(hidden)]
#[macro_export]
macro_rules! enum_derive_util {
(@as_expr $e:expr) => {$e};
(@as_item $($i:item)+) => {$($i)+};
(@first_expr $head:expr, $($tail:expr),*) => {$head};
(@first_expr $head:expr) => {$head};
(
@collect_unitary_variants ($callback:ident { $($args:tt)* }),
($(,)*) -> ($($var_names:ident,)*)
) => {
$crate::enum_derive_util! {
@as_item
$callback!{ $($args)* ($($var_names),*) }
}
};
(
@collect_unitary_variants $fixed:tt,
(#[$_attr:meta] $($tail:tt)*) -> ($($var_names:tt)*)
) => {
$crate::enum_derive_util! {
@collect_unitary_variants $fixed,
($($tail)*) -> ($($var_names)*)
}
};
(
@collect_unitary_variants $fixed:tt,
($var:ident $(= $_val:expr)*, $($tail:tt)*) -> ($($var_names:tt)*)
) => {
$crate::enum_derive_util! {
@collect_unitary_variants $fixed,
($($tail)*) -> ($($var_names)* $var,)
}
};
(
@collect_unitary_variants ($name:ident),
($var:ident $_struct:tt, $($tail:tt)*) -> ($($var_names:tt)*)
) => {
const _error: () = "cannot parse unitary variants from enum with non-unitary variants";
};
(
@collect_unary_variants ($callback:ident { $($args:tt)* }),
($(,)*) -> ($($out:tt)*)
) => {
$crate::enum_derive_util! {
@as_item
$callback!{ $($args)* ($($out)*) }
}
};
(
@collect_unary_variants $fixed:tt,
(#[$_attr:meta] $($tail:tt)*) -> ($($out:tt)*)
) => {
$crate::enum_derive_util! {
@collect_unary_variants $fixed,
($($tail)*) -> ($($out)*)
}
};
(
@collect_unary_variants $fixed:tt,
($var_name:ident($var_ty:ty), $($tail:tt)*) -> ($($out:tt)*)
) => {
$crate::enum_derive_util! {
@collect_unary_variants $fixed,
($($tail)*) -> ($($out)* $var_name($var_ty),)
}
};
(
@collect_unary_variants $fixed:tt,
($var_name:ident(pub $var_ty:ty), $($tail:tt)*) -> ($($out:tt)*)
) => {
$crate::enum_derive_util! {
@collect_unary_variants $fixed,
($($tail)*) -> ($($out)* $var_name($var_ty),)
}
};
(
@collect_unary_variants ($name:ident),
($var:ident $_struct:tt, $($tail:tt)*) -> ($($_out:tt)*)
) => {
const _error: () = "cannot parse unary variants from enum with non-unary tuple variants";
};
}
#[macro_export]
macro_rules! IterVariants {
(
@expand ($($pub_:tt)*) $itername:ident, $name:ident ()
) => {
$crate::enum_derive_util! { @as_item $($pub_)* struct $itername; }
impl ::std::iter::Iterator for $itername {
type Item = $name;
fn next(&mut self) -> ::std::option::Option<Self::Item> {
None
}
fn size_hint(&self) -> (usize, ::std::option::Option<usize>) {
(0, Some(0))
}
}
impl ::std::iter::ExactSizeIterator for $itername { }
$crate::enum_derive_util! {
@as_item
impl $name {
#[allow(dead_code)]
$($pub_)* fn iter_variants() -> $itername {
$itername
}
}
}
};
(
@expand ($($pub_:tt)*) $itername:ident, $name:ident ($($var_names:ident),*)
) => {
$crate::enum_derive_util! { @as_item $($pub_)* struct $itername(::std::option::Option<$name>); }
IterVariants! { @iter ($itername, $name), ($($var_names,)*) -> () () (0usize) }
$crate::enum_derive_util! {
@as_item
impl $name {
#[allow(dead_code)]
$($pub_)* fn iter_variants() -> $itername {
$itername(::std::option::Option::Some($crate::enum_derive_util!(@first_expr $($name::$var_names),+)))
}
}
}
};
(
@iter ($itername:ident, $name:ident), () -> ($($next_body:tt)*) ($($size_body:tt)*) ($($count:tt)*)
) => {
$crate::enum_derive_util! {
@as_item
impl ::std::iter::Iterator for $itername {
type Item = $name;
fn next(&mut self) -> ::std::option::Option<Self::Item> {
let next_item = match self.0 {
$($next_body)*
None => None
};
::std::mem::replace(&mut self.0, next_item)
}
fn size_hint(&self) -> (usize, ::std::option::Option<usize>) {
let variants = $($count)*;
let progress = match self.0 {
$($size_body)*
None => variants
};
(variants - progress, ::std::option::Option::Some(variants - progress))
}
}
impl ::std::iter::ExactSizeIterator for $itername { }
}
};
(
@iter ($itername:ident, $name:ident), ($a:ident, $b:ident, $($rest:tt)*) -> ($($next_body:tt)*) ($($size_body:tt)*) ($($count:tt)*)
) => {
IterVariants! {
@iter ($itername, $name), ($b, $($rest)*)
-> (
$($next_body)*
::std::option::Option::Some($name::$a) => ::std::option::Option::Some($name::$b),
)
(
$($size_body)*
::std::option::Option::Some($name::$a) => $($count)*,
)
($($count)* + 1usize)
}
};
(
@iter ($itername:ident, $name:ident), ($a:ident,) -> ($($next_body:tt)*) ($($size_body:tt)*) ($($count:tt)*)
) => {
IterVariants! {
@iter ($itername, $name), ()
-> (
$($next_body)*
::std::option::Option::Some($name::$a) => ::std::option::Option::None,
)
(
$($size_body)*
::std::option::Option::Some($name::$a) => $($count)*,
)
($($count)* + 1usize)
}
};
(($itername:ident) pub enum $name:ident { $($body:tt)* }) => {
$crate::enum_derive_util! {
@collect_unitary_variants
(IterVariants { @expand (pub) $itername, $name }),
($($body)*,) -> ()
}
};
(($itername:ident) enum $name:ident { $($body:tt)* }) => {
$crate::enum_derive_util! {
@collect_unitary_variants
(IterVariants { @expand () $itername, $name }),
($($body)*,) -> ()
}
};
}
#[macro_export]
macro_rules! IterVariantNames {
(
@expand ($($pub_:tt)*) $itername:ident, $name:ident ()
) => {
$crate::enum_derive_util! { @as_item $($pub_)* struct $itername; }
impl ::std::iter::Iterator for $itername {
type Item = &'static str;
fn next(&mut self) -> ::std::option::Option<Self::Item> {
None
}
fn size_hint(&self) -> (usize, ::std::option::Option<usize>) {
(0, Some(0))
}
}
impl ::std::iter::ExactSizeIterator for $itername { }
$crate::enum_derive_util! {
@as_item
impl $name {
#[allow(dead_code)]
$($pub_)* fn iter_variant_names() -> $itername {
$itername
}
}
}
};
(
@expand ($($pub_:tt)*) $itername:ident, $name:ident ($($var_names:ident),*)
) => {
$crate::enum_derive_util! { @as_item $($pub_)* struct $itername(::std::option::Option<$name>); }
IterVariantNames! { @iter ($itername, $name), ($($var_names,)*) -> () () (0usize) }
$crate::enum_derive_util! {
@as_item
impl $name {
#[allow(dead_code)]
$($pub_)* fn iter_variant_names() -> $itername {
$itername(::std::option::Option::Some($crate::enum_derive_util!(@first_expr $($name::$var_names),+)))
}
}
}
};
(
@iter ($itername:ident, $name:ident), () -> ($($next_body:tt)*) ($($size_body:tt)*) ($($count:tt)*)
) => {
$crate::enum_derive_util! {
@as_item
impl ::std::iter::Iterator for $itername {
type Item = &'static str;
fn next(&mut self) -> ::std::option::Option<Self::Item> {
let (next_state, result) = match self.0 {
$($next_body)*
::std::option::Option::None => (::std::option::Option::None, ::std::option::Option::None)
};
self.0 = next_state;
result
}
fn size_hint(&self) -> (usize, ::std::option::Option<usize>) {
let variants = $($count)*;
let progress = match self.0 {
$($size_body)*
None => variants
};
(variants - progress, ::std::option::Option::Some(variants - progress))
}
}
impl ::std::iter::ExactSizeIterator for $itername { }
}
};
(
@iter ($itername:ident, $name:ident), ($a:ident, $b:ident, $($rest:tt)*) -> ($($next_body:tt)*) ($($size_body:tt)*) ($($count:tt)*)
) => {
IterVariantNames! {
@iter ($itername, $name), ($b, $($rest)*)
-> (
$($next_body)*
::std::option::Option::Some($name::$a)
=> (::std::option::Option::Some($name::$b), ::std::option::Option::Some(stringify!($a))),
)
(
$($size_body)*
::std::option::Option::Some($name::$a) => $($count)*,
)
($($count)* + 1usize)
}
};
(
@iter ($itername:ident, $name:ident), ($a:ident,) -> ($($next_body:tt)*) ($($size_body:tt)*) ($($count:tt)*)
) => {
IterVariantNames! {
@iter ($itername, $name), ()
-> (
$($next_body)*
::std::option::Option::Some($name::$a)
=> (::std::option::Option::None, ::std::option::Option::Some(stringify!($a))),
)
(
$($size_body)*
::std::option::Option::Some($name::$a) => $($count)*,
)
($($count)* + 1usize)
}
};
(($itername:ident) pub enum $name:ident { $($body:tt)* }) => {
$crate::enum_derive_util! {
@collect_unitary_variants
(IterVariantNames { @expand (pub) $itername, $name }),
($($body)*,) -> ()
}
};
(($itername:ident) enum $name:ident { $($body:tt)* }) => {
$crate::enum_derive_util! {
@collect_unitary_variants
(IterVariantNames { @expand () $itername, $name }),
($($body)*,) -> ()
}
};
}
#[macro_export]
macro_rules! NextVariant {
(
@expand ($($pub_:tt)*) $name:ident ()
) => {
$crate::enum_derive_util! {
@as_item
impl $name {
#[allow(dead_code)]
$($pub_)* fn next_variant(&self) -> ::std::option::Option<$name> {
loop {}
}
}
}
};
(
@expand ($($pub_:tt)*) $name:ident ($($var_names:ident),*)
) => {
$crate::enum_derive_util! {
@as_item
impl $name {
#[allow(dead_code)]
$($pub_)* fn next_variant(&self) -> ::std::option::Option<$name> {
NextVariant!(@arms ($name, self), ($($var_names)*) -> ())
}
}
}
};
(
@arms ($name:ident, $self_:expr), ($a:ident) -> ($($body:tt)*)
) => {
$crate::enum_derive_util! {
@as_expr
match *$self_ {
$($body)*
$name::$a => ::std::option::Option::None
}
}
};
(
@arms ($name:ident, $self_:expr), ($a:ident $b:ident $($rest:tt)*) -> ($($body:tt)*)
) => {
NextVariant! {
@arms ($name, $self_), ($b $($rest)*)
-> (
$($body)*
$name::$a => ::std::option::Option::Some($name::$b),
)
}
};
(() pub enum $name:ident { $($body:tt)* }) => {
$crate::enum_derive_util! {
@collect_unitary_variants
(NextVariant { @expand (pub) $name }),
($($body)*,) -> ()
}
};
(() enum $name:ident { $($body:tt)* }) => {
$crate::enum_derive_util! {
@collect_unitary_variants
(NextVariant { @expand () $name }),
($($body)*,) -> ()
}
};
}
#[macro_export]
macro_rules! PrevVariant {
(
@expand ($($pub_:tt)*) $name:ident ()
) => {
$crate::enum_derive_util! {
@as_item
impl $name {
#[allow(dead_code)]
$($pub_)* fn prev_variant(&self) -> ::std::option::Option<$name> {
loop {}
}
}
}
};
(
@expand ($($pub_:tt)*) $name:ident ($($var_names:ident),*)
) => {
$crate::enum_derive_util! {
@as_item
impl $name {
#[allow(dead_code)]
$($pub_)* fn prev_variant(&self) -> ::std::option::Option<$name> {
PrevVariant!(@arms ($name, self), (::std::option::Option::None, $($var_names)*) -> ())
}
}
}
};
(
@arms ($name:ident, $self_:expr), ($prev:expr, $a:ident) -> ($($body:tt)*)
) => {
$crate::enum_derive_util! {
@as_expr
match *$self_ {
$($body)*
$name::$a => $prev
}
}
};
(
@arms ($name:ident, $self_:expr), ($prev:expr, $a:ident $($rest:tt)*) -> ($($body:tt)*)
) => {
PrevVariant! {
@arms ($name, $self_), (::std::option::Option::Some($name::$a), $($rest)*)
-> (
$($body)*
$name::$a => $prev,
)
}
};
(() pub enum $name:ident { $($body:tt)* }) => {
$crate::enum_derive_util! {
@collect_unitary_variants
(PrevVariant { @expand (pub) $name }),
($($body)*,) -> ()
}
};
(() enum $name:ident { $($body:tt)* }) => {
$crate::enum_derive_util! {
@collect_unitary_variants
(PrevVariant { @expand () $name }),
($($body)*,) -> ()
}
};
}
#[macro_export]
macro_rules! EnumDisplay {
(
@expand $name:ident ()
) => {
$crate::enum_derive_util! {
@as_item
impl ::std::fmt::Display for $name {
fn fmt(&self, _: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
loop {}
}
}
}
};
(
@expand $name:ident ($($var_names:ident),*)
) => {
$crate::enum_derive_util! {
@as_item
impl ::std::fmt::Display for $name {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
EnumDisplay!(@arms ($name, self, f), ($($var_names)*) -> ())
}
}
}
};
(
@arms ($name:ident, $self_:expr, $f:ident), ($a:ident) -> ($($body:tt)*)
) => {
$crate::enum_derive_util! {
@as_expr
match *$self_ {
$($body)*
$name::$a => write!($f, stringify!($a)),
}
}
};
(
@arms ($name:ident, $self_:expr, $f:ident), ($a:ident $b:ident $($rest:tt)*) -> ($($body:tt)*)
) => {
EnumDisplay! {
@arms ($name, $self_, $f), ($b $($rest)*)
-> (
$($body)*
$name::$a => write!($f, stringify!($a)),
)
}
};
(() pub enum $name:ident { $($body:tt)* }) => {
$crate::enum_derive_util! {
@collect_unitary_variants
(EnumDisplay { @expand $name }),
($($body)*,) -> ()
}
};
(() enum $name:ident { $($body:tt)* }) => {
$crate::enum_derive_util! {
@collect_unitary_variants
(EnumDisplay { @expand $name }),
($($body)*,) -> ()
}
};
}
#[macro_export]
macro_rules! EnumFromStr {
(
@expand ($($pub_:tt)*) $name:ident ()
) => {
$crate::enum_derive_util! {
@as_item
impl ::std::str::FromStr for $name {
type Err = $crate::ParseEnumError;
fn from_str(_: &str) -> ::std::result::Result<Self, Self::Err> {
Err($crate::ParseEnumError)
}
}
}
};
(
@expand ($($pub_:tt)*) $name:ident ($($var_names:ident),*)
) => {
$crate::enum_derive_util! {
@as_item
impl ::std::str::FromStr for $name {
type Err = $crate::ParseEnumError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
EnumFromStr!(@arms ($name, s), ($($var_names)*) -> ())
}
}
}
};
(
@arms ($name:ident, $s:ident), ($a:ident) -> ($($body:tt)*)
) => {
$crate::enum_derive_util! {
@as_expr
match $s {
$($body)*
stringify!($a) => ::std::result::Result::Ok($name::$a),
_ => ::std::result::Result::Err($crate::ParseEnumError)
}
}
};
(
@arms ($name:ident, $s:ident), ($a:ident $b:ident $($rest:tt)*) -> ($($body:tt)*)
) => {
EnumFromStr! {
@arms ($name, $s), ($b $($rest)*)
-> (
$($body)*
stringify!($a) => ::std::result::Result::Ok($name::$a),
)
}
};
(() pub enum $name:ident { $($body:tt)* }) => {
$crate::enum_derive_util! {
@collect_unitary_variants
(EnumFromStr { @expand (pub) $name }),
($($body)*,) -> ()
}
};
(() enum $name:ident { $($body:tt)* }) => {
$crate::enum_derive_util! {
@collect_unitary_variants
(EnumFromStr { @expand () $name }),
($($body)*,) -> ()
}
};
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ParseEnumError;
impl fmt::Display for ParseEnumError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "provided string did not match any enum variant")
}
}
#[cfg(feature = "std")]
impl ::std::error::Error for ParseEnumError {
fn description(&self) -> &str {
"provided string did not match any enum variant"
}
}
#[macro_export]
macro_rules! EnumFromInner {
(
@expand $name:ident ($($var_names:ident($var_tys:ty),)*)
) => {
$(
impl ::std::convert::From<$var_tys> for $name {
fn from(v: $var_tys) -> $name {
$name::$var_names(v)
}
}
)*
};
(() $(pub)* enum $name:ident { $($body:tt)* }) => {
$crate::enum_derive_util! {
@collect_unary_variants
(EnumFromInner { @expand $name }),
($($body)*,) -> ()
}
};
}
#[macro_export]
macro_rules! EnumInnerAsTrait {
(
@expand (pub $fn_name:ident -> &mut $tr:ty), $($tail:tt)*
) => {
EnumInnerAsTrait! { @expand_inner (pub), $fn_name, (mut), $tr, $($tail)* }
};
(
@expand (pub $fn_name:ident -> &$tr:ty), $($tail:tt)*
) => {
EnumInnerAsTrait! { @expand_inner (pub), $fn_name, (), $tr, $($tail)* }
};
(
@expand ($fn_name:ident -> &mut $tr:ty), $($tail:tt)*
) => {
EnumInnerAsTrait! { @expand_inner (), $fn_name, (mut), $tr, $($tail)* }
};
(
@expand ($fn_name:ident -> &$tr:ty), $($tail:tt)*
) => {
EnumInnerAsTrait! { @expand_inner (), $fn_name, (), $tr, $($tail)* }
};
(
@expand_inner
($($vis:tt)*), $fn_name:ident, (mut), $tr:ty,
$ty_name:ident,
($($var_names:ident($_var_tys:ty),)*)
) => {
$crate::enum_derive_util! {
@as_item
impl $ty_name {
$($vis)* fn $fn_name(&mut self) -> &mut $tr {
match *self {
$(
$ty_name::$var_names(ref mut v) => v as &mut $tr,
)*
}
}
}
}
};
(
@expand_inner
($($vis:tt)*), $fn_name:ident, (), $tr:ty,
$ty_name:ident,
($($var_names:ident($_var_tys:ty),)*)
) => {
$crate::enum_derive_util! {
@as_item
impl $ty_name {
$($vis)* fn $fn_name(&self) -> &$tr {
match *self {
$(
$ty_name::$var_names(ref v) => v as &$tr,
)*
}
}
}
}
};
($arg:tt $(pub)* enum $name:ident { $($body:tt)* }) => {
$crate::enum_derive_util! {
@collect_unary_variants
(EnumInnerAsTrait { @expand $arg, $name, }),
($($body)*,) -> ()
}
};
}