1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//! [`words!`]: an enum whose variants each have fixed words, declared as
//! one table so a variant and its words are written once.
/// Declares an enum from one table: a row per variant, a column per
/// `&'static str` function. It derives `Debug, Clone, Copy, PartialEq,
/// Eq` (more go above the enum) and gives `ALL`, in the rows' order. A
/// column written `fn slug -> fn from_slug` also gets its reverse
/// lookup, which finds the variant with that word.
///
/// ```text
/// words! {
/// #[derive(Hash)]
/// pub enum Since { pub fn slug -> pub fn from_slug, pub fn label }
/// Daily "daily" "Today";
/// Weekly "weekly" "This week";
/// }
/// ```
macro_rules! words {
(
$(#[$meta:meta])*
$vis:vis enum $Enum:ident { $($columns:tt)* }
$($(#[$vmeta:meta])* $V:ident $($word:literal)*;)*
) => {
$(#[$meta])*
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
$vis enum $Enum {
$($(#[$vmeta])* $V,)*
}
impl $Enum {
pub const ALL: [$Enum; [$(stringify!($V)),*].len()] = [$($Enum::$V),*];
words!(@columns [$($columns)*] [$($V [$($word)*])*]);
}
};
// Each column takes its rows' first words, and passes on the rest.
(@columns [] [$($rows:tt)*]) => {};
(
@columns
[$(#[$cmeta:meta])* $cvis:vis fn $column:ident $(-> $rvis:vis fn $reverse:ident)? $(, $($rest:tt)*)?]
[$($V:ident [$word:literal $($words:literal)*])*]
) => {
$(#[$cmeta])*
$cvis fn $column(self) -> &'static str {
match self {
$(Self::$V => $word,)*
}
}
$(
$rvis fn $reverse(word: &str) -> Option<Self> {
Self::ALL.into_iter().find(|v| v.$column() == word)
}
)?
words!(@columns [$($($rest)*)?] [$($V [$($words)*])*]);
};
}