whiskers.git / crates / whiskers-art / src / character.rs
1//! Who there is: the three drawn characters (the grey cat, the ginger cat and the bunny) and the ten of the pack,
2//! as one closed list. The household's document, the chooser, the app and the site all read this list; none keeps
3//! its own. A character's scene in a mood is `scene`: drawn from this crate's data, or, for a pack character, a
4//! picture's place with the same shadow, body motion, mouth and weather round it.
5
6use whiskers_pet::Mood;
7
8use crate::node::Node;
9use crate::pack::{PACK, Pack};
10use crate::scene::{Mouth, Sleeps, assemble};
11
12/// One of the drawn characters.
13#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14pub enum Drawn {
15    Grey,
16    Ginger,
17    Bunny,
18}
19
20impl Drawn {
21    pub const ALL: [Drawn; 3] = [Drawn::Grey, Drawn::Ginger, Drawn::Bunny];
22
23    pub fn id(self) -> &'static str {
24        match self {
25            Drawn::Grey => "grey-cat",
26            Drawn::Ginger => "ginger-cat",
27            Drawn::Bunny => "bunny",
28        }
29    }
30
31    pub fn label(self) -> &'static str {
32        match self {
33            Drawn::Grey => "Grey",
34            Drawn::Ginger => "Ginger",
35            Drawn::Bunny => "Bunny",
36        }
37    }
38
39    pub fn kind(self) -> &'static str {
40        match self {
41            Drawn::Grey => "grey cat",
42            Drawn::Ginger => "ginger cat",
43            Drawn::Bunny => "rabbit",
44        }
45    }
46}
47
48/// Who Whiskers can be.
49#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
50pub enum Character {
51    Drawn(Drawn),
52    Pack(&'static Pack),
53}
54
55impl PartialEq for Pack {
56    fn eq(&self, other: &Pack) -> bool {
57        self.id == other.id
58    }
59}
60impl Eq for Pack {}
61impl std::hash::Hash for Pack {
62    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
63        self.id.hash(state);
64    }
65}
66
67impl Character {
68    /// Every character, in the order the chooser steps through them: the drawn ones, then the pack.
69    pub fn all() -> Vec<Character> {
70        Drawn::ALL.into_iter().map(Character::Drawn).chain(PACK.iter().map(Character::Pack)).collect()
71    }
72
73    /// The id the household's document stores, which the page's signals use too.
74    pub fn id(self) -> &'static str {
75        match self {
76            Character::Drawn(d) => d.id(),
77            Character::Pack(p) => p.id,
78        }
79    }
80
81    /// The word on the picker's button.
82    pub fn label(self) -> &'static str {
83        match self {
84            Character::Drawn(d) => d.label(),
85            Character::Pack(p) => p.label,
86        }
87    }
88
89    /// What it is, for the picture's description.
90    pub fn kind(self) -> &'static str {
91        match self {
92            Character::Drawn(d) => d.kind(),
93            Character::Pack(p) => p.kind,
94        }
95    }
96
97    /// The character called `id`, or none.
98    pub fn parse(id: &str) -> Option<Character> {
99        Character::all().into_iter().find(|c| c.id() == id)
100    }
101
102    /// The character in `mood`, whole: shadow, body, mouth and weather. `mouth` matters only while speaking.
103    pub fn scene(self, mood: Mood, mouth: Option<Mouth>) -> Vec<Node> {
104        match self {
105            Character::Drawn(Drawn::Grey) => crate::scene::scene(mood, mouth),
106            Character::Drawn(Drawn::Ginger) => {
107                let body = if mood == Mood::Sleeping { crate::ginger::sleeping() } else { crate::ginger::standing(mood, mouth) };
108                assemble(mood, mouth, body, Sleeps::Flat)
109            }
110            Character::Drawn(Drawn::Bunny) => crate::scene::bunny_scene(mood, mouth),
111            Character::Pack(p) => crate::scene::pack_scene(p, mood, mouth),
112        }
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119
120    #[test]
121    fn there_are_thirteen_with_their_own_ids_and_the_drawn_ones_come_first() {
122        let all = Character::all();
123        assert_eq!(all.len(), 13);
124        let mut ids: Vec<_> = all.iter().map(|c| c.id()).collect();
125        assert_eq!(&ids[..3], ["grey-cat", "ginger-cat", "bunny"]);
126        ids.sort();
127        ids.dedup();
128        assert_eq!(ids.len(), 13);
129        for c in all {
130            assert_eq!(Character::parse(c.id()), Some(c));
131        }
132        assert_eq!(Character::parse("grey"), None);
133    }
134}