1use crate::command::Command;
2use crate::error::ModCliError;
3use crate::loader::CommandRegistry;
4use crate::output::hook;
5use crate::output::markdown;
6use crate::output::messages;
7
8pub struct HelpCommand;
10
11impl Default for HelpCommand {
12 fn default() -> Self {
13 Self::new()
14 }
15}
16
17impl HelpCommand {
18 pub fn new() -> Self {
19 Self
20 }
21}
22
23impl Command for HelpCommand {
24 fn name(&self) -> &str {
25 "help"
26 }
27
28 fn aliases(&self) -> &[&str] {
29 &["--help", "-h"]
30 }
31
32 fn help(&self) -> Option<&str> {
33 Some("Displays help information")
34 }
35
36 fn validate(&self, args: &[String]) -> Result<(), ModCliError> {
37 if args.len() > 1 {
38 Err(ModCliError::InvalidUsage(
39 "Too many arguments. Usage: help [command]".into(),
40 ))
41 } else {
42 Ok(())
43 }
44 }
45
46 fn execute(&self, _args: &[String]) {}
47
48 fn execute_with(&self, args: &[String], registry: &CommandRegistry) {
49 if args.len() == 1 {
51 let query = &args[0];
52 if let Some(target) = registry.get(query) {
54 if registry.is_visible(target) {
55 let name_line = target.name().to_string();
56 println!("{name_line}");
57 let body = target.help().unwrap_or("No description.");
58 let rendered = markdown::render_markdown(body);
59 print!("{rendered}");
60 } else {
61 println!("No help available for '{query}'");
62 }
63 return;
64 }
65
66 let ns = format!("{query}:");
68 let mut any = false;
69 let ns_header_fallback = format!("Help ({query}):");
70 let header = messages::message_or_default("help.ns_header", &ns_header_fallback);
71 println!("{header}");
72 for command in registry.all() {
73 let name = command.name();
74 if name.starts_with(&ns) && registry.is_visible(command.as_ref()) {
75 println!(
76 " {:<20} {}",
77 name,
78 markdown::render_markdown(command.help().unwrap_or("No description"))
79 );
80 any = true;
81 }
82 }
83 if !any {
84 let unknown =
85 format!("[{query}]. Type `help` or `--help` for a list of available commands.");
86 hook::unknown(&unknown);
87 }
88 return;
89 }
90
91 let header = messages::message_or_default("help.header", "Help:");
92 println!("{header}");
93 for command in registry.all() {
94 let name = command.name();
95 let top_level = !name.contains(':');
96 if top_level && registry.is_visible(command.as_ref()) {
97 println!(
98 " {:<12} {}",
99 name,
100 markdown::render_markdown(command.help().unwrap_or("No description"))
101 );
102 }
103 }
104 if let Some(footer) = messages::get_message("help.footer") {
105 println!("{footer}");
106 }
107 }
108}