use std::fmt::{Debug, Display};
use std::hash::Hash;
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct OutOfRangeError<T: Enumeration>(pub T::Index);
impl<T: Enumeration> Display for OutOfRangeError<T> {
#[inline(always)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Out of enumeration index range (0 to {}): {}",
T::VARIANT_COUNT,
self.0
)
}
}
impl<T: Enumeration + Debug> std::error::Error for OutOfRangeError<T> {}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct UnknownAssociatedValueError<'a, T: Enumeration>(pub &'a T::AssociatedValueType);
impl<'a, T: Enumeration> Display for UnknownAssociatedValueError<'a, T>
where
T::AssociatedValueType: Display,
{
#[inline(always)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Unknown associated value: {}", self.0,)
}
}
impl<'a, T: Enumeration> std::error::Error for UnknownAssociatedValueError<'a, T> where
T::AssociatedValueType: Display
{
}
pub trait Enumeration:
TryFrom<Self::Index, Error = OutOfRangeError<Self>>
+ Into<Self::Index>
+ Clone
+ Copy
+ Debug
+ Hash
+ PartialEq
+ Eq
+ PartialOrd
+ Ord
+ 'static
{
type Index: Debug + Display;
type AssociatedValueType: 'static + Debug;
const VARIANT_COUNT: Self::Index;
#[inline(always)]
fn count() -> Self::Index {
Self::VARIANT_COUNT
}
#[inline(always)]
fn len() -> usize
where
Self::Index: Into<usize>,
{
Self::count().into()
}
fn value(self) -> &'static Self::AssociatedValueType;
#[inline(always)]
fn value_copy(self) -> Self::AssociatedValueType
where
Self::AssociatedValueType: Copy,
{
*self.value()
}
#[inline(always)]
fn value_clone(self) -> Self::AssociatedValueType
where
Self::AssociatedValueType: Clone,
{
self.value().clone()
}
#[inline(always)]
fn variant(index: Self::Index) -> Result<Self, Self::Error> {
Self::try_from(index)
}
unsafe fn variant_unchecked(index: Self::Index) -> Self;
#[inline(always)]
fn to_index(self) -> Self::Index {
self.into()
}
#[inline(always)]
fn to_usize(self) -> usize
where
Self::Index: Into<usize>,
{
self.to_index().into()
}
#[inline(always)]
fn iter() -> impl Iterator<Item = Self>
where
Self::Index: Into<usize> + From<usize>,
{
(0..Self::len()).map(|i| unsafe { Self::variant_unchecked(i.into()) })
}
#[inline(always)]
fn try_iter() -> impl Iterator<Item = Self>
where
Self::Index: Into<usize> + TryFrom<usize>,
{
(0..Self::len()).map(|i| unsafe {
Self::variant_unchecked(i.try_into().unwrap_or_else(|_| unreachable!()))
})
}
}
pub trait FromValue: Enumeration {
fn from_value<'a>(
value: &'a Self::AssociatedValueType,
) -> Result<Self, UnknownAssociatedValueError<'a, Self>>;
fn from_value_unchecked(value: &Self::AssociatedValueType) -> Self;
}
pub trait DefaultAssociatedValue: Enumeration {
const DEFAULT_ASSOCIATED_VALUE: Self::AssociatedValueType;
}
#[macro_export]
macro_rules! enumerate {
($(#[$enum_attr:meta])* $visibility:vis enum $name:ident ($t:ident) $($(#[$attr:meta])* $variant:ident $(,)? $(;)?)*) => {
$crate::enumerate!($(#[$enum_attr])* $visibility $name ($t; () = ()) $($(#[$attr])* $variant = ())*);
};
($(#[$enum_attr:meta])* $visibility:vis $name:ident ($t:ident) $($(#[$attr:meta])* $variant:ident $(,)? $(;)?)*) => {
$crate::enumerate!($(#[$enum_attr])* $visibility $name ($t; () = ()) $($(#[$attr])* $variant = ())*);
};
($(#[$enum_attr:meta])* $visibility:vis enum $name:ident ($t:ident; $associated_value_type:ty $(= $default_value:expr)?) $($(#[$attr:meta])* $variant:ident $(= $associated_value:expr)? $(,)? $(;)?)*) => {
$crate::enumerate!($(#[$enum_attr])* $visibility $name ($t; $associated_value_type $(= $default_value)?) $($(#[$attr])* $variant $(= $associated_value)?)*);
};
($(#[$enum_attr:meta])* $visibility:vis one-way enum $name:ident ($t:ident; $associated_value_type:ty $(= $default_value:expr)?) $($(#[$attr:meta])* $variant:ident $(= $associated_value:expr)? $(,)? $(;)?)*) => {
$crate::enumerate!($(#[$enum_attr])* $visibility one-way $name ($t; $associated_value_type $(= $default_value)?) $($(#[$attr])* $variant $(= $associated_value)?)*);
};
($(#[$enum_attr:meta])* $visibility:vis enum $name:ident ($t:ident; $value_alias:ident : $associated_value_type:ty $(= $default_value:expr)?) $($(#[$attr:meta])* $variant:ident $(= $associated_value:expr)? $(,)? $(;)?)*) => {
$crate::enumerate!($(#[$enum_attr])* $visibility $name ($t; $value_alias : $associated_value_type $(= $default_value)?) $($(#[$attr])* $variant $(= $associated_value)?)*);
};
($(#[$enum_attr:meta])* $visibility:vis one-way enum $name:ident ($t:ident; $value_alias:ident : $associated_value_type:ty $(= $default_value:expr)?) $($(#[$attr:meta])* $variant:ident $(= $associated_value:expr)? $(,)? $(;)?)*) => {
$crate::enumerate!($(#[$enum_attr])* $visibility one-way $name ($t; $value_alias : $associated_value_type $(= $default_value)?) $($(#[$attr])* $variant $(= $associated_value)?)*);
};
($(#[$enum_attr:meta])* $visibility:vis one-way $name:ident ($t:ident; $value_alias:ident : $associated_value_type:ty $(= $default_value:expr)?) $($(#[$attr:meta])* $variant:ident $(= $associated_value:expr)? $(,)? $(;)?)*) => {
$crate::enumerate!($(#[$enum_attr])* $visibility one-way $name ($t; $associated_value_type $(= $default_value)?) $($(#[$attr])* $variant $(= $associated_value)?)*);
impl $name where {
#[inline(always)]
fn $value_alias(self) -> &'static <Self as $crate::enumeration::Enumeration>::AssociatedValueType {
self.value()
}
}
};
($(#[$enum_attr:meta])* $visibility:vis one-way $name:ident ($t:ident; $associated_value_type:ty $(= $default_value:expr)?) $($(#[$attr:meta])* $variant:ident $(= $associated_value:expr)? $(,)? $(;)?)*) => {
$(#[$enum_attr])*
#[repr($t)]
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
$visibility enum $name {
$($(#[$attr])* $variant,)*
}
$crate::impl_default!($name $associated_value_type $(= $default_value)?);
#[allow(non_upper_case_globals)]
impl $crate::enumeration::Enumeration for $name {
type Index = $t;
type AssociatedValueType = $associated_value_type;
const VARIANT_COUNT: $t = $crate::count!($($variant)*);
#[inline(always)]
fn value(self) -> &'static Self::AssociatedValueType {
$crate::validate!($name $associated_value_type, $($default_value)?, $(($($attr)* : $variant : $($associated_value)?))*);
match self {
$(Self::$variant => $variant,)*
}
}
#[inline(always)]
unsafe fn variant_unchecked(index: Self::Index) -> Self {
debug_assert!({ #[allow(unused_comparisons)] let b = index >= 0 && index < Self::VARIANT_COUNT; b });
::std::mem::transmute(index)
}
}
impl ::std::borrow::Borrow<$associated_value_type> for $name {
#[inline(always)]
fn borrow(&self) -> &$associated_value_type {
use $crate::enumeration::Enumeration;
self.value()
}
}
$crate::impl_try_from_into!($t, $name);
};
($(#[$enum_attr:meta])* $visibility:vis $name:ident ($t:ident; $associated_value_type:ty $(= $default_value:expr)?) $($(#[$attr:meta])* $variant:ident $(= $associated_value:expr)? $(,)? $(;)?)*) => {
$crate::enumerate!($(#[$enum_attr])* $visibility one-way $name ($t; $associated_value_type $(= $default_value)?) $($(#[$attr])* $variant $(= $associated_value)?)*);
$crate::impl_from_value!($name: $associated_value_type $(= $default_value)?; $($(#[$attr])* $variant $(= $associated_value)?)*);
};
($(#[$enum_attr:meta])* $visibility:vis $name:ident ($t:ident; $value_alias:ident : $associated_value_type:ty $(= $default_value:expr)?) $($(#[$attr:meta])* $variant:ident $(= $associated_value:expr)? $(,)? $(;)?)*) => {
$crate::enumerate!($(#[$enum_attr])* $visibility one-way $name ($t; $value_alias : $associated_value_type $(= $default_value)?) $($(#[$attr])* $variant $(= $associated_value)?)*);
$crate::impl_from_value!($name: $associated_value_type $(= $default_value)?; $($(#[$attr])* $variant $(= $associated_value)?)*);
};
}