#[macro_export]
macro_rules! enhanced_enum {
($name:ident $(,)? {$($variants:ident$(,)?)*}) => {
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum $name {
$( $variants ),*
}
impl $name {
pub const fn count() -> ::std::primitive::usize {$crate::count!($($variants)*)}
pub const fn len() -> ::std::primitive::usize { $name::count() }
pub const fn is_empty() -> ::std::primitive::bool { $name::len() == 0 }
pub fn iter() -> impl ::std::iter::Iterator<Item=Self> {
(0..Self::count()).map(|x| match x {
$( _ if x == Self::$variants as ::std::primitive::usize => Self::$variants, )*
_ => ::std::panic!()
})
}
pub fn to_str(&self) -> &'static ::std::primitive::str {
match self {
$( Self::$variants => stringify!($variants), )*
}
}
}
impl ::std::fmt::Display for $name {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl ::std::convert::TryFrom<::std::primitive::u32> for $name {
type Error = &'static str;
fn try_from(value: ::std::primitive::u32) -> ::std::result::Result<Self, Self::Error> {
if cfg!(debug_assertions) && ::std::primitive::u32::try_from(Self::count() - 1).is_err() {
::std::panic!("Too many enum variants to fit inside a u32!");
}
match value {
$( _ if value == Self::$variants as ::std::primitive::u32 => Ok(Self::$variants), )*
_ => Err("Bad enum discriminant!")
}
}
}
impl ::std::convert::TryFrom<::std::primitive::u64> for $name {
type Error = &'static str;
fn try_from(value: ::std::primitive::u64) -> ::std::result::Result<Self, Self::Error> {
let value = ::std::primitive::u32::try_from(value).unwrap();
Self::try_from(value)
}
}
impl ::std::convert::TryFrom<::std::primitive::usize> for $name {
type Error = &'static str;
fn try_from(value: ::std::primitive::usize) -> ::std::result::Result<Self, Self::Error> {
let value = ::std::primitive::u32::try_from(value).unwrap();
Self::try_from(value)
}
}
impl ::std::convert::TryFrom<&str> for $name {
type Error = &'static str;
fn try_from(value: &str) -> ::std::result::Result<Self, Self::Error> {
match value {
$( _ if value == Self::$variants.to_string() => Ok(Self::$variants), )*
_ => Err("Unrecognized variant name!")
}
}
}
$crate::pyo3_traits!($name, {$($variants,)*});
$crate::paste::paste! {
pub struct [<$name Array>]<T> {
data: [T; $name::count()]
}
impl<T> [<$name Array>]<T> {
pub fn new(initial_value: T) -> Self where T: Clone {
Self::new_with(|_| initial_value.clone())
}
pub fn new_with<F>(initial_value: F) -> Self
where F: Fn($name) -> T
{
use ::std::convert::TryFrom;
use ::std::mem::{MaybeUninit, forget, replace};
let mut data: [T; $name::count()] = unsafe {
MaybeUninit::uninit().assume_init()
};
for (idx, elem) in data.iter_mut().enumerate() {
forget(replace(elem, initial_value($name::try_from(idx).unwrap())));
}
return Self { data };
}
pub const fn len(&self) -> ::std::primitive::usize { $name::count() }
pub const fn is_empty(&self) -> ::std::primitive::bool { self.len() == 0 }
pub fn iter<'a>(&'a self) -> impl ::std::iter::Iterator<Item=&T> {
self.data.iter()
}
pub fn iter_mut<'a>(&'a mut self) -> impl ::std::iter::Iterator<Item=&mut T> {
self.data.iter_mut()
}
pub fn iter_enumerate<'a>(&'a self) -> impl ::std::iter::Iterator<Item=($name, &T)> {
use ::std::convert::TryFrom;
self.data.iter().enumerate().map(|(idx, v)| ($name::try_from(idx).unwrap(),v))
}
pub fn iter_mut_enumerate<'a>(&'a mut self) -> impl ::std::iter::Iterator<Item=($name, &mut T)> {
use ::std::convert::TryFrom;
self.data.iter_mut().enumerate().map(|(idx, v)| ($name::try_from(idx).unwrap(),v))
}
pub fn map<F, Q>(&self, f: F) -> [<$name Array>]<Q> where F: Fn(&T) -> Q {
[<$name Array>]::new_with(|x| f(&self[x]))
}
pub fn contains(&self, x: &T) -> ::std::primitive::bool where T: PartialEq<T> {
self.data.contains(x)
}
}
impl<T> ::std::fmt::Debug for [<$name Array>]<T> where T: ::std::fmt::Debug {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
self.data.fmt(f)
}
}
impl<T> ::std::ops::Index<$name> for [<$name Array>]<T> {
type Output = T;
fn index(&self, x: $name) -> &Self::Output {
&self.data[x as ::std::primitive::usize]
}
}
impl<T> ::std::ops::IndexMut<$name> for [<$name Array>]<T> {
fn index_mut(&mut self, x: $name) -> &mut Self::Output {
&mut self.data[x as ::std::primitive::usize]
}
}
impl<'a, T> ::std::iter::IntoIterator for &'a [<$name Array>]<T> {
type Item = &'a T;
type IntoIter = ::std::slice::Iter<'a, T>;
fn into_iter(self) -> ::std::slice::Iter<'a, T> {
self.data.iter()
}
}
impl<'a, T> ::std::iter::IntoIterator for &'a mut [<$name Array>]<T> {
type Item = &'a mut T;
type IntoIter = ::std::slice::IterMut<'a, T>;
fn into_iter(self) -> ::std::slice::IterMut<'a, T> {
self.data.iter_mut()
}
}
impl<T> Copy for [<$name Array>]<T> where T: Copy {}
impl<T> Clone for [<$name Array>]<T> where T: Clone {
fn clone(&self) -> Self {
Self { data: self.data.clone() }
}
}
impl<T> PartialEq for [<$name Array>]<T> where T: PartialEq {
fn eq(&self, other: &Self) -> bool {
self.data == other.data
}
}
impl<T> Eq for [<$name Array>]<T> where T: Eq {}
impl<T> PartialOrd for [<$name Array>]<T> where T: PartialOrd {
fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> {
self.data.partial_cmp(&other.data)
}
}
impl<T> Ord for [<$name Array>]<T> where T: Ord {
fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
self.data.cmp(&other.data)
}
}
impl<T> ::std::hash::Hash for [<$name Array>]<T> where T: ::std::hash::Hash {
fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
self.data.hash(state);
}
}
impl<T> ::std::default::Default for [<$name Array>]<T> where T: ::std::default::Default {
fn default() -> Self {
Self::new_with(|_| T::default())
}
}
}
}
}
#[doc(hidden)]
pub use paste;
#[doc(hidden)]
#[macro_export]
macro_rules! count {
() => (0_usize);
( $x:ident $($xs:ident)* ) => (1_usize + $crate::count!($($xs)*));
}
#[cfg(not(feature = "pyo3"))]
#[doc(hidden)]
#[macro_export]
macro_rules! pyo3_traits {
($name:ident $(,)? {$($variants:ident$(,)?)*}) => {};
}
#[cfg(feature = "pyo3")]
#[doc(hidden)]
#[macro_export]
macro_rules! pyo3_traits {
($name:ident $(,)? {$($variants:ident$(,)?)*}) => {
impl ::pyo3::conversion::FromPyObject<'_> for $name {
fn extract(obj: &::pyo3::PyAny) -> ::std::result::Result<Self, ::pyo3::PyErr> {
let string: ::std::string::String = obj.extract()?;
use std::convert::TryFrom;
return Ok($name::try_from(string.as_str()).map_err(|err| {
::pyo3::PyErr::new::<::pyo3::exceptions::PyTypeError, _>(err.to_string())
})?);
}
}
};
}