rush_sync_server/commands/
registry.rs1use super::command::Command;
2use crate::core::prelude::*;
3use std::collections::HashMap;
4
5pub struct CommandRegistry {
6 commands: Vec<Box<dyn Command>>,
7 name_map: HashMap<String, usize>,
8 available_cache: std::sync::RwLock<Vec<usize>>, cache_dirty: std::sync::atomic::AtomicBool, }
11
12impl CommandRegistry {
13 pub fn new() -> Self {
14 Self {
15 commands: Vec::new(),
16 name_map: HashMap::new(),
17 available_cache: std::sync::RwLock::new(Vec::new()),
18 cache_dirty: std::sync::atomic::AtomicBool::new(true),
19 }
20 }
21
22 pub fn register<T>(&mut self, command: T) -> &mut Self
23 where
24 T: Into<Box<dyn Command>>,
25 {
26 let boxed = command.into();
27 let name = boxed.name().to_lowercase();
28 let index = self.commands.len();
29
30 self.commands.push(boxed);
31 self.name_map.insert(name, index);
32
33 self.cache_dirty
35 .store(true, std::sync::atomic::Ordering::Release);
36
37 self
38 }
39
40 pub fn find_command(&self, input: &str) -> Option<&dyn Command> {
41 let input = input.trim().to_lowercase();
42
43 if let Some(&index) = self.name_map.get(&input) {
45 if let Some(cmd) = self.commands.get(index) {
46 if cmd.is_available() {
47 return Some(cmd.as_ref());
48 }
49 }
50 }
51
52 self.update_available_cache_if_needed();
54
55 if let Ok(cache) = self.available_cache.read() {
56 for &index in cache.iter() {
57 if let Some(cmd) = self.commands.get(index) {
58 if cmd.matches(&input) {
59 return Some(cmd.as_ref());
60 }
61 }
62 }
63 }
64
65 None
66 }
67
68 fn update_available_cache_if_needed(&self) {
70 if !self.cache_dirty.load(std::sync::atomic::Ordering::Acquire) {
71 return;
72 }
73
74 if let Ok(mut cache) = self.available_cache.write() {
75 cache.clear();
76 for (index, cmd) in self.commands.iter().enumerate() {
77 if cmd.is_available() {
78 cache.push(index);
79 }
80 }
81 self.cache_dirty
82 .store(false, std::sync::atomic::Ordering::Release);
83 }
84 }
85
86 pub fn execute_sync(&self, command: &str, args: &[&str]) -> Option<Result<String>> {
87 self.find_command(command).map(|cmd| cmd.execute_sync(args))
88 }
89
90 pub async fn execute_async(&self, command: &str, args: &[&str]) -> Option<Result<String>> {
92 match self.find_command(command) {
93 Some(cmd) => Some(cmd.execute(args).await),
94 None => None,
95 }
96 }
97
98 pub fn list_commands(&self) -> Vec<(&str, &str)> {
100 self.update_available_cache_if_needed();
101
102 if let Ok(cache) = self.available_cache.read() {
103 cache
104 .iter()
105 .filter_map(|&index| {
106 self.commands
107 .get(index)
108 .map(|cmd| (cmd.name(), cmd.description()))
109 })
110 .collect()
111 } else {
112 self.commands
114 .iter()
115 .filter(|cmd| cmd.is_available())
116 .map(|cmd| (cmd.name(), cmd.description()))
117 .collect()
118 }
119 }
120
121 pub fn debug_info(&self) -> String {
123 format!(
124 "CommandRegistry: {} commands registered",
125 self.commands.len()
126 )
127 }
128
129 pub fn len(&self) -> usize {
130 self.commands.len()
131 }
132
133 pub fn is_empty(&self) -> bool {
134 self.commands.is_empty()
135 }
136}
137
138impl Default for CommandRegistry {
139 fn default() -> Self {
140 Self::new()
141 }
142}
143
144impl<T: Command> From<T> for Box<dyn Command> {
146 fn from(cmd: T) -> Self {
147 Box::new(cmd)
148 }
149}