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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//! Macro for generating enums associated with values.
//! 
//! # Example
//! 
//! ```
//! use value_enum::value_enum;
//! 
//! value_enum!(
//!     #[derive(Clone, Copy, PartialEq, Eq, Debug)]
//!     enum Abc: char {
//!         A = 'a',
//!         B = 'b',
//!         C = 'c',
//!     }
//! );
//! 
//! assert_eq!(char::from(Abc::A), 'a');
//! assert_eq!(Abc::try_from('b'), Ok(Abc::B));
//! ```

/// Macro for generating enums associated with values.
/// 
/// # Example
/// 
/// ```
/// use value_enum::value_enum;
/// 
/// value_enum!(
///     #[derive(Clone, Copy, PartialEq, Eq, Debug)]
///     enum Abc: char {
///         A = 'a',
///         B = 'b',
///         C = 'c',
///     }
/// );
/// 
/// assert_eq!(char::from(Abc::A), 'a');
/// assert_eq!(Abc::try_from('b'), Ok(Abc::B));
/// ```
#[macro_export]
macro_rules! value_enum {
    (
        $(#[$attr:meta])*
        $vis:vis enum $name:ident: $type:ty {
            $($variant:ident = $value:expr),*
            $(,)?
        }
    ) => {
        $(#[$attr])*
        $vis enum $name {
            $($variant,)*
        }

        impl From<$name> for $type {
            fn from(val: $name) -> Self {
                match val {
                    $($name::$variant => $value,)*
                }
            }
        }

        impl TryFrom<$type> for $name {
            type Error = $type;

            #[allow(non_upper_case_globals)]
            fn try_from(val: $type) -> Result<Self, Self::Error> {
                $(
                    const $variant: $type = $value;
                )*
                match val {
                    $($variant => Ok($name::$variant),)*
                    _ => Err(val),
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    value_enum!(
        #[derive(Clone, Copy, PartialEq, Eq, Debug)]
        enum Abc: char {
            A = 'a',
            B = 'b',
            C = 'c',
        }
    );

    value_enum!(
        #[derive(Clone, Copy, PartialEq, Eq, Debug)]
        enum Str: (&'static str, &'static str) {
            Test = ("test", "qwerty"),
            Nya = ("nya", "nyan"),
        }
    );

    #[test]
    fn test_char() {
        assert_eq!(char::from(Abc::A), 'a');
        assert_eq!(Abc::try_from('a'), Ok(Abc::A));
        assert_eq!(char::from(Abc::B), 'b');
        assert_eq!(Abc::try_from('b'), Ok(Abc::B));
        assert_eq!(char::from(Abc::C), 'c');
        assert_eq!(Abc::try_from('c'), Ok(Abc::C));
        assert_eq!(Abc::try_from('d'), Err('d'));
    }

    #[test]
    fn test_str() {
        assert_eq!(<(&str, &str)>::from(Str::Test), ("test", "qwerty"));
        assert_eq!(Str::try_from(("test", "qwerty")), Ok(Str::Test));
        assert_eq!(<(&str, &str)>::from(Str::Nya), ("nya", "nyan"));
        assert_eq!(Str::try_from(("nya", "nyan")), Ok(Str::Nya));
        assert_eq!(Str::try_from(("wtf", "wtf")), Err(("wtf", "wtf")));
    }
}