rush_sync_server/commands/help/
command.rs1use 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 fn get_command_usage(command_name: &str) -> Option<&'static str> {
46 match command_name {
47 "create" => Some(
48 " create Create server with auto name/port\n \
49 create <name> Create server with custom name\n \
50 create <name> <port> Create with name and port\n \
51 create <count> Bulk create (1-100 servers)\n \
52 create <name> <port> <n> Bulk with base name, port, count\n\n \
53 Examples:\n \
54 create -> rss-001 on next free port\n \
55 create mysite -> mysite on next free port\n \
56 create mysite 8080 -> mysite on port 8080\n \
57 create 50 -> 50 servers (rss-001..rss-050)\n \
58 create web 8001 10 -> web-001:8001 .. web-010:8010",
59 ),
60 "start" => Some(
61 " start <id|name|number> Start a single server\n \
62 start <start>-<end> Start range of servers\n \
63 start all Start all stopped servers\n \
64 --workers N, -w N Set workers per server (1-16)\n\n \
65 Note: 'start all' and ranges skip browser opening.\n \
66 Bulk operations show time + memory benchmarks.\n\n \
67 Examples:\n \
68 start rss-001 -> start by name\n \
69 start 1 -> start server #1\n \
70 start 1-10 -> start servers 1 through 10\n \
71 start all -> start all stopped servers\n \
72 start rss-001 --workers 3 -> start with 3 workers\n \
73 start all -w 2 -> start all with 2 workers each",
74 ),
75 "stop" => Some(
76 " stop <id|name|number> Stop a single server\n \
77 stop <start>-<end> Stop range of servers\n \
78 stop all Stop all running servers\n\n \
79 Examples:\n \
80 stop rss-001 -> stop by name\n \
81 stop 1 -> stop server #1\n \
82 stop 1-5 -> stop servers 1 through 5\n \
83 stop all -> stop all running servers",
84 ),
85 "list" => Some(
86 " list Show all servers (sorted by port)\n\n \
87 Filter:\n \
88 list running Only running servers\n \
89 list stopped Only stopped servers\n \
90 list failed Only failed servers\n\n \
91 Sort:\n \
92 list -port asc By port ascending (default)\n \
93 list -port desc By port descending\n \
94 list -name asc By name A-Z\n \
95 list -name desc By name Z-A\n\n \
96 Special:\n \
97 list memory Disk + RAM usage per server\n\n \
98 Combine: list running -name asc\n \
99 Aliases: list servers, list server",
100 ),
101 "restart" => Some(
102 " restart Restart application (with confirm)\n \
103 restart -f, --force Force restart without confirm\n \
104 restart -h, --help Show help\n\n \
105 Aliases: reboot, reset",
106 ),
107 "cleanup" => Some(
108 " cleanup Clean stopped servers (confirm)\n \
109 cleanup stopped Clean stopped servers\n \
110 cleanup failed Clean failed servers\n \
111 cleanup logs Clean all log files\n \
112 cleanup www Clean all www directories\n \
113 cleanup www <name> Clean specific server www\n \
114 cleanup all Clean everything\n \
115 cleanup --force-stopped Skip confirmation\n \
116 cleanup --force-failed Skip confirmation\n \
117 cleanup --force-logs Skip confirmation\n \
118 cleanup --force-www Skip confirmation\n \
119 cleanup --force-all Skip confirmation",
120 ),
121 "recover" => Some(
122 " recover Auto-fix inconsistent servers\n \
123 recover all Fix all servers\n \
124 recover <id> Fix specific server\n\n \
125 Aliases: fix, status-fix",
126 ),
127 "remote" => Some(
128 " remote list List SSH profiles\n \
129 remote add <name> <user@host> <path> [port] [key]\n \
130 remote show <name> Show profile details\n \
131 remote remove <name> Delete profile\n \
132 remote test <name> Test SSH connection\n \
133 remote help Show help\n\n \
134 Aliases: remote ls, remote rm, remote delete",
135 ),
136 "sync" => Some(
137 " sync push <remote> [path] [--delete] [--dry-run]\n \
138 sync pull <remote> [path] [--delete] [--dry-run]\n \
139 sync test <remote> Test connection\n \
140 sync exec <remote> <cmd> Run remote command\n \
141 sync restart <remote> Restart remote service\n \
142 sync git-pull <remote> Remote git pull\n\n \
143 Flags:\n \
144 --delete Remove files not in source\n \
145 --dry-run, -n Preview without applying",
146 ),
147 "theme" => Some(
148 " theme Show current & available themes\n \
149 theme <name> Switch theme (live)\n \
150 theme preview <name> Preview theme\n \
151 theme debug <name> Show theme details\n \
152 theme -h, --help Show help",
153 ),
154 "lang" | "language" => Some(
155 " lang Show current language\n \
156 lang <code> Switch language (en, de, fr...)",
157 ),
158 "log-level" => Some(
159 " log-level Show current level\n \
160 log-level <level> Set level (trace/debug/info/warn/error)\n \
161 log-level -h, --help Show help",
162 ),
163 "history" => Some(
164 " history Show info\n \
165 history -c, --clear Clear with confirmation\n \
166 history -fc, --force-clear Force clear\n \
167 history -h, --help Show help",
168 ),
169 "version" => Some(
170 " version Show version info\n\n \
171 Alias: ver",
172 ),
173 "clear" => Some(
174 " clear Clear screen\n\n \
175 Alias: cls",
176 ),
177 "exit" => Some(
178 " exit Exit with confirmation\n\n \
179 Alias: q",
180 ),
181 "help" => Some(
182 " help Show all commands\n \
183 help <command> Show command details\n \
184 help -s, --simple Simple list\n \
185 help -d, --detailed Detailed list\n\n \
186 Tip: Use '<command> ?' for quick help (e.g. 'create ?')",
187 ),
188 _ => None,
189 }
190 }
191
192 fn get_localized_description(&self, command_name: &str, original_description: &str) -> String {
194 let normalized_name = command_name.replace("-", "_");
195 let description_key = format!("system.commands.{}.description", normalized_name);
196
197 if crate::i18n::has_translation(&description_key) {
198 get_command_translation(&description_key, &[])
199 } else {
200 original_description.to_string()
201 }
202 }
203
204 fn create_formatted_list(&self, handler: &crate::commands::CommandHandler) -> String {
206 let commands = handler.list_commands();
207
208 if commands.is_empty() {
209 return get_command_translation("system.commands.help.no_commands", &[]);
210 }
211
212 let mut result = String::new();
213 result.push_str(&get_command_translation("system.commands.help.header", &[]));
214 result.push_str("\n\n");
215
216 let mut categorized = std::collections::BTreeMap::new();
217
218 for (name, original_description) in commands {
219 let category_key = self.determine_category(name);
220 let localized_description = self.get_localized_description(name, original_description);
221
222 categorized
223 .entry(category_key)
224 .or_insert_with(Vec::new)
225 .push((name, localized_description));
226 }
227
228 for (category_key, commands) in categorized {
229 let category_translation_key =
230 format!("system.commands.help.category.{}", category_key);
231
232 let category_name = if crate::i18n::has_translation(&category_translation_key) {
233 get_command_translation(&category_translation_key, &[])
234 } else {
235 self.get_fallback_category_name(category_key)
236 };
237
238 result.push_str(&format!("{}:\n", category_name));
239
240 for (name, description) in &commands {
241 let usage_hint = match *name {
243 "create" => " (create [name] [port] [count])",
244 "start" => " (start <id|name|all|1-N> [-w N])",
245 "stop" => " (stop <id|name|all|1-N>)",
246 "cleanup" => " (cleanup [stopped|failed|logs|www|all])",
247 "sync" => " (sync push|pull|test|exec ...)",
248 "remote" => " (remote list|add|show|remove|test)",
249 "lang" | "language" => " (lang [code])",
250 "theme" => " (theme [name|preview|debug])",
251 "log-level" => " (log-level [level])",
252 _ => "",
253 };
254
255 result.push_str(&format!(
256 " {:12} {}{}\n",
257 name, description, usage_hint
258 ));
259 }
260 result.push('\n');
261 }
262
263 result.push_str(" Tip: '<command> ?' for details (e.g. 'create ?')\n");
264 result.push_str(&get_command_translation("system.commands.help.footer", &[]));
265 result
266 }
267
268 fn determine_category(&self, command_name: &str) -> &'static str {
270 match command_name {
271 name if name.starts_with("start")
272 || name.starts_with("stop")
273 || name.starts_with("restart") =>
274 {
275 "server_control"
276 }
277 name if name.starts_with("create") || name.starts_with("list") => "server_management",
278 name if name.starts_with("remote") || name.starts_with("sync") => "deployment",
279 name if name.starts_with("cleanup") || name.starts_with("recover") => "maintenance",
280 name if name.starts_with("theme")
281 || name.starts_with("lang")
282 || name.starts_with("log-level") =>
283 {
284 "configuration"
285 }
286 name if name.starts_with("help")
287 || name.starts_with("version")
288 || name.starts_with("history") =>
289 {
290 "information"
291 }
292 name if name.starts_with("exit") || name.starts_with("clear") => "system",
293 _ => "other",
294 }
295 }
296
297 fn get_fallback_category_name(&self, category_key: &str) -> String {
299 match category_key {
300 "server_control" => "Server Control".to_string(),
301 "server_management" => "Server Management".to_string(),
302 "deployment" => "Deployment & Sync".to_string(),
303 "maintenance" => "Maintenance".to_string(),
304 "configuration" => "Configuration".to_string(),
305 "information" => "Information".to_string(),
306 "system" => "System".to_string(),
307 "other" => "Other".to_string(),
308 _ => category_key.to_string(),
309 }
310 }
311
312 fn create_simple_list(&self, handler: &crate::commands::CommandHandler) -> String {
314 let commands = handler.list_commands();
315 let names: Vec<&str> = commands.iter().map(|(name, _)| *name).collect();
316 let names_str = names.join(", ");
317
318 get_command_translation("system.commands.help.simple_list", &[&names_str])
319 }
320
321 fn create_detailed_list(&self, handler: &crate::commands::CommandHandler) -> String {
323 let commands = handler.list_commands();
324 let mut result = String::new();
325
326 result.push_str(&get_command_translation(
327 "system.commands.help.detailed_header",
328 &[],
329 ));
330 result.push('\n');
331 result.push_str(&get_command_translation(
332 "system.commands.help.detailed_separator",
333 &[],
334 ));
335 result.push_str("\n\n");
336
337 for (name, original_description) in commands {
338 let localized_description = self.get_localized_description(name, original_description);
339
340 result.push_str(&format!(" {}\n", name.to_uppercase()));
341 result.push_str(&format!(" {}\n", localized_description));
342
343 if let Some(usage) = Self::get_command_usage(name) {
344 result.push_str(&format!("\n{}\n", usage));
345 }
346
347 result.push_str("\n ──────────────────────────────\n\n");
348 }
349
350 result
351 }
352
353 fn show_command_help(
355 &self,
356 command_name: &str,
357 handler: &crate::commands::CommandHandler,
358 ) -> String {
359 let commands = handler.list_commands();
360
361 for (name, original_description) in commands {
362 if name.eq_ignore_ascii_case(command_name) {
363 let localized_description =
364 self.get_localized_description(name, original_description);
365
366 let mut result = format!(
367 "\n {} - {}\n",
368 name.to_uppercase(),
369 localized_description
370 );
371
372 if let Some(usage) = Self::get_command_usage(name) {
373 result.push_str(&format!("\n{}\n", usage));
374 }
375
376 return result;
377 }
378 }
379
380 get_command_translation("system.commands.help.command_not_found", &[command_name])
381 }
382}