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
#[macro_export]
macro_rules! implement_annotatable_enum {
    ($name:ident {
        $($item: ident = $code: expr,)*
    }) => {
        impl $name {
            pub fn try_from(code: isize) -> ::std::option::Option<$name> {
                match code {
                    $($code => ::std::option::Option::Some($name::$item),)*
                    _ => ::std::option::Option::None
                }
            }
        }

        impl ::std::convert::Into<isize> for $name {
            fn into(self) -> isize {
                self as isize
            }
        }
    }
}

#[macro_export]
macro_rules! annotatable_enum {
    ($name:ident {
        $($item: ident = $code: expr,)*
    }) => {
        #[derive(Debug, Copy, Clone, PartialEq, Eq)]
        enum $name {
            $($item = $code,)*
        }

        implement_annotatable_enum!($name {
            $($item = $code,)*
        });
    };

    (pub $name:ident {
        $($item: ident = $code: expr,)*
    }) => {
        #[derive(Debug, Copy, Clone, PartialEq, Eq)]
        pub enum $name {
            $($item = $code,)*
        }

        implement_annotatable_enum!($name {
            $($item = $code,)*
        });

    };
}

#[cfg(test)]
mod tests {
    annotatable_enum!(Test {
        Value = 0,
        AnotherValue = 1,
    });

    #[test]
    fn it_creates_enum_from_valid_code() {
        assert_eq!(Test::try_from(0), Some(Test::Value));
        assert_eq!(Test::try_from(1), Some(Test::AnotherValue));
    }

    #[test]
    fn it_does_not_create_enum_from_invalid_code() {
        assert_eq!(Test::try_from(2), None);
    }

    #[test]
    fn it_converts_enum_into_isize() {
        let zero: isize = Test::Value.into();
        assert_eq!(zero, 0);
    }
}