Skip to main content

slowshell_core/
commands.rs

1use 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}