repl_ng/
help.rs

1use crate::error::*;
2use crate::Parameter;
3use yansi::Paint;
4
5/// Help entry which gets sent to [HelpViewer](trait.HelpViewer.html) when help for a particular
6/// command is requested
7#[derive(Debug)]
8pub struct HelpEntry {
9    /// Command from `help <command>`
10    pub command: String,
11
12    /// The aliases, if any.
13    pub aliases: Vec<String>,
14
15    /// Parameters defined for the command
16    pub parameters: Vec<(String, bool)>,
17
18    /// Help summary for the command
19    pub summary: Option<String>,
20}
21
22impl HelpEntry {
23    pub(crate) fn new(
24        command_name: &str,
25        aliases: &Vec<String>,
26        parameters: &[Parameter],
27        summary: &Option<String>,
28    ) -> Self {
29        Self {
30            command: command_name.to_string(),
31            aliases: aliases.clone(),
32            parameters: parameters
33                .iter()
34                .map(|pd| (pd.name.clone(), pd.required))
35                .collect(),
36            summary: summary.clone(),
37        }
38    }
39}
40
41/// Struct which gets sent to [HelpViewer](trait.HelpViewer.html) when `help` command is called
42pub struct HelpContext {
43    /// Application name
44    pub app_name: String,
45
46    /// Application version
47    pub app_version: String,
48
49    /// Application purpose/description
50    pub app_purpose: String,
51
52    /// List of help entries
53    pub help_entries: Vec<HelpEntry>,
54}
55
56impl HelpContext {
57    pub(crate) fn new(
58        app_name: &str,
59        app_version: &str,
60        app_purpose: &str,
61        help_entries: Vec<HelpEntry>,
62    ) -> Self {
63        Self {
64            app_name: app_name.into(),
65            app_version: app_version.into(),
66            app_purpose: app_purpose.into(),
67            help_entries,
68        }
69    }
70}
71
72/// Trait to be used if you want your own custom Help output
73pub trait HelpViewer {
74    /// Called when the plain `help` command is called with no arguments
75    fn help_general(&self, context: &HelpContext) -> Result<()>;
76
77    /// Called when the `help` command is called with a command argument (i.e., `help foo`).
78    /// Note that you won't have to handle an unknown command - it'll be handled in the caller
79    fn help_command(&self, entry: &HelpEntry) -> Result<()>;
80}
81
82/// Default [HelpViewer](trait.HelpViewer.html)
83pub struct DefaultHelpViewer;
84
85impl DefaultHelpViewer {
86    pub fn new() -> Self {
87        Self
88    }
89}
90
91impl HelpViewer for DefaultHelpViewer {
92    fn help_general(&self, context: &HelpContext) -> Result<()> {
93        self.print_help_header(context);
94        for entry in &context.help_entries {
95            print!("{}", entry.command);
96            if !entry.aliases.is_empty() {
97                print!(", {}", entry.aliases.join(", "));
98            }
99            if entry.summary.is_some() {
100                print!(" - {}", entry.summary.as_ref().unwrap());
101            }
102            println!();
103        }
104
105        Ok(())
106    }
107
108    fn help_command(&self, entry: &HelpEntry) -> Result<()> {
109        if entry.summary.is_some() {
110            println!("{}: {}", entry.command, entry.summary.as_ref().unwrap());
111        } else {
112            println!("{}:", entry.command);
113        }
114        if !entry.aliases.is_empty() {
115            println!("Aliases: {}", entry.aliases.join(", "));
116        }
117        println!("Usage:");
118        print!("\t{}", entry.command);
119        for param in &entry.parameters {
120            if param.1 {
121                print!(" {}", param.0);
122            } else {
123                print!(" [{}]", param.0);
124            }
125        }
126        println!();
127
128        Ok(())
129    }
130}
131
132impl DefaultHelpViewer {
133    fn print_help_header(&self, context: &HelpContext) {
134        let header = format!(
135            "{} {}: {}",
136            context.app_name, context.app_version, context.app_purpose
137        );
138        let underline = Paint::new(" ".repeat(header.len())).strikethrough();
139        println!("{}", header);
140        println!("{}", underline);
141    }
142}