use std::{iter::Iterator, marker::PhantomData, fmt::{Display, Debug}};
pub struct EnumIterator<E: Enum> {
index: u8,
phantom: PhantomData<E>,
}
impl<E: Enum> Iterator for EnumIterator<E> {
type Item = E;
fn next(&mut self) -> Option<Self::Item> {
let ret = E::from_u8(self.index);
if ret.is_some() {
self.index += 1;
}
ret
}
fn size_hint(&self) -> (usize, Option<usize>) {
let size = (E::COUNT - self.index) as usize;
(size, Some(size))
}
}
pub trait Enum: Copy + Clone + Eq + PartialEq + Display + Debug {
const NAME: &'static str;
const COUNT: u8;
fn display_name(&self) -> &'static str;
fn identifier_name(&self) -> &'static str;
fn from_u8(byte: u8) -> Option<Self>;
fn into_u8(self) -> u8;
fn iter() -> EnumIterator<Self> {
EnumIterator::<Self> {
index: 0, phantom: PhantomData
}
}
}
macro_rules! count {
() => { 0 };
($first:tt $($rest:tt)*) => { 1 + count!($($rest)*) };
}
macro_rules! enumeration {
(
$name:ident,
$description:literal,
[$(($identifier_name:ident, $display_name:literal)),+$(,)?]
) => {
use crate::Enum;
#[doc = $description]
#[repr(u8)]
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum $name {
$($identifier_name),*
}
impl $name {
const DISPLAY_NAMES: [&'static str; Self::COUNT as usize] = [
$($display_name),*
];
const IDENTIFIER_NAMES: [&'static str; Self::COUNT as usize] = [
$(stringify!($identifier_name)),*
];
}
impl Enum for $name {
const NAME: &'static str = stringify!($name);
const COUNT: u8 = count!($($identifier_name)*);
fn display_name(&self) -> &'static str {
Self::DISPLAY_NAMES[*self as u8 as usize]
}
fn identifier_name(&self) -> &'static str {
Self::IDENTIFIER_NAMES[*self as u8 as usize]
}
fn from_u8(byte: u8) -> Option<Self> {
match byte {
$(b if b == Self::$identifier_name as u8 => Some(Self::$identifier_name)),*,
_ => None,
}
}
fn into_u8(self) -> u8 {
self as u8
}
}
impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.display_name())
}
}
impl std::fmt::Debug for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.identifier_name())
}
}
}
}