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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189

use indexmap::{IndexMap, indexmap};
use crate::CliCommand;
use crate::router::CliRouter;
use crate::*;

pub struct CliHelpScreen {
    pub title: String,
    pub usage: String,
    pub description: String,
    pub params: IndexMap<String, String>,
    pub flags: IndexMap<String, String>,
    pub examples: Vec<String>
}

impl CliHelpScreen {

    pub fn new(title: &str, usage: &str, description: &str) -> Self {

        Self {
            title: title.to_string(),
            usage: usage.to_string(),
            description: description.to_string(),
            params: indexmap![],
            flags: indexmap![],
            examples: Vec::new()
        }
    }

    /// Add item to list of parameters that are displayed when the help screen is output.
    pub fn add_param(&mut self, param: &str, description: &str) {
        self.params.insert(param.to_string(), description.to_string());
    }

    /// Add item to list of flags that are displayed when the help screen is output.
    pub fn add_flag(&mut self, flag: &str, description: &str) {
        self.flags.insert(flag.to_string(), description.to_string());
    }

    /// Add item to list of examples that are displayed when the help screen is output.
    pub fn add_example(&mut self, example: &str) {
        self.examples.push(example.to_string());
    }

    // Never needs to be manually executed, and automatically executed if the first argument passed 
    /// via the command line is 'help' or '-h'.  Outputs the help screen fo the specified CLI command. 
    pub fn render(cmd: &Box<dyn CliCommand>, cmd_alias: &String, shortcuts: &Vec<String>) {

        // Get help screen
        let help = cmd.help();

        // Display basics
        cli_header(help.title.as_str());
        cli_send("USAGE\r\n\r\n");
        cli_send(format!("    {}\r\n", help.usage).as_str());

        // Display shortcuts
        for shortcut in shortcuts {
            let tmp_usage = help.usage.replace(cmd_alias, shortcut.as_str());
            cli_send(format!("    {}\r\n", tmp_usage).as_str());
        }
        cli_send("\r\n");

        // Description
        if !help.description.is_empty() {
            let options = textwrap::Options::new(75).initial_indent("    ").subsequent_indent("    ");
            let desc = textwrap::fill(help.description.as_str(), &options);

            cli_send("DESCRIPTION:\r\n\r\n");
            cli_send(desc.as_str());
            cli_send("\r\n\r\n");
        }

        // Parameters
        if !help.params.is_empty() {
            cli_send("PARAMETERS\r\n\r\n");
            cli_display_array(&help.params);
        }

        // Flags
        if !help.flags.is_empty() {
            cli_send("FLAGS\r\n\r\n");
            cli_display_array(&help.flags);
        }

        // Examples
        if !help.examples.is_empty() {
            cli_send("EXAMPLES\r\n\r\n");
            for example in help.examples {
                println!("    {}\r\n", example);
            }
        }

        // End
        cli_send("-- END --\r\n\r\n");
    }

    /// Never needs to be manually executed, and is automatically when the first and only argument passed 
    /// via command line is 'help' or '-h'.  Displays either all availalbe categories or CLI commands 
    /// depending whether or not categories have been added into the router.
    pub fn render_index(router: &CliRouter) {

        // Header
        cli_header("Available Commands");
        cli_send("Below shows all available commands.  Run any of the commands with 'help' as the first argument to view full details on the command.\r\n\r\n");
        cli_send("AVAILABLE COMMANDS\r\n\r\n");

        // Display as needed
        let mut table: IndexMap<String, String> = indexmap![];
        if !router.categories.is_empty() {

            // Sort keys
            let mut keys: Vec<String> = router.categories.keys().cloned().collect();
            keys.sort();

            // Create array to render
            for alias in keys {
                let (_title, description) = router.categories.get(&alias).unwrap();
                table.insert(alias.to_string(), description.to_string());
            }

            // Render array
            cli_display_array(&table);

        // No categories, display individual commands
        } else {

            // Sort keys
            let mut keys: Vec<String> = router.commands.keys().cloned().collect();
            keys.sort();

            // Go through keys
            for alias in keys {
                let cmd = router.commands.get(&alias).unwrap();
                let cmd_help = cmd.help();
                table.insert(alias.to_string(), cmd_help.description);
            }

            // Display commands
            cli_display_array(&table);
        }

        // Exit
        cli_send("-- END --\r\n");
        std::process::exit(0);
    }

    /// Never needs to be manually executed, and only applicable if you're using multiple categories to organize groups 
    /// of CLI commands.  Executed first first argument via command line is either 'help' or '-h', and 
    /// second is the name of a category.  Will display all CLI commands available within that category.
    pub fn render_category(router: &CliRouter, cat_alias: &String) {

        // GEt category
        let (cat_title, cat_desc) = router.categories.get(cat_alias).unwrap();
        cli_header(cat_title);

        // Description
        if !cat_desc.is_empty() {
            let options = textwrap::Options::new(75).initial_indent("    ").subsequent_indent("    ");
            let desc = textwrap::fill(cat_desc.as_str(), &options);

            cli_send("DESCRIPTION:\r\n\r\n");
            cli_send(desc.as_str());
            cli_send("\r\n\r\n");
        }

        let chk = format!("{} ", cat_alias);
        let mut keys: Vec<String> = router.commands.keys().filter(|&k| k.starts_with(&chk)).cloned().collect();
        keys.sort();

        // GO through commands
        let mut table: IndexMap<String, String> = indexmap![];
        for alias in keys {
            let cmd = router.commands.get(&alias).unwrap();
            let cmd_help = cmd.help();
            table.insert(alias.trim_start_matches(&chk).to_string(), cmd_help.description);
        }


        // Display commands
        cli_send("AVAILABLE COMMANDS\r\n\r\n");
        cli_display_array(&table);
        cli_send("-- END --\r\n\r\n");
        std::process::exit(0);
    }

}