#[macro_export]
macro_rules! plus_1 {
($t:tt) => { 1 };
($a:tt $($bs:tt)+) => {{
1 + $crate::plus_1!($($bs)+)
}
};
}
#[macro_export]
macro_rules! stringy {
(
$name:ident
=
$(
$label:ident $lit:literal
)+
) => {
$(#[doc = "1. `"]
#[doc = $lit]
#[doc = "`"])+
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum $name {
$(
#[doc = "Corresponds to the symbol `"]
#[doc = $lit]
#[doc = "`."]
#[allow(dead_code)]
$label,
)+
}
#[allow(unused)]
impl $name {
$(
#[doc = "1. `"]
#[doc = $lit]
#[doc = "`"]
#[doc = " "]
)+
#[inline]
pub fn from_str(s: &str) -> Option<Self> {
match s {
$($lit => {Some($name::$label)})+
_ => None
}
}
#[inline]
pub fn as_str(&self) -> &str {
match self {
$($name::$label => { $lit })+
}
}
#[inline]
pub fn as_usize(&self) -> usize {
let mut i = 0;
$(
if self == &Self::$label { return i; } else { i += 1; };
)+
i
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
(match self {
$($name::$label => { $lit })+
}).as_bytes()
}
pub fn array() -> [$name; $crate::plus_1!($($label)+)] {
[$($name::$label,)+]
}
#[inline]
pub fn is_any_of(&self, others: &[$name]) -> bool {
others.contains(self)
}
$(
#[doc = "`"]
#[doc = $lit]
#[doc = "`"]
)+
#[inline]
pub fn test_str(text: &str) -> bool {
match text {
$($lit => { true })+
_ => false
}
}
#[inline]
pub fn same_bytes(&self, bytes: &[u8]) -> bool {
self.as_bytes() == bytes
}
}
impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let s = match self {
$($name::$label => { $lit })+
};
write!(f, "{}", s)
}
}
impl<'t> From<$name> for std::borrow::Cow<'t, str> {
fn from(label: $name) -> std::borrow::Cow<'t, str> {
match label {
$($name::$label => {
std::borrow::Cow::from($lit)
})+
}
}
}
impl std::ops::Deref for $name {
type Target = str;
fn deref(&self) -> &Self::Target {
match self {
$($name::$label => { $lit })+
}
}
}
impl<'t> std::convert::TryFrom<&'t str> for $name {
type Error = &'t str;
fn try_from(value: &'t str) -> Result<Self, Self::Error> {
match value {
$($lit => { Ok($name::$label) })+
_ => { Err(value) }
}
}
}
impl std::convert::TryFrom<String> for $name {
type Error = String;
fn try_from(value: String) -> Result<Self, Self::Error> {
match value.as_str() {
$($lit => { Ok($name::$label) })+
_ => { Err(value) }
}
}
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
let _m = 0;
let _n = plus_1!(x y z u v w);
stringy! {
Color = Red "red" Blue "blue" Green "green"
}
Color::from_str("red");
println!(
"|Color| = {}\n|Color::Red| = {}\n|Color::Red.as_str()| = {}\n|Color::variants()| = {}",
std::mem::size_of::<Color>(),
std::mem::size_of_val(&Color::Red),
std::mem::size_of_val(Color::Red.as_str()),
std::mem::size_of_val(&Color::array())
);
println!("Color::Red.as_str() = {:?}", Color::Red)
}
#[test]
fn test_array() {
stringy! {
Color = Red "red" Green "green" Blue "blue"
}
let [r, g, b] = Color::array();
assert_eq!(r, Color::Red);
assert_eq!(g, Color::Green);
assert_eq!(b, Color::Blue);
}
#[test]
fn test_from_str() {
stringy! { Operator = Add "+" Sub "-" Mul "*" Div "/" }
assert_eq!(Operator::from_str("+"), Some(Operator::Add));
assert_eq!(Operator::from_str("-"), Some(Operator::Sub));
assert_eq!(Operator::from_str("*"), Some(Operator::Mul));
assert_eq!(Operator::from_str("/"), Some(Operator::Div));
}
#[test]
fn test_is_even() {
stringy! { Digit =
One "1"
Two "2"
Three "3"
Four "4"
Five "5"
Six "6"
Seven "7"
Eight "8"
Nine "9"
}
let evens = Digit::array()
.iter()
.map(|d| d.parse::<usize>().unwrap())
.filter(|n| n % 2 == 0)
.collect::<Vec<_>>();
assert_eq!(evens, vec![2, 4, 6, 8])
}
}