rush_sync_server/commands/
registry.rs

1use 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>>, // Neu: Cache für verfügbare Commands
9    cache_dirty: std::sync::atomic::AtomicBool,     // Neu: Cache-Status
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        // Cache invalidieren
34        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        // Exakte Übereinstimmung (schnellster Pfad)
44        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        // Cache-basierte Pattern-Matching
53        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    // Neue private Methode hinzufügen:
69    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    // src/commands/registry.rs
91    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    // ✅ OPTIMIERT: Iterator-Chain statt collect
99    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            // Fallback bei Lock-Fehler
113            self.commands
114                .iter()
115                .filter(|cmd| cmd.is_available())
116                .map(|cmd| (cmd.name(), cmd.description()))
117                .collect()
118        }
119    }
120
121    // ✅ VEREINFACHT: Weniger Felder zu debuggen
122    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
144// ✅ AUTO-IMPL: Ermöglicht register(MyCommand::new()) und register(Box::new(MyCommand::new()))
145impl<T: Command> From<T> for Box<dyn Command> {
146    fn from(cmd: T) -> Self {
147        Box::new(cmd)
148    }
149}