mit_build_tools/manpage/
generate.rs

1use std::{fs, fs::File, io::Write, path::Path};
2
3use clap::App;
4use serde::Serialize;
5use tinytemplate::TinyTemplate;
6
7use crate::manpage::formatters;
8
9#[derive(Serialize)]
10struct Context {
11    bin: String,
12    version: String,
13    about: String,
14    usage: String,
15    author: String,
16    all_args: String,
17    options: String,
18    positionals: String,
19    subcommands: String,
20    after_help: String,
21    before_help: String,
22}
23
24/// # Panics
25///
26/// Will panic if it can't render the template
27pub fn generate(app: &App<'_>, out_dir: &Path, md_template: &str) {
28    let mut tt = TinyTemplate::new();
29    let manpage_template = fs::read_to_string(md_template).unwrap();
30    tt.set_default_formatter(&formatters::format_escape);
31    tt.add_template("man", &manpage_template).unwrap();
32    tt.add_formatter("upper", formatters::format_upper);
33    tt.add_formatter("escape", formatters::format_escape);
34    tt.add_formatter("unescape", tinytemplate::format_unescaped);
35
36    let context = Context::new(app);
37
38    let rendered = tt.render("man", &context).unwrap();
39    let mut file =
40        File::create(&out_dir.join(format!("{}.man.md", app.get_bin_name().unwrap()))).unwrap();
41    file.write_all(rendered.as_ref()).unwrap();
42}
43
44impl<'a> Context {
45    fn new(app: &App<'_>) -> Self {
46        Self {
47            bin: existing::existing(app, "{bin}"),
48            version: existing::existing(app, "{version}"),
49            author: existing::existing(app, "{mit}"),
50            usage: existing::existing(app, "{usage}"),
51            all_args: existing::existing(app, "{all-args}"),
52            options: existing::existing(app, "{options}"),
53            positionals: existing::existing(app, "{positionals}"),
54            subcommands: existing::existing(app, "{subcommands}"),
55            after_help: existing::existing(app, "{after-help}"),
56            before_help: existing::existing(app, "{before-help}"),
57            about: app.get_about().unwrap().into(),
58        }
59    }
60}
61
62mod existing {
63    use clap::App;
64
65    pub fn existing(app: &App<'_>, variable: &'static str) -> String {
66        let mut copy = app.clone().help_template(variable);
67
68        let mut version_buffer: Vec<u8> = vec![];
69        copy.write_help(&mut version_buffer).unwrap();
70
71        String::from_utf8(version_buffer).unwrap().trim_end().into()
72    }
73}