falcon_cli/
help.rs

1use crate::router::CliRouter;
2use crate::CliCommand;
3use crate::*;
4use indexmap::{indexmap, IndexMap};
5
6pub struct CliHelpScreen {
7    pub title: String,
8    pub usage: String,
9    pub description: String,
10    pub params: IndexMap<String, String>,
11    pub flags: IndexMap<String, String>,
12    pub examples: Vec<String>,
13}
14
15impl CliHelpScreen {
16    pub fn new(title: &str, usage: &str, description: &str) -> Self {
17        Self {
18            title: title.to_string(),
19            usage: usage.to_string(),
20            description: description.to_string(),
21            params: indexmap![],
22            flags: indexmap![],
23            examples: Vec::new(),
24        }
25    }
26
27    /// Add item to list of parameters that are displayed when the help screen is output.
28    pub fn add_param(&mut self, param: &str, description: &str) {
29        self.params
30            .insert(param.to_string(), description.to_string());
31    }
32
33    /// Add item to list of flags that are displayed when the help screen is output.
34    pub fn add_flag(&mut self, flag: &str, description: &str) {
35        self.flags.insert(flag.to_string(), description.to_string());
36    }
37
38    /// Add item to list of examples that are displayed when the help screen is output.
39    pub fn add_example(&mut self, example: &str) {
40        self.examples.push(example.to_string());
41    }
42
43    // Never needs to be manually executed, and automatically executed if the first argument passed
44    /// via the command line is 'help' or '-h'.  Outputs the help screen fo the specified CLI command.
45    pub fn render(cmd: &Box<dyn CliCommand>, cmd_alias: &String, shortcuts: &Vec<String>) {
46        // Get help screen
47        let help = cmd.help();
48
49        // Display basics
50        cli_header(help.title.as_str());
51        cli_send!("USAGE\r\n");
52        cli_send!(format!("    {}\r\n", help.usage).as_str());
53
54        // Display shortcuts
55        for shortcut in shortcuts {
56            let tmp_usage = help.usage.replace(cmd_alias, shortcut.as_str());
57            cli_send!(format!("    {}\r\n", tmp_usage).as_str());
58        }
59        cli_send!("\r\n");
60
61        // Description
62        if !help.description.is_empty() {
63            let options = textwrap::Options::new(75)
64                .initial_indent("    ")
65                .subsequent_indent("    ");
66            let desc = textwrap::fill(help.description.as_str(), &options);
67
68            cli_send!("DESCRIPTION:\r\n\r\n");
69            cli_send!(desc.as_str());
70            cli_send!("\r\n\r\n");
71        }
72
73        // Parameters
74        if !help.params.is_empty() {
75            cli_send!("PARAMETERS\r\n\r\n");
76            cli_display_array(&help.params);
77        }
78
79        // Flags
80        if !help.flags.is_empty() {
81            cli_send!("FLAGS\r\n\r\n");
82            cli_display_array(&help.flags);
83        }
84
85        // Examples
86        if !help.examples.is_empty() {
87            cli_send!("EXAMPLES\r\n\r\n");
88            for example in help.examples {
89                println!("    {}\r\n", example);
90            }
91        }
92
93        // End
94        cli_send!("-- END --\r\n\r\n");
95    }
96
97    /// Never needs to be manually executed, and is automatically when the first and only argument passed
98    /// via command line is 'help' or '-h'.  Displays either all availalbe categories or CLI commands
99    /// depending whether or not categories have been added into the router.
100    pub fn render_index(router: &CliRouter) {
101        // Header
102        cli_header("Available Commands");
103        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");
104        cli_send!("AVAILABLE COMMANDS\r\n\r\n");
105
106        // Display as needed
107        let mut table: IndexMap<String, String> = indexmap![];
108        if !router.categories.is_empty() {
109            // Sort keys
110
111            let mut keys: Vec<String> = router.categories.keys().cloned().collect();
112            keys.sort();
113
114            // Create array to render
115            for alias in keys {
116                let (_title, description) = router.categories.get(&alias).unwrap();
117                table.insert(alias.to_string(), description.to_string());
118            }
119
120            // Render array
121            cli_display_array(&table);
122
123        // No categories, display individual commands
124        } else {
125            // Sort keys
126            let mut keys: Vec<String> = router.commands.keys().cloned().collect();
127            keys.sort();
128
129            // Go through keys
130            for alias in keys {
131                let cmd = router.commands.get(&alias).unwrap();
132                let cmd_help = cmd.help();
133                table.insert(alias.to_string(), cmd_help.description);
134            }
135
136            // Display commands
137            cli_display_array(&table);
138        }
139
140        // Exit
141        cli_send!("-- END --\r\n");
142        std::process::exit(0);
143    }
144
145    /// Never needs to be manually executed, and only applicable if you're using multiple categories to organize groups
146    /// of CLI commands.  Executed first first argument via command line is either 'help' or '-h', and
147    /// second is the name of a category.  Will display all CLI commands available within that category.
148    pub fn render_category(router: &CliRouter, cat_alias: &String) {
149        // GEt category
150        let (cat_title, cat_desc) = router.categories.get(cat_alias).unwrap();
151        cli_header(cat_title);
152
153        // Description
154        if !cat_desc.is_empty() {
155            let options = textwrap::Options::new(75)
156                .initial_indent("    ")
157                .subsequent_indent("    ");
158            let desc = textwrap::fill(cat_desc.as_str(), &options);
159
160            cli_send!("DESCRIPTION:\r\n\r\n");
161            cli_send!(desc.as_str());
162            cli_send!("\r\n\r\n");
163        }
164
165        // Sub categories
166        let chk = format!("{} ", cat_alias);
167        let mut sub_categories: Vec<String> = router
168            .categories
169        .keys()
170        .filter(|&k| k.starts_with(&chk))
171        .cloned()
172        .collect();
173    sub_categories.sort();
174
175        // Go through sub-categories
176        let mut table: IndexMap<String, String> = indexmap![];
177        for full_alias in sub_categories {
178            let alias = full_alias.trim_start_matches(&chk).to_string();
179            if alias.contains(" ") {
180                continue;
181            }
182            let (_, desc) = router.categories.get(&full_alias).unwrap();
183
184            table.insert(
185                alias,
186                desc.clone()
187            );
188        }
189
190        // Get commands to display
191        let mut keys: Vec<String> = router
192            .commands
193            .keys()
194            .filter(|&k| k.starts_with(&chk))
195            .cloned()
196            .collect();
197        keys.sort();
198
199        // GO through commands
200        for full_alias in keys {
201            let alias = full_alias.trim_start_matches(&chk).to_string();
202            if alias.contains(" ") {
203                continue;
204            }
205            let cmd = router.commands.get(&full_alias).unwrap();
206            let cmd_help = cmd.help();
207            table.insert(
208                alias,
209                cmd_help.description,
210            );
211        }
212
213        // Display commands
214        cli_send!("AVAILABLE COMMANDS\r\n\r\n");
215        cli_display_array(&table);
216        cli_send!("-- END --\r\n\r\n");
217        std::process::exit(0);
218    }
219}