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>>,
9 cache_dirty: std::sync::atomic::AtomicBool,
10}
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) {
69 if !self.cache_dirty.load(std::sync::atomic::Ordering::Acquire) {
70 return;
71 }
72
73 if let Ok(mut cache) = self.available_cache.write() {
74 cache.clear();
75 for (index, cmd) in self.commands.iter().enumerate() {
76 if cmd.is_available() {
77 cache.push(index);
78 }
79 }
80 self.cache_dirty
81 .store(false, std::sync::atomic::Ordering::Release);
82 }
83 }
84
85 pub fn execute_sync(&self, command: &str, args: &[&str]) -> Option<Result<String>> {
86 self.find_command(command).map(|cmd| cmd.execute_sync(args))
87 }
88
89 pub async fn execute_async(&self, command: &str, args: &[&str]) -> Option<Result<String>> {
90 match self.find_command(command) {
91 Some(cmd) => Some(cmd.execute(args).await),
92 None => None,
93 }
94 }
95
96 pub fn list_commands(&self) -> Vec<(&str, &str)> {
97 self.update_available_cache_if_needed();
98
99 if let Ok(cache) = self.available_cache.read() {
100 cache
101 .iter()
102 .filter_map(|&index| {
103 self.commands
104 .get(index)
105 .map(|cmd| (cmd.name(), cmd.description()))
106 })
107 .collect()
108 } else {
109 self.commands
111 .iter()
112 .filter(|cmd| cmd.is_available())
113 .map(|cmd| (cmd.name(), cmd.description()))
114 .collect()
115 }
116 }
117
118 pub fn debug_info(&self) -> String {
119 format!(
120 "CommandRegistry: {} commands registered",
121 self.commands.len()
122 )
123 }
124
125 pub fn len(&self) -> usize {
126 self.commands.len()
127 }
128
129 pub fn is_empty(&self) -> bool {
130 self.commands.is_empty()
131 }
132}
133
134impl Default for CommandRegistry {
135 fn default() -> Self {
136 Self::new()
137 }
138}
139
140impl<T: Command> From<T> for Box<dyn Command> {
142 fn from(cmd: T) -> Self {
143 Box::new(cmd)
144 }
145}