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
use std;
use std::fmt;
use std::str::FromStr;

use enum_set::CLike;

pub trait ToStaticStr {
    fn to_static_str(&self) -> &'static str;
}

pub trait ListAll: Sized {
    fn list_all() -> &'static [Self];
}

macro_rules! static_str_enum {
    (
        $name:ident {
            $($item:ident => $str:ident),*
        }
    ) => {
        #[derive(Clone, Copy)]
        #[repr(u32)]
        pub enum $name {
            $($item),*
        }
        impl ToStaticStr for $name {
            fn to_static_str(&self) -> &'static str {
                match *self {
                    $($name::$item => stringify!($str)),*
                }
            }
        }
        impl fmt::Display for $name {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                f.write_str(self.to_static_str())
            }
        }
        impl ListAll for $name {
            fn list_all() -> &'static [Self] {
                static LIST: &'static [$name] = &[$($name::$item),+];
                &LIST
            }
        }
        impl FromStr for $name {
            type Err = ();
            fn from_str(s: &str) -> Result<Self, Self::Err> {
                match s {
                    $(stringify!($str) => Ok($name::$item)),*,
                    _ => Err(())
                }
            }
        }
        impl CLike for $name {
            fn to_u32(&self) -> u32 {
                *self as u32
            }
            unsafe fn from_u32(v: u32) -> $name {
                std::mem::transmute(v)
            }
        }
    }
}

static_str_enum! {
    Category {
        Anime => anime,
        Book => book,
        Music => music,
        Game => game,
        Real => real
    }
}
static_str_enum! {
    State {
        Wish => wish,
        Collect => collect,
        Do => do,
        OnHold => on_hold,
        Dropped => dropped
    }
}

pub type Id = u32;
pub type Rating = u8;
pub const MAX_RATING: Rating = 10;

pub struct Item {
    pub id: Id,
    pub title: String,
    pub rating: Option<Rating>,
    pub tags: Vec<String>,
}