slowshell_core/
commands.rs1use std::sync::{Mutex, OnceLock};
2
3use crate::{listeners::ListenerAction, types::Ustr};
4
5#[derive(Clone, Debug)]
6pub struct CommandEntry {
7 pub name: Ustr,
8 pub title: Option<String>,
9 pub description: Option<String>,
10 pub icon: Option<String>,
11 pub action: ListenerAction,
12}
13
14impl CommandEntry {
15 pub fn named(name: impl Into<Ustr>) -> Self {
16 let name = name.into();
17 Self {
18 action: ListenerAction::Named(name.clone()),
19 name,
20 title: None,
21 description: None,
22 icon: None,
23 }
24 }
25
26 pub fn payload(name: impl Into<Ustr>) -> Self {
27 let name = name.into();
28 Self {
29 action: ListenerAction::Payload {
30 name: name.clone(),
31 payload: None,
32 },
33 name,
34 title: None,
35 description: None,
36 icon: None,
37 }
38 }
39
40 pub fn title(mut self, title: impl Into<String>) -> Self {
41 self.title = Some(title.into());
42 self
43 }
44
45 pub fn description(mut self, description: impl Into<String>) -> Self {
46 self.description = Some(description.into());
47 self
48 }
49
50 pub fn icon(mut self, icon: impl Into<String>) -> Self {
51 self.icon = Some(icon.into());
52 self
53 }
54
55 pub fn action(mut self, action: ListenerAction) -> Self {
56 self.action = action;
57 self
58 }
59
60 pub fn display(&self) -> String {
61 self.title.clone().unwrap_or_else(|| self.name.to_string())
62 }
63}
64
65#[derive(Default)]
66pub struct CommandRegistry {
67 entries: Mutex<Vec<CommandEntry>>,
68}
69
70impl CommandRegistry {
71 pub fn register(&self, entry: CommandEntry) {
72 let mut entries = self.entries.lock().unwrap();
73
74 if let Some(existing) = entries
75 .iter_mut()
76 .find(|existing| existing.name == entry.name)
77 {
78 *existing = entry;
79 return;
80 }
81
82 entries.push(entry);
83 }
84
85 pub fn register_default(&self, entry: CommandEntry) {
86 let mut entries = self.entries.lock().unwrap();
87
88 if entries.iter().any(|existing| existing.name == entry.name) {
89 return;
90 }
91
92 entries.push(entry);
93 }
94
95 pub fn unregister(&self, name: &str) -> bool {
96 let mut entries = self.entries.lock().unwrap();
97 let before = entries.len();
98 entries.retain(|entry| &*entry.name != name);
99 entries.len() != before
100 }
101
102 pub fn get(&self, name: &str) -> Option<CommandEntry> {
103 self
104 .entries
105 .lock()
106 .unwrap()
107 .iter()
108 .find(|entry| &*entry.name == name)
109 .cloned()
110 }
111
112 pub fn contains(&self, name: &str) -> bool {
113 self
114 .entries
115 .lock()
116 .unwrap()
117 .iter()
118 .any(|entry| &*entry.name == name)
119 }
120
121 pub fn list(&self) -> Vec<CommandEntry> {
122 self.entries.lock().unwrap().clone()
123 }
124
125 pub fn len(&self) -> usize {
126 self.entries.lock().unwrap().len()
127 }
128
129 pub fn is_empty(&self) -> bool {
130 self.len() == 0
131 }
132}
133
134static REGISTRY: OnceLock<CommandRegistry> = OnceLock::new();
135
136pub fn commands() -> &'static CommandRegistry {
137 REGISTRY.get_or_init(CommandRegistry::default)
138}
139
140pub fn register(entry: CommandEntry) {
141 commands().register(entry);
142}
143
144pub fn register_default(entry: CommandEntry) {
145 commands().register_default(entry);
146}