1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use crate::{Group, option, arg};
use std::rc::Rc;

/// Entry in the help documentation.
pub struct HelpEntry<K, V> {
    pub key: K,
    pub value: V,
}

/// Help formatter to use when printing the help documentation.
pub trait HelpPrinter {
    /// Print the help documentation.
    fn print(
        &self,
        group: &Group,
        subcommand_entries: &Vec<HelpEntry<&Rc<String>, &Rc<Group>>>,
        option_entries: &Vec<HelpEntry<&Rc<String>, &Rc<option::Descriptor>>>,
        arg_entries: &Vec<arg::Descriptor>,
    );
}

/// Default help printer used when none is specified.
pub struct DefaultHelpPrinter {}

impl HelpPrinter for DefaultHelpPrinter {
    fn print(
        &self,
        group: &Group,
        subcommand_entries: &Vec<HelpEntry<&Rc<String>, &Rc<Group>>>,
        option_entries: &Vec<HelpEntry<&Rc<String>, &Rc<option::Descriptor>>>,
        arg_entries: &Vec<arg::Descriptor>,
    ) {
        println!("\n### DESCRIPTION ###");
        println!("{description}", description = group.description());

        println!("\n### SUB-COMMANDS ###");
        if subcommand_entries.len() == 0 {
            println!("(No sub-commands available...)");
        } else {
            // Get longest sub-command name
            let mut max_length = 0;
            for entry in subcommand_entries {
                let prefix = match group.get_aliases_for_group_name(&entry.key) {
                    Some(aliases) => format!(
                        "{name} ({aliases})",
                        name = entry.key,
                        aliases = aliases.iter().map(|s| s.as_ref().to_string()).collect::<Vec<String>>().join(", ")
                    ),
                    None => entry.key.to_string(),
                };
                if prefix.len() > max_length {
                    max_length = prefix.len();
                }
            }

            for entry in subcommand_entries {
                let prefix = match group.get_aliases_for_group_name(&entry.key) {
                    Some(aliases) => format!(
                        "{name} ({aliases})",
                        name = entry.key,
                        aliases = aliases.iter().map(|s| s.as_ref().to_string()).collect::<Vec<String>>().join(", ")
                    ),
                    None => entry.key.to_string(),
                };
                println!("  - {prefix:<width$} | {description}", prefix = prefix, width = max_length, description = entry.value.description());
            }
        }

        println!("\n### OPTIONS ###");
        if option_entries.len() == 0 {
            println!("(No options available...)");
        } else {
            // Get longest option name
            let mut max_length = 0;
            for entry in option_entries {
                let aliases = entry.value.get_aliases();
                let prefix = if aliases.len() == 0 {
                    format!("{name} <{type_name}>", name = entry.key, type_name = entry.value.value_type())
                } else {
                    format!(
                        "{name} ({aliases}) <{type_name}>",
                        name = entry.key,
                        aliases = aliases.iter().map(|s| format!("-{}", s)).collect::<Vec<String>>().join(", "),
                        type_name = entry.value.value_type()
                    )
                };
                if prefix.len() > max_length {
                    max_length = prefix.len();
                }
            }

            for entry in option_entries {
                let aliases = entry.value.get_aliases();
                let prefix = if aliases.len() == 0 {
                    format!("{name} <{type_name}>", name = entry.key, type_name = entry.value.value_type())
                } else {
                    format!(
                        "{name} ({aliases}) <{type_name}>",
                        name = entry.key,
                        aliases = aliases.iter().map(|s| format!("-{}", s)).collect::<Vec<String>>().join(", "),
                        type_name = entry.value.value_type()
                    )
                };
                println!("  --{prefix:<width$} | {description}", prefix = prefix, width = max_length, description = entry.value.description());
            }
        }

        println!("\n### ARGUMENTS ###");
        if arg_entries.len() == 0 {
            println!("(Command expects no arguments...)");
        } else {
            let mut max_length = 0;
            for i in 0..arg_entries.len() {
                let arg_d = &arg_entries[i];

                let prefix = format!("{num}. <{type_name}>", num = i + 1, type_name = arg_d.value_type());
                if prefix.len() > max_length {
                    max_length = prefix.len();
                }
            }

            for i in 0..arg_entries.len() {
                let arg_d = &arg_entries[i];

                let prefix = format!("{num}. <{type_name}>", num = i + 1, type_name = arg_d.value_type());
                println!("  {prefix:<width$} | {description}", prefix = prefix, width = max_length, description = arg_d.description());
            }
        }

        println!();
    }
}