#![allow(renamed_and_removed_lints, clippy::unknown_clippy_lints)]
#[allow(unknown_lints)] #[allow(unused_macro_rules)]
macro_rules! decl_keyword {
{ $(#[$meta:meta])* $v:vis
$name:ident { $( $($anno:ident)? $($s:literal)|+ => $i:ident),* $(,)? } } => {
#[derive(Copy,Clone,Eq,PartialEq,Debug,std::hash::Hash)]
#[allow(non_camel_case_types)]
$(#[$meta])*
#[allow(unknown_lints)]
#[allow(renamed_and_removed_lints, clippy::unknown_clippy_lints)]
#[allow(clippy::upper_case_acronyms)]
$v enum $name {
$( $i , )*
UNRECOGNIZED,
ANN_UNRECOGNIZED
}
impl $crate::parse::keyword::Keyword for $name {
fn idx(self) -> usize { self as usize }
fn n_vals() -> usize { ($name::ANN_UNRECOGNIZED as usize) + 1 }
fn unrecognized() -> Self { $name::UNRECOGNIZED }
fn ann_unrecognized() -> Self { $name::ANN_UNRECOGNIZED }
fn from_str(s : &str) -> Self {
const KEYWORD: phf::Map<&'static str, $name> = phf::phf_map! {
$( $( $s => $name::$i , )+ )*
};
match KEYWORD.get(s) {
Some(k) => *k,
None => if s.starts_with('@') {
$name::ANN_UNRECOGNIZED
} else {
$name::UNRECOGNIZED
}
}
}
fn from_idx(i : usize) -> Option<Self> {
static VALS: once_cell::sync::Lazy<Vec<$name>> =
once_cell::sync::Lazy::new(
|| vec![ $($name::$i , )*
$name::UNRECOGNIZED,
$name::ANN_UNRECOGNIZED ]);
VALS.get(i).copied()
}
fn to_str(self) -> &'static str {
use $name::*;
match self {
$( $i => decl_keyword![@impl join $($s),+], )*
UNRECOGNIZED => "<unrecognized>",
ANN_UNRECOGNIZED => "<unrecognized annotation>"
}
}
fn is_annotation(self) -> bool {
use $name::*;
match self {
$( $i => decl_keyword![@impl is_anno $($anno)? ], )*
UNRECOGNIZED => false,
ANN_UNRECOGNIZED => true,
}
}
}
};
[ @impl is_anno annotation ] => ( true );
[ @impl is_anno $x:ident ] => ( compile_error!("unrecognized keyword; not annotation") );
[ @impl is_anno ] => ( false );
[ @impl join $s:literal ] => ( $s );
[ @impl join $s:literal , $($ss:literal),+ ] => (
concat! { $s, "/", decl_keyword![@impl join $($ss),*] }
);
}
#[cfg(test)]
pub(crate) mod test {
#![allow(clippy::cognitive_complexity)]
decl_keyword! {
pub(crate) Fruit {
"apple" => APPLE,
"orange" => ORANGE,
"lemon" => LEMON,
"guava" => GUAVA,
"cherry" | "plum" => STONEFRUIT,
"banana" => BANANA,
annotation "@tasty" => ANN_TASTY,
}
}
#[test]
fn kwd() {
use crate::parse::keyword::Keyword;
use Fruit::*;
assert_eq!(Fruit::from_str("lemon"), LEMON);
assert_eq!(Fruit::from_str("cherry"), STONEFRUIT);
assert_eq!(Fruit::from_str("plum"), STONEFRUIT);
assert_eq!(Fruit::from_str("pear"), UNRECOGNIZED);
assert_eq!(Fruit::from_str("@tasty"), ANN_TASTY);
assert_eq!(Fruit::from_str("@tastier"), ANN_UNRECOGNIZED);
assert_eq!(APPLE.idx(), 0);
assert_eq!(ORANGE.idx(), 1);
assert_eq!(ANN_UNRECOGNIZED.idx(), 8);
assert_eq!(Fruit::n_vals(), 9);
assert_eq!(Fruit::from_idx(0), Some(APPLE));
assert_eq!(Fruit::from_idx(8), Some(ANN_UNRECOGNIZED));
assert_eq!(Fruit::from_idx(9), None);
assert_eq!(Fruit::idx_to_str(3), "guava");
assert_eq!(Fruit::idx_to_str(999), "<out of range>");
assert_eq!(APPLE.to_str(), "apple");
assert_eq!(GUAVA.to_str(), "guava");
assert_eq!(ANN_TASTY.to_str(), "@tasty");
assert_eq!(STONEFRUIT.to_str(), "cherry/plum");
assert_eq!(UNRECOGNIZED.to_str(), "<unrecognized>");
assert_eq!(ANN_UNRECOGNIZED.to_str(), "<unrecognized annotation>");
assert!(!GUAVA.is_annotation());
assert!(!STONEFRUIT.is_annotation());
assert!(!UNRECOGNIZED.is_annotation());
assert!(ANN_TASTY.is_annotation());
assert!(ANN_UNRECOGNIZED.is_annotation());
}
}