#![macro_use]
#[allow(unused_imports)]
pub use std::collections::HashMap;
pub use std::mem::discriminant;
pub use std::mem::Discriminant;
pub use std::sync::OnceLock;
pub trait Meta<R>
where
Self: Sized,
{
fn meta(&self) -> R;
fn all() -> Vec<Self>;
}
#[macro_export]
macro_rules! meta {
($enum_type:ident, $return_type:ty;
$($enum_variant:ident, $return_value:expr);*
) => {
impl Meta<$return_type> for $enum_type {
fn meta(&self) -> $return_type {
match self {
$(
$enum_type::$enum_variant => {
$return_value
}
)*
}
}
fn all() -> Vec<$enum_type>{
vec![
$(
$enum_type::$enum_variant
),*
]
}
}
};
($enum_type:ident, $return_type:ty;
$($enum_variant:ident, $return_value:expr);+ ;
) => {
meta!{
$enum_type, $return_type;
$( $enum_variant, $return_value );*
}
};
}
#[macro_export]
macro_rules! lazy_meta {
($enum_type:ident, $return_type:ty, $storage:ident;
$($enum_variant:ident, $return_expr:expr);*
) => {
impl $enum_type {
fn storage() -> &'static HashMap<Discriminant<$enum_type>,$return_type> {
static $storage: OnceLock<HashMap<Discriminant<$enum_type>,$return_type>> = OnceLock::new();
$storage.get_or_init(|| {
let mut hm = HashMap::new();
$(
hm.insert(discriminant(&$enum_type::$enum_variant),$return_expr);
)*
hm
})
}
}
impl<'a> Meta<&'a $return_type> for $enum_type {
fn meta(&self) -> &'a $return_type {
Self::storage().get(&discriminant(&self)).unwrap()
}
fn all() -> Vec<$enum_type>{
vec![
$(
$enum_type::$enum_variant
),*
]
}
}
impl $enum_type {
#[allow(dead_code)]
fn meta_check(&self) {
match self {
$(
$enum_type::$enum_variant => {}
),*
}
}
}
};
($enum_type:ident, $return_type:ty, $storage:ident;
$($enum_variant:ident, $return_expr:expr);+ ;
) => {
lazy_meta!{
$enum_type, $return_type, $storage;
$( $enum_variant, $return_expr );*
}
};
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_meta() {
enum Colour {
Red,
Orange,
Green,
}
meta! {
Colour, &'static str;
Red, "Red";
Orange, "Orange";
Green, "Green"
}
assert_eq!(Colour::Red.meta(), "Red");
assert_eq!(Colour::Orange.meta(), "Orange");
assert_eq!(Colour::Green.meta(), "Green");
}
#[test]
fn test_all() {
#[derive(Debug, Eq, PartialEq)]
enum Colour {
Red,
Orange,
Green,
}
meta! {
Colour, &'static str;
Red, "Red";
Orange, "Orange";
Green, "Green"
}
assert_eq!(
vec![Colour::Red, Colour::Orange, Colour::Green],
Colour::all()
);
}
#[test]
fn test_meta_complex_return_type() {
enum Colour {
Red,
Orange,
Green,
}
meta! {
Colour, (&'static str, i64);
Red, ("Red", 10);
Orange, ("Orange", 11);
Green, ("Green", 12)
}
assert_eq!(Colour::Red.meta(), ("Red", 10));
assert_eq!(Colour::Orange.meta(), ("Orange", 11));
assert_eq!(Colour::Green.meta(), ("Green", 12));
}
#[test]
fn test_meta_trailing_semi() {
enum Colour {
Red,
Orange,
Green,
}
meta! {
Colour, &'static str;
Red, "Red";
Orange, "Orange";
Green, "Green";
}
assert_eq!(Colour::Red.meta(), "Red");
assert_eq!(Colour::Orange.meta(), "Orange");
assert_eq!(Colour::Green.meta(), "Green");
}
#[test]
fn test_lazy_meta() {
enum Colour {
Red,
Orange,
Green,
}
lazy_meta! {
Colour, String, TEST1;
Red, "Red".to_string();
Orange, "Orange".to_string();
Green, "Green".to_string();
}
assert_eq!(Colour::Red.meta(), "Red");
assert_eq!(Colour::Orange.meta(), "Orange");
assert_eq!(Colour::Green.meta(), "Green");
}
#[test]
fn test_lazy_all() {
#[derive(Debug, Eq, PartialEq)]
enum Colour {
Red,
Orange,
Green,
}
lazy_meta! {
Colour, String, TEST1;
Red, "Red".to_string();
Orange, "Orange".to_string();
Green, "Green".to_string();
}
assert_eq!(
Colour::all(),
vec![Colour::Red, Colour::Orange, Colour::Green]
);
}
#[test]
fn test_meta_access() {
use crate::test::test_meta::Number;
assert_eq!(Number::One.meta(), "one");
}
#[test]
fn test_lazy_meta_access() {
use crate::test::test_lazy_meta::Number;
assert_eq!(Number::One.meta(), &"one");
}
#[cfg(test)]
mod test_meta {
use crate::*;
pub enum Number {
One, Two, Three
}
meta! {
Number, &'static str;
One, "one";
Two, "two";
Three, "three";
}
pub enum Alphabet {
A, B, C
}
meta! {
Alphabet, &'static str;
A, "A";
B, "B";
C, "C";
}
}
mod test_lazy_meta {
use crate::*;
pub enum Number {
One, Two, Three
}
lazy_meta! {
Number, &'static str, META_NUMBER;
One, "one";
Two, "two";
Three, "three";
}
pub enum Alphabet {
A, B, C
}
lazy_meta! {
Alphabet, &'static str, META_ALPHABET;
A, "A";
B, "B";
C, "C";
}
}
}