Skip to main content

rush_sync_server/commands/help/
command.rs

1use crate::commands::command::Command;
2use crate::core::prelude::*;
3
4#[derive(Debug, Default)]
5pub struct HelpCommand;
6
7impl HelpCommand {
8    pub fn new() -> Self {
9        Self
10    }
11}
12
13impl Command for HelpCommand {
14    fn name(&self) -> &'static str {
15        "help"
16    }
17
18    fn description(&self) -> &'static str {
19        "Show all available commands"
20    }
21
22    fn matches(&self, command: &str) -> bool {
23        let cmd = command.trim().to_lowercase();
24        cmd == "help" || cmd == "?" || cmd == "commands" || cmd == "list-commands"
25    }
26
27    fn execute_sync(&self, args: &[&str]) -> Result<String> {
28        let handler = crate::commands::CommandHandler::new();
29
30        match args.first() {
31            Some(&"--simple" | &"-s") => Ok(self.create_simple_list(&handler)),
32            Some(&"--detailed" | &"-d") => Ok(self.create_detailed_list(&handler)),
33            None => Ok(self.create_formatted_list(&handler)),
34            Some(&command_name) => Ok(self.show_command_help(command_name, &handler)),
35        }
36    }
37
38    fn priority(&self) -> u8 {
39        95
40    }
41}
42
43impl HelpCommand {
44    /// Look up the localized description for a command, falling back to the original
45    fn get_localized_description(&self, command_name: &str, original_description: &str) -> String {
46        // Normalize command name for i18n key lookup
47        let normalized_name = command_name.replace("-", "_");
48        let description_key = format!("system.commands.{}.description", normalized_name);
49
50        if crate::i18n::has_translation(&description_key) {
51            get_command_translation(&description_key, &[])
52        } else {
53            log::debug!(
54                "No translation found for key '{}', using original description",
55                description_key
56            );
57            original_description.to_string()
58        }
59    }
60
61    /// Build the formatted default help list
62    fn create_formatted_list(&self, handler: &crate::commands::CommandHandler) -> String {
63        let commands = handler.list_commands();
64
65        if commands.is_empty() {
66            return get_command_translation("system.commands.help.no_commands", &[]);
67        }
68
69        let mut result = String::new();
70        result.push_str(&get_command_translation("system.commands.help.header", &[]));
71        result.push_str("\n\n");
72
73        let mut categorized = std::collections::BTreeMap::new();
74
75        for (name, original_description) in commands {
76            let category_key = self.determine_category(name);
77            let localized_description = self.get_localized_description(name, original_description);
78
79            categorized
80                .entry(category_key)
81                .or_insert_with(Vec::new)
82                .push((name, localized_description));
83        }
84
85        for (category_key, commands) in categorized {
86            let category_translation_key =
87                format!("system.commands.help.category.{}", category_key);
88
89            let category_name = if crate::i18n::has_translation(&category_translation_key) {
90                get_command_translation(&category_translation_key, &[])
91            } else {
92                log::debug!(
93                    "No translation for category '{}', using fallback",
94                    category_key
95                );
96                self.get_fallback_category_name(category_key)
97            };
98
99            result.push_str(&format!("{}:\n", category_name));
100
101            for (name, description) in commands {
102                result.push_str(&format!("  {:12} - {}\n", name, description));
103            }
104            result.push('\n');
105        }
106
107        result.push_str(&get_command_translation("system.commands.help.footer", &[]));
108        result
109    }
110
111    /// Determine the category for a command by name prefix
112    fn determine_category(&self, command_name: &str) -> &'static str {
113        match command_name {
114            name if name.starts_with("start")
115                || name.starts_with("stop")
116                || name.starts_with("restart") =>
117            {
118                "server_control"
119            }
120            name if name.starts_with("create") || name.starts_with("list") => "server_management",
121            name if name.starts_with("remote") || name.starts_with("sync") => "deployment",
122            name if name.starts_with("cleanup") || name.starts_with("recover") => "maintenance",
123            name if name.starts_with("theme")
124                || name.starts_with("lang")
125                || name.starts_with("log-level") =>
126            {
127                "configuration"
128            }
129            name if name.starts_with("help")
130                || name.starts_with("version")
131                || name.starts_with("history") =>
132            {
133                "information"
134            }
135            name if name.starts_with("exit") || name.starts_with("clear") => "system",
136            _ => "other",
137        }
138    }
139
140    /// Fallback category names when i18n key is missing
141    fn get_fallback_category_name(&self, category_key: &str) -> String {
142        match category_key {
143            "server_control" => "Server Control".to_string(),
144            "server_management" => "Server Management".to_string(),
145            "deployment" => "Deployment & Sync".to_string(),
146            "maintenance" => "Maintenance".to_string(),
147            "configuration" => "Configuration".to_string(),
148            "information" => "Information".to_string(),
149            "system" => "System".to_string(),
150            "other" => "Other".to_string(),
151            _ => category_key.to_string(),
152        }
153    }
154
155    /// Build a comma-separated simple command list
156    fn create_simple_list(&self, handler: &crate::commands::CommandHandler) -> String {
157        let commands = handler.list_commands();
158        let names: Vec<&str> = commands.iter().map(|(name, _)| *name).collect();
159        let names_str = names.join(", ");
160
161        get_command_translation("system.commands.help.simple_list", &[&names_str])
162    }
163
164    /// Build a detailed command list with labels and separators
165    fn create_detailed_list(&self, handler: &crate::commands::CommandHandler) -> String {
166        let commands = handler.list_commands();
167        let mut result = String::new();
168
169        result.push_str(&get_command_translation(
170            "system.commands.help.detailed_header",
171            &[],
172        ));
173        result.push('\n');
174        result.push_str(&get_command_translation(
175            "system.commands.help.detailed_separator",
176            &[],
177        ));
178        result.push_str("\n\n");
179
180        for (name, original_description) in commands {
181            let localized_description = self.get_localized_description(name, original_description);
182
183            let command_label = get_command_translation("system.commands.help.command_label", &[]);
184            let description_label =
185                get_command_translation("system.commands.help.description_label", &[]);
186            let usage_label = get_command_translation("system.commands.help.usage_label", &[]);
187            let separator = get_command_translation("system.commands.help.command_separator", &[]);
188
189            result.push_str(&format!("{} {}\n", command_label, name.to_uppercase()));
190            result.push_str(&format!(
191                "{} {}\n",
192                description_label, localized_description
193            ));
194            result.push_str(&format!("{} {} [options]\n", usage_label, name));
195            result.push_str(&format!("{}\n", separator));
196        }
197
198        result
199    }
200
201    /// Show help for a specific command
202    fn show_command_help(
203        &self,
204        command_name: &str,
205        handler: &crate::commands::CommandHandler,
206    ) -> String {
207        let commands = handler.list_commands();
208
209        for (name, original_description) in commands {
210            if name.eq_ignore_ascii_case(command_name) {
211                let localized_description =
212                    self.get_localized_description(name, original_description);
213
214                return get_command_translation(
215                    "system.commands.help.specific_help_template",
216                    &[name, &localized_description, name, name],
217                );
218            }
219        }
220
221        get_command_translation("system.commands.help.command_not_found", &[command_name])
222    }
223}