macro_rules! dataset_enum {
(
$(#[$meta:meta])*
pub enum $name:ident {
$($(#[$variant_meta:meta])* $variant:ident $(= $value:expr)? $(=> $label:literal)?),* $(,)?
}
) => {
$(#[$meta])*
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "strum", derive(strum_macros::EnumIter, strum_macros::EnumCount))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum $name {
$($(#[$variant_meta])* $variant $(= $value)?),*
}
impl $name {
pub const ALL: &'static [Self] = &[$(Self::$variant),*];
pub const COUNT: usize = Self::ALL.len();
#[must_use]
pub const fn label(self) -> &'static str {
match self {
$(Self::$variant => dataset_enum!(@label $variant $(, $label)?)),*
}
}
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
$(Self::$variant => stringify!($variant)),*
}
}
}
impl crate::Dataset for $name {
const INFO: crate::DatasetInfo = Self::INFO;
const ALL: &'static [Self] = Self::ALL;
const COUNT: usize = Self::COUNT;
fn as_str(self) -> &'static str { self.as_str() }
fn label(self) -> &'static str { self.label() }
}
#[cfg(feature = "rand")]
impl rand::distr::Distribution<$name> for rand::distr::StandardUniform {
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> $name {
$name::ALL[rng.random_range(0..$name::COUNT)]
}
}
impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.pad(self.as_str())
}
}
impl std::str::FromStr for $name {
type Err = crate::ParseEnumError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
$(if input.eq_ignore_ascii_case(stringify!($variant)) {
return Ok(Self::$variant);
})*
Err(crate::ParseEnumError::new(stringify!($name)))
}
}
};
(@label $variant:ident, $label:literal) => { $label };
(@label $variant:ident) => { stringify!($variant) };
}
macro_rules! numeric_enum {
($(#[$meta:meta])* pub enum $name:ident {
$($(#[$variant_meta:meta])* $variant:ident = $value:literal $(=> $label:literal)?),* $(,)?
}) => {
dataset_enum! {
$(#[$meta])* pub enum $name {
$($(#[$variant_meta])* $variant = $value $(=> $label)?),*
}
}
impl TryFrom<u8> for $name {
type Error = crate::EnumValueError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
$($value => Ok(Self::$variant),)*
_ => Err(crate::EnumValueError::new(stringify!($name), value)),
}
}
}
};
}
macro_rules! color_enum {
(
$(#[$meta:meta])*
pub enum $name:ident {
$($(#[$variant_meta:meta])* $variant:ident = $rgb:expr $(=> $label:literal)?),* $(,)?
}
) => {
dataset_enum! {
$(#[$meta])*
pub enum $name {
$($(#[$variant_meta])* $variant $(=> $label)?),*
}
}
impl $name {
#[must_use]
pub const fn rgb_channels(self) -> [u8; 3] {
let [_, red, green, blue] = self.rgb().to_be_bytes();
[red, green, blue]
}
#[must_use]
pub fn matching_rgb(rgb: u32) -> impl DoubleEndedIterator<Item = Self> {
Self::ALL.iter().copied().filter(move |color| color.rgb() == rgb)
}
#[must_use]
pub const fn rgb(self) -> u32 {
match self {
$(Self::$variant => $rgb),*
}
}
}
impl std::fmt::LowerHex for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.pad_integral(true, "0x", &format!("{:06x}", self.rgb()))
}
}
};
}
macro_rules! checked_u8_enum {
($name:ident, $method:ident) => {
impl TryFrom<u8> for $name {
type Error = crate::EnumValueError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
Self::ALL
.iter()
.copied()
.find(|item| item.$method() == value)
.ok_or_else(|| crate::EnumValueError::new(stringify!($name), value))
}
}
};
}
macro_rules! group_members {
($parent:ident, $method:ident, $child:ident, $relation:ident) => {
impl $parent {
#[must_use]
pub fn $method(self) -> impl DoubleEndedIterator<Item = $child> {
$child::ALL
.iter()
.copied()
.filter(move |member| member.$relation() == self)
}
}
};
}