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}