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