rush_sync_server/commands/
handler.rs

1// =====================================================
2// FILE: src/commands/handler.rs - PERFORMANCE & ROBUSTE VERSION
3// =====================================================
4
5use super::registry::CommandRegistry;
6use crate::core::prelude::*;
7use crate::i18n;
8use std::sync::Arc;
9
10#[derive(Debug, Clone)]
11pub struct CommandResult {
12    pub message: String,
13    pub success: bool,
14    pub should_exit: bool,
15}
16
17// ✅ PERFORMANCE: Arc für shared Registry - keine Kopien bei Clone
18pub struct CommandHandler {
19    registry: Arc<CommandRegistry>,
20}
21
22impl CommandHandler {
23    pub fn new() -> Self {
24        Self {
25            registry: Arc::new(crate::create_default_registry()),
26        }
27    }
28
29    pub fn with_registry(registry: CommandRegistry) -> Self {
30        Self {
31            registry: Arc::new(registry),
32        }
33    }
34
35    // ✅ PERFORMANCE: Shared registry reference
36    pub fn with_shared_registry(registry: Arc<CommandRegistry>) -> Self {
37        Self { registry }
38    }
39
40    // ✅ ROBUSTHEIT: Zentrale Input-Verarbeitung mit Error-Handling
41    pub fn handle_input(&self, input: &str) -> CommandResult {
42        self.process_input(input, false)
43    }
44
45    pub async fn handle_input_async(&self, input: &str) -> CommandResult {
46        self.process_input_async(input).await
47    }
48
49    pub fn add_command<T: crate::commands::command::Command>(&mut self, command: T) -> Result<()> {
50        if let Some(registry) = Arc::get_mut(&mut self.registry) {
51            registry.register(command);
52            Ok(())
53        } else {
54            // Registry ist geteilt - wir müssen eine neue erstellen
55            log::warn!("Registry is shared, creating new instance with added command");
56
57            let mut new_registry = CommandRegistry::new();
58
59            // Alle bestehenden Commands kopieren
60            let existing_commands = self.registry.list_commands();
61            for (name, _) in existing_commands {
62                // TODO: Commands können nicht geklont werden - das ist ein Architektur-Problem
63                log::warn!("Cannot copy existing command: {}", name);
64            }
65
66            // Neues Command hinzufügen
67            new_registry.register(command);
68            self.registry = Arc::new(new_registry);
69
70            Err(AppError::Validation(
71                "Registry was shared, created new instance".to_string(),
72            ))
73        }
74    }
75
76    pub fn list_commands(&self) -> Vec<(&str, &str)> {
77        self.registry.list_commands()
78    }
79
80    pub fn debug_info(&self) -> String {
81        self.registry.debug_info()
82    }
83}
84
85// ✅ PERFORMANCE & ROBUSTHEIT: Optimierte Core-Implementierung
86impl CommandHandler {
87    // ✅ SHARED: Gemeinsame Logik für sync/async
88    fn process_input(&self, input: &str, is_async: bool) -> CommandResult {
89        // ✅ PERFORMANCE: Early returns ohne Allokationen
90        let input = input.trim();
91        if input.is_empty() {
92            return CommandResult::empty();
93        }
94
95        // ✅ ROBUSTHEIT: Input-Validierung
96        if input.len() > 1000 {
97            log::warn!("Command input too long: {} chars", input.len());
98            return CommandResult::error("Command input too long (max 1000 characters)");
99        }
100
101        // ✅ PERFORMANCE: Single allocation für parts
102        let parts = InputParser::parse(input);
103
104        if is_async {
105            log::debug!("Processing async command: '{}'", parts.command);
106        } else {
107            log::debug!("Processing sync command: '{}'", parts.command);
108        }
109
110        // ✅ ROBUSTHEIT: Safe command execution
111        match self.registry.execute_sync(parts.command, &parts.args) {
112            Some(result) => self.process_command_result(result),
113            None => self.create_unknown_command_result(input),
114        }
115    }
116
117    // ✅ ASYNC: Separate async path
118    async fn process_input_async(&self, input: &str) -> CommandResult {
119        let input = input.trim();
120        if input.is_empty() {
121            return CommandResult::empty();
122        }
123
124        if input.len() > 1000 {
125            log::warn!("Async command input too long: {} chars", input.len());
126            return CommandResult::error("Command input too long (max 1000 characters)");
127        }
128
129        let parts = InputParser::parse(input);
130        log::debug!("Processing async command: '{}'", parts.command);
131
132        match self
133            .registry
134            .execute_async(parts.command, &parts.args)
135            .await
136        {
137            Some(result) => self.process_command_result(result),
138            None => self.create_unknown_command_result(input),
139        }
140    }
141
142    // ✅ PERFORMANCE: Optimierte Result-Verarbeitung
143    fn process_command_result(&self, result: Result<String>) -> CommandResult {
144        match result {
145            Ok(msg) => {
146                // ✅ PERFORMANCE: Conditional logging
147                if log::log_enabled!(log::Level::Debug) {
148                    self.log_command_success(&msg);
149                }
150
151                CommandResult {
152                    message: msg.clone(),
153                    success: true,
154                    should_exit: ExitChecker::should_exit(&msg),
155                }
156            }
157            Err(e) => {
158                log::error!("Command execution failed: {}", e);
159                CommandResult::error(&e.to_string())
160            }
161        }
162    }
163
164    // ✅ PERFORMANCE: Cached unknown command message
165    fn create_unknown_command_result(&self, input: &str) -> CommandResult {
166        log::warn!("Unknown command: '{}'", input);
167        CommandResult::error(&UnknownCommandCache::get_message(input))
168    }
169
170    // ✅ PERFORMANCE: Optimiertes Logging
171    fn log_command_success(&self, msg: &str) {
172        let char_count = msg.chars().count();
173
174        let preview = if char_count <= 100 {
175            msg
176        } else {
177            // ✅ PERFORMANCE: Lazy evaluation
178            &format!("{}...", msg.chars().take(97).collect::<String>())
179        };
180
181        log::debug!("Command returned {} chars: '{}'", char_count, preview);
182    }
183}
184
185// ✅ PERFORMANCE: Optimierte Input-Parsing
186struct InputParser;
187
188impl InputParser {
189    fn parse(input: &str) -> ParsedInput<'_> {
190        // Effizienter: Nur einmal splitten
191        let parts: Vec<&str> = input.split_whitespace().collect();
192
193        if parts.is_empty() {
194            ParsedInput {
195                command: "",
196                args: Vec::new(),
197            }
198        } else {
199            ParsedInput {
200                command: parts[0],
201                args: parts[1..].to_vec(),
202            }
203        }
204    }
205}
206
207struct ParsedInput<'a> {
208    command: &'a str,
209    args: Vec<&'a str>,
210}
211
212// ✅ PERFORMANCE: Static Exit-Checker ohne Allokationen
213struct ExitChecker;
214
215impl ExitChecker {
216    const EXIT_PREFIXES: &'static [&'static str] = &[
217        "__EXIT__",
218        "__CONFIRM_EXIT__",
219        "__RESTART__",
220        "__CONFIRM_RESTART__",
221    ];
222
223    fn should_exit(message: &str) -> bool {
224        Self::EXIT_PREFIXES
225            .iter()
226            .any(|&prefix| message.starts_with(prefix))
227    }
228}
229
230// ✅ PERFORMANCE: Cached i18n Messages
231use std::sync::OnceLock;
232
233struct UnknownCommandCache;
234
235impl UnknownCommandCache {
236    fn get_message(input: &str) -> String {
237        static TEMPLATE: OnceLock<String> = OnceLock::new();
238
239        let template = TEMPLATE
240            .get_or_init(|| i18n::get_command_translation("system.commands.unknown", &["%INPUT%"]));
241
242        template.replace("%INPUT%", input)
243    }
244}
245
246// ✅ ROBUSTHEIT: CommandResult Factory mit besseren Methoden
247impl CommandResult {
248    pub fn empty() -> Self {
249        Self {
250            message: String::new(),
251            success: false,
252            should_exit: false,
253        }
254    }
255
256    pub fn success(message: String) -> Self {
257        Self {
258            message,
259            success: true,
260            should_exit: false,
261        }
262    }
263
264    pub fn error(message: &str) -> Self {
265        Self {
266            message: message.to_string(),
267            success: false,
268            should_exit: false,
269        }
270    }
271
272    pub fn exit(message: String) -> Self {
273        Self {
274            message,
275            success: true,
276            should_exit: true,
277        }
278    }
279
280    // ✅ UTILITY: Convenience checks
281    pub fn is_success(&self) -> bool {
282        self.success
283    }
284
285    pub fn is_error(&self) -> bool {
286        !self.success
287    }
288
289    pub fn has_message(&self) -> bool {
290        !self.message.is_empty()
291    }
292}
293
294// ✅ PERFORMANCE: Clone für CommandHandler ist jetzt günstig (nur Arc clone)
295impl Clone for CommandHandler {
296    fn clone(&self) -> Self {
297        Self {
298            registry: Arc::clone(&self.registry),
299        }
300    }
301}
302
303impl Default for CommandHandler {
304    fn default() -> Self {
305        Self::new()
306    }
307}
308
309// ✅ ROBUSTHEIT: Thread-safe shared handler
310impl CommandHandler {
311    pub fn create_shared() -> Arc<Self> {
312        Arc::new(Self::new())
313    }
314
315    pub fn with_shared_handler(handler: Arc<Self>) -> Self {
316        Self {
317            registry: Arc::clone(&handler.registry),
318        }
319    }
320}