holodeck_simctl_tui/state/
palette_command.rs1use holodeck_core::models::{Simulator, SimulatorState};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub enum PaletteCommand {
5 Appearance,
6 Boot,
7 Delete,
8 Erase,
9 Focus,
10 Inspect,
11 Launch,
12 New,
13 Open,
14 Privacy,
15 Record,
16 Screenshot,
17 Shutdown,
18}
19
20impl PaletteCommand {
21 const CASES: [PaletteCommand; 13] = [
22 PaletteCommand::Appearance,
23 PaletteCommand::Boot,
24 PaletteCommand::Delete,
25 PaletteCommand::Erase,
26 PaletteCommand::Focus,
27 PaletteCommand::Inspect,
28 PaletteCommand::Launch,
29 PaletteCommand::New,
30 PaletteCommand::Open,
31 PaletteCommand::Privacy,
32 PaletteCommand::Record,
33 PaletteCommand::Screenshot,
34 PaletteCommand::Shutdown,
35 ];
36
37 pub fn all() -> Vec<PaletteCommand> {
42 let mut all = Self::CASES.to_vec();
43 all.sort_by_key(|c| c.display_name());
44 all
45 }
46
47 pub fn display_name(self) -> &'static str {
48 match self {
49 PaletteCommand::Appearance => "appearance",
50 PaletteCommand::Boot => "boot",
51 PaletteCommand::Delete => "delete",
52 PaletteCommand::Erase => "erase",
53 PaletteCommand::Focus => "focus",
54 PaletteCommand::Inspect => "inspect",
55 PaletteCommand::Launch => "launch",
56 PaletteCommand::New => "new",
57 PaletteCommand::Open => "open",
58 PaletteCommand::Privacy => "privacy",
59 PaletteCommand::Record => "record",
60 PaletteCommand::Screenshot => "screenshot",
61 PaletteCommand::Shutdown => "shutdown",
62 }
63 }
64
65 pub fn description(self) -> &'static str {
66 match self {
67 PaletteCommand::Appearance => "Switch the booted simulator between light and dark",
68 PaletteCommand::Boot => "Boot the selected simulator",
69 PaletteCommand::Delete => "Delete the selected simulator",
70 PaletteCommand::Erase => "Erase the selected (shutdown) simulator",
71 PaletteCommand::Focus => "Bring Simulator.app to the front for the selection",
72 PaletteCommand::Inspect => "Open the inspector for the selected simulator",
73 PaletteCommand::Launch => "Launch an installed app on the booted simulator",
74 PaletteCommand::New => "Create a new simulator (wizard)",
75 PaletteCommand::Open => "Open a URL or deep link on the booted simulator",
76 PaletteCommand::Privacy => "Grant or revoke privacy permissions for an app",
77 PaletteCommand::Record => "Start screen recording on the booted simulator",
78 PaletteCommand::Screenshot => "Capture a screenshot of the booted simulator",
79 PaletteCommand::Shutdown => "Shut down the selected simulator",
80 }
81 }
82
83 pub fn is_applicable(self, simulator: Option<&Simulator>, is_recording: bool) -> bool {
84 match self {
85 PaletteCommand::New => true,
86 PaletteCommand::Focus | PaletteCommand::Inspect | PaletteCommand::Delete => simulator.is_some(),
87 PaletteCommand::Boot => simulator.is_some_and(|s| s.state == SimulatorState::Shutdown),
88 PaletteCommand::Shutdown => simulator.is_some_and(|s| s.state == SimulatorState::Booted),
89 PaletteCommand::Erase => simulator.is_some_and(|s| s.state == SimulatorState::Shutdown),
90 PaletteCommand::Record => simulator.is_some_and(|s| s.state == SimulatorState::Booted) && !is_recording,
91 PaletteCommand::Screenshot
92 | PaletteCommand::Appearance
93 | PaletteCommand::Open
94 | PaletteCommand::Privacy
95 | PaletteCommand::Launch => simulator.is_some_and(|s| s.state == SimulatorState::Booted),
96 }
97 }
98
99 pub fn matches(self, prefix: &str) -> bool {
101 if prefix.is_empty() {
102 return true;
103 }
104 self.display_name().to_lowercase().starts_with(&prefix.to_lowercase())
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn all_is_sorted_alphabetically_by_display_name() {
114 let names: Vec<&str> = PaletteCommand::all().into_iter().map(PaletteCommand::display_name).collect();
115 let mut sorted = names.clone();
116 sorted.sort();
117 assert_eq!(names, sorted);
118 }
119
120 #[test]
121 fn empty_prefix_matches_everything() {
122 assert!(PaletteCommand::Boot.matches(""));
123 }
124
125 #[test]
126 fn prefix_match_is_case_insensitive() {
127 assert!(PaletteCommand::Boot.matches("BO"));
128 assert!(!PaletteCommand::Boot.matches("xyz"));
129 }
130}