Who the page can show. A closed set: the scene is an exhaustive match over it, so a mascot
cannot be added without art for every mood (a compile error, not a fallback at run time), and
a name from a visitor is parsed against it and nothing else.
The ginger cat. The default, and first in the picker.
8 Ginger,
The grey cat, drawn once in whiskers-art and shared with the app.
10 Grey,
Every mascot, in the order the picker lists them.
19 pub const ALL: &'static [Mascot] = &[Mascot::Ginger, Mascot::Grey, Mascot::Bunny];
The word the page and its signals use.
The word on the picker's button.
What it is, for the picture's description.
A name from outside, against the closed set. Anything else is None.
Like [Mascot::parse] for a value that may be missing or wrong, such as a query: the
default for anything that is not a mascot.
60#[cfg(test)] 61mod tests { 62 use super::*; 63 64 #[test] 65 fn ginger_is_the_default_and_listed_first_then_grey_then_bunny() { 66 assert_eq!(Mascot::DEFAULT, Mascot::Ginger); 67 assert_eq!(Mascot::ALL, &[Mascot::Ginger, Mascot::Grey, Mascot::Bunny]); 68 let labels: Vec<_> = Mascot::ALL.iter().map(|m| m.label()).collect(); 69 assert_eq!(labels, ["Ginger", "Grey", "Bunny"]); 70 } 71 72 #[test] 73 fn names_round_trip_and_nothing_else_parses() { 74 for m in Mascot::ALL { 75 assert_eq!(Mascot::parse(m.name()), Some(*m)); 76 } 77 for bad in ["", "Bunny", "BUNNY", "Ginger", " bunny", "bunny ", "rabbit", "cat", "zebra", "whiskers", "../etc", "bunny&x=1", "grey\n"] { 78 assert_eq!(Mascot::parse(bad), None, "{bad:?}"); 79 } 80 assert_eq!(Mascot::parse_or_default(Some("nonsense")), Mascot::Ginger); 81 assert_eq!(Mascot::parse_or_default(None), Mascot::Ginger); 82 assert_eq!(Mascot::parse_or_default(Some("bunny")), Mascot::Bunny); 83 assert_eq!(Mascot::parse_or_default(Some("grey")), Mascot::Grey); 84 assert_eq!(Mascot::parse_or_default(Some("ginger")), Mascot::Ginger); 85 } 86}