Skip to main content

rush_sync_server/commands/
handler.rs

1use super::registry::CommandRegistry;
2use crate::core::prelude::*;
3use crate::i18n;
4use std::sync::Arc;
5
6#[derive(Debug, Clone)]
7pub struct CommandResult {
8    pub message: String,
9    pub success: bool,
10    pub should_exit: bool,
11}
12
13pub struct CommandHandler {
14    registry: Arc<CommandRegistry>,
15}
16
17impl CommandHandler {
18    pub fn new() -> Self {
19        Self {
20            registry: Arc::new(crate::create_default_registry()),
21        }
22    }
23
24    pub fn with_registry(registry: CommandRegistry) -> Self {
25        Self {
26            registry: Arc::new(registry),
27        }
28    }
29
30    pub fn with_shared_registry(registry: Arc<CommandRegistry>) -> Self {
31        Self { registry }
32    }
33
34    pub fn handle_input(&self, input: &str) -> CommandResult {
35        self.process_input(input, false)
36    }
37
38    pub async fn handle_input_async(&self, input: &str) -> CommandResult {
39        self.process_input_async(input).await
40    }
41
42    pub fn add_command<T: crate::commands::command::Command>(&mut self, command: T) -> Result<()> {
43        if let Some(registry) = Arc::get_mut(&mut self.registry) {
44            registry.register(command);
45            Ok(())
46        } else {
47            // Registry is shared - must create a new one
48            log::warn!("Registry is shared, creating new instance with added command");
49
50            let mut new_registry = CommandRegistry::new();
51
52            let existing_commands = self.registry.list_commands();
53            for (name, _) in existing_commands {
54                // TODO: Commands are not cloneable - architectural limitation
55                log::warn!("Cannot copy existing command: {}", name);
56            }
57
58            new_registry.register(command);
59            self.registry = Arc::new(new_registry);
60
61            Err(AppError::Validation(
62                "Registry was shared, created new instance".to_string(),
63            ))
64        }
65    }
66
67    pub fn list_commands(&self) -> Vec<(&str, &str)> {
68        self.registry.list_commands()
69    }
70
71    pub fn debug_info(&self) -> String {
72        self.registry.debug_info()
73    }
74}
75
76impl CommandHandler {
77    fn validate_and_parse<'a>(
78        &self,
79        input: &'a str,
80    ) -> std::result::Result<ParsedInput<'a>, CommandResult> {
81        let input = input.trim();
82        if input.is_empty() {
83            return Err(CommandResult::empty());
84        }
85        if input.len() > 1000 {
86            log::warn!("Command input too long: {} chars", input.len());
87            return Err(CommandResult::error(
88                "Command input too long (max 1000 characters)",
89            ));
90        }
91        Ok(InputParser::parse(input))
92    }
93
94    fn process_input(&self, input: &str, _is_async: bool) -> CommandResult {
95        let parts = match self.validate_and_parse(input) {
96            Ok(p) => p,
97            Err(result) => return result,
98        };
99        log::debug!("Processing command: '{}'", parts.command);
100
101        match self.registry.execute_sync(parts.command, &parts.args) {
102            Some(result) => self.process_command_result(result),
103            None => self.create_unknown_command_result(input.trim()),
104        }
105    }
106
107    async fn process_input_async(&self, input: &str) -> CommandResult {
108        let parts = match self.validate_and_parse(input) {
109            Ok(p) => p,
110            Err(result) => return result,
111        };
112        log::debug!("Processing async command: '{}'", parts.command);
113
114        match self
115            .registry
116            .execute_async(parts.command, &parts.args)
117            .await
118        {
119            Some(result) => self.process_command_result(result),
120            None => self.create_unknown_command_result(input.trim()),
121        }
122    }
123
124    fn process_command_result(&self, result: Result<String>) -> CommandResult {
125        match result {
126            Ok(msg) => {
127                if log::log_enabled!(log::Level::Debug) {
128                    self.log_command_success(&msg);
129                }
130
131                CommandResult {
132                    message: msg.clone(),
133                    success: true,
134                    should_exit: ExitChecker::should_exit(&msg),
135                }
136            }
137            Err(e) => {
138                log::error!("Command execution failed: {}", e);
139                CommandResult::error(&e.to_string())
140            }
141        }
142    }
143
144    fn create_unknown_command_result(&self, input: &str) -> CommandResult {
145        log::warn!("Unknown command: '{}'", input);
146        CommandResult::error(&UnknownCommandCache::get_message(input))
147    }
148
149    fn log_command_success(&self, msg: &str) {
150        let char_count = msg.chars().count();
151
152        log::debug!("Command returned {} chars", char_count);
153
154        // Full output only for large outputs like mem info or JSON
155        if char_count > 500
156            && (msg.starts_with("MEMORY SNAPSHOT") || msg.trim_start().starts_with('{'))
157        {
158            log::debug!("FULL COMMAND OUTPUT:\n{}", msg);
159        }
160    }
161}
162
163struct InputParser;
164
165impl InputParser {
166    fn parse(input: &str) -> ParsedInput<'_> {
167        let parts: Vec<&str> = input.split_whitespace().collect();
168
169        if parts.is_empty() {
170            ParsedInput {
171                command: "",
172                args: Vec::new(),
173            }
174        } else {
175            ParsedInput {
176                command: parts[0],
177                args: parts[1..].to_vec(),
178            }
179        }
180    }
181}
182
183struct ParsedInput<'a> {
184    command: &'a str,
185    args: Vec<&'a str>,
186}
187
188// Static exit checker
189struct ExitChecker;
190
191impl ExitChecker {
192    fn should_exit(message: &str) -> bool {
193        use crate::core::constants::*;
194        [SIG_EXIT, SIG_CONFIRM_EXIT, SIG_RESTART, SIG_CONFIRM_RESTART]
195            .iter()
196            .any(|&prefix| message.starts_with(prefix))
197    }
198}
199
200use std::sync::OnceLock;
201
202struct UnknownCommandCache;
203
204impl UnknownCommandCache {
205    fn get_message(input: &str) -> String {
206        static TEMPLATE: OnceLock<String> = OnceLock::new();
207
208        let template = TEMPLATE
209            .get_or_init(|| i18n::get_command_translation("system.commands.unknown", &["%INPUT%"]));
210
211        template.replace("%INPUT%", input)
212    }
213}
214
215impl CommandResult {
216    pub fn empty() -> Self {
217        Self {
218            message: String::new(),
219            success: false,
220            should_exit: false,
221        }
222    }
223
224    pub fn success(message: String) -> Self {
225        Self {
226            message,
227            success: true,
228            should_exit: false,
229        }
230    }
231
232    pub fn error(message: &str) -> Self {
233        Self {
234            message: message.to_string(),
235            success: false,
236            should_exit: false,
237        }
238    }
239
240    pub fn exit(message: String) -> Self {
241        Self {
242            message,
243            success: true,
244            should_exit: true,
245        }
246    }
247
248    pub fn is_success(&self) -> bool {
249        self.success
250    }
251
252    pub fn is_error(&self) -> bool {
253        !self.success
254    }
255
256    pub fn has_message(&self) -> bool {
257        !self.message.is_empty()
258    }
259}
260
261// Clone is cheap: only clones the Arc
262impl Clone for CommandHandler {
263    fn clone(&self) -> Self {
264        Self {
265            registry: Arc::clone(&self.registry),
266        }
267    }
268}
269
270impl Default for CommandHandler {
271    fn default() -> Self {
272        Self::new()
273    }
274}
275
276impl CommandHandler {
277    pub fn create_shared() -> Arc<Self> {
278        Arc::new(Self::new())
279    }
280
281    pub fn with_shared_handler(handler: Arc<Self>) -> Self {
282        Self {
283            registry: Arc::clone(&handler.registry),
284        }
285    }
286}