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
use std::fs;
use std::fs::File;
use std::io::Write;
use std::path::Path;

use clap::App;
use serde::Serialize;
use tinytemplate::TinyTemplate;

use crate::manpage::formatters;

#[derive(Serialize)]
struct Context {
    bin: String,
    version: String,
    about: String,
    usage: String,
    author: String,
    all_args: String,
    unified: String,
    flags: String,
    options: String,
    positionals: String,
    subcommands: String,
    after_help: String,
    before_help: String,
}

/// # Panics
///
/// Will panic if it can't render the template
pub fn generate(app: &App, out_dir: &Path, md_template: &str) {
    let mut tt = TinyTemplate::new();
    let manpage_template = fs::read_to_string(md_template).unwrap();
    tt.set_default_formatter(&formatters::format_escape);
    tt.add_template("man", &manpage_template).unwrap();
    tt.add_formatter("upper", formatters::format_upper);
    tt.add_formatter("escape", formatters::format_escape);
    tt.add_formatter("unescape", tinytemplate::format_unescaped);

    let context = Context::new(app);

    let rendered = tt.render("man", &context).unwrap();
    let mut file =
        File::create(&out_dir.join(format!("{}.man.md", app.get_bin_name().unwrap()))).unwrap();
    file.write_all(rendered.as_ref()).unwrap();
}

impl<'a> Context {
    fn new(app: &App) -> Context {
        Context {
            bin: existing::existing(app, "{bin}"),
            version: existing::existing(app, "{version}"),
            author: existing::existing(app, "{mit}"),
            usage: existing::existing(app, "{usage}"),
            all_args: existing::existing(app, "{all-args}"),
            unified: existing::existing(app, "{unified}"),
            flags: existing::existing(app, "{flags}"),
            options: existing::existing(app, "{options}"),
            positionals: existing::existing(app, "{positionals}"),
            subcommands: existing::existing(app, "{subcommands}"),
            after_help: existing::existing(app, "{after-help}"),
            before_help: existing::existing(app, "{before-help}"),
            about: app.get_about().unwrap().into(),
        }
    }
}

mod existing {
    use clap::App;

    pub(crate) fn existing(app: &App, variable: &'static str) -> String {
        let mut copy = app.clone().help_template(variable);

        let mut version_buffer: Vec<u8> = vec![];
        copy.write_help(&mut version_buffer).unwrap();

        String::from_utf8(version_buffer).unwrap().trim_end().into()
    }
}