cmd_args/
help.rs

1use crate::{Group, option, arg};
2use std::rc::Rc;
3
4/// Entry in the help documentation.
5pub struct HelpEntry<K, V> {
6    pub key: K,
7    pub value: V,
8}
9
10/// Help formatter to use when printing the help documentation.
11pub trait HelpPrinter {
12    /// Print the help documentation.
13    fn print(
14        &self,
15        group: &Group,
16        subcommand_entries: &Vec<HelpEntry<&Rc<String>, &Rc<Group>>>,
17        option_entries: &Vec<HelpEntry<&Rc<String>, &Rc<option::Descriptor>>>,
18        arg_entries: &Vec<arg::Descriptor>,
19    );
20}
21
22/// Default help printer used when none is specified.
23pub struct DefaultHelpPrinter {}
24
25impl HelpPrinter for DefaultHelpPrinter {
26    fn print(
27        &self,
28        group: &Group,
29        subcommand_entries: &Vec<HelpEntry<&Rc<String>, &Rc<Group>>>,
30        option_entries: &Vec<HelpEntry<&Rc<String>, &Rc<option::Descriptor>>>,
31        arg_entries: &Vec<arg::Descriptor>,
32    ) {
33        println!("\n### DESCRIPTION ###");
34        println!("{description}", description = group.description());
35
36        println!("\n### SUB-COMMANDS ###");
37        if subcommand_entries.len() == 0 {
38            println!("(No sub-commands available...)");
39        } else {
40            // Get longest sub-command name
41            let mut max_length = 0;
42            for entry in subcommand_entries {
43                let prefix = match group.get_aliases_for_group_name(&entry.key) {
44                    Some(aliases) => format!(
45                        "{name} ({aliases})",
46                        name = entry.key,
47                        aliases = aliases.iter().map(|s| s.as_ref().to_string()).collect::<Vec<String>>().join(", ")
48                    ),
49                    None => entry.key.to_string(),
50                };
51                if prefix.len() > max_length {
52                    max_length = prefix.len();
53                }
54            }
55
56            for entry in subcommand_entries {
57                let prefix = match group.get_aliases_for_group_name(&entry.key) {
58                    Some(aliases) => format!(
59                        "{name} ({aliases})",
60                        name = entry.key,
61                        aliases = aliases.iter().map(|s| s.as_ref().to_string()).collect::<Vec<String>>().join(", ")
62                    ),
63                    None => entry.key.to_string(),
64                };
65                println!("  - {prefix:<width$} | {description}", prefix = prefix, width = max_length, description = entry.value.description());
66            }
67        }
68
69        println!("\n### OPTIONS ###");
70        if option_entries.len() == 0 {
71            println!("(No options available...)");
72        } else {
73            // Get longest option name
74            let mut max_length = 0;
75            for entry in option_entries {
76                let aliases = entry.value.get_aliases();
77                let prefix = if aliases.len() == 0 {
78                    format!("{name} <{type_name}>", name = entry.key, type_name = entry.value.value_type())
79                } else {
80                    format!(
81                        "{name} ({aliases}) <{type_name}>",
82                        name = entry.key,
83                        aliases = aliases.iter().map(|s| format!("-{}", s)).collect::<Vec<String>>().join(", "),
84                        type_name = entry.value.value_type()
85                    )
86                };
87                if prefix.len() > max_length {
88                    max_length = prefix.len();
89                }
90            }
91
92            for entry in option_entries {
93                let aliases = entry.value.get_aliases();
94                let prefix = if aliases.len() == 0 {
95                    format!("{name} <{type_name}>", name = entry.key, type_name = entry.value.value_type())
96                } else {
97                    format!(
98                        "{name} ({aliases}) <{type_name}>",
99                        name = entry.key,
100                        aliases = aliases.iter().map(|s| format!("-{}", s)).collect::<Vec<String>>().join(", "),
101                        type_name = entry.value.value_type()
102                    )
103                };
104                println!("  --{prefix:<width$} | {description}", prefix = prefix, width = max_length, description = entry.value.description());
105            }
106        }
107
108        println!("\n### ARGUMENTS ###");
109        if arg_entries.len() == 0 {
110            println!("(Command expects no arguments...)");
111        } else {
112            let mut max_length = 0;
113            for i in 0..arg_entries.len() {
114                let arg_d = &arg_entries[i];
115
116                let prefix = format!("{num}. <{type_name}>", num = i + 1, type_name = arg_d.value_type());
117                if prefix.len() > max_length {
118                    max_length = prefix.len();
119                }
120            }
121
122            for i in 0..arg_entries.len() {
123                let arg_d = &arg_entries[i];
124
125                let prefix = format!("{num}. <{type_name}>", num = i + 1, type_name = arg_d.value_type());
126                println!("  {prefix:<width$} | {description}", prefix = prefix, width = max_length, description = arg_d.description());
127            }
128        }
129
130        println!();
131    }
132}