16

CLI apps on Unix-like OSes generally provide man pages for reference. I have not yet seen any good guide on how to do this in the Rust ecosystem - what is the idiomatic way of doing this?

I am aware of the Cargo build scripts feature, is this the way it is generally done? If yes, how would it generate man pages and how would it handle man installation on different OSes?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Espen H
  • 261
  • 2
  • 9
  • 3
    I believe following the [CLI working group](https://github.com/rust-lang-nursery/cli-wg/) will be most fruitful. They have a specific issue for [documentation of CLI apps](https://github.com/rust-lang-nursery/cli-wg/issues/23), which includes man pages. – Shepmaster Apr 06 '18 at 18:32
  • 1
    Thanks for the links! After reading the documentation issue it seems man pages are an issue that has not been solved yet. I see ripgrep has a [working approach](https://github.com/BurntSushi/ripgrep/blob/master/build.rs#L71). I'll have a look into that. – Espen H Apr 06 '18 at 18:42
  • 5
    The key insight to ripgrep's approach was to define a light abstraction over clap that stores the state of each clap arg, which can then be reused to build the tedious parts of a man page pretty easily. This abstraction layer could be avoided if clap grew an API to query its arguments. (But that is a fairly sizable change to clap, and solving the specific case of ripgrep is much easier than the fully general case.) – BurntSushi5 Apr 06 '18 at 19:46

1 Answers1

3

The current best approach I am aware of is to use the man crate. It is still a work in progress, and adding better support for man-page generation is an area that the CLI Working Group is actively working on.

As described in more detail in the README, man lets generate man pages from syntax like:

use man::prelude::*;

fn main() {
    let page = Manual::new("basic")
        .about("A basic example")
        .author(Author::new("Alice Person").email("alice@person.com"))
        .author(Author::new("Bob Human").email("bob@human.com"))
        .flag(
            Flag::new()
                .short("-d")
                .long("--debug")
                .help("Enable debug mode"),
        )
        .flag(
            Flag::new()
                .short("-v")
                .long("--verbose")
                .help("Enable verbose mode"),
        )
        .option(
            Opt::new("output")
                .short("-o")
                .long("--output")
                .help("The file path to write output to"),
        )
        .example(
            Example::new()
                .text("run basic in debug mode")
                .command("basic -d")
                .output("Debug Mode: basic will print errors to the console")
            )
        .custom(
            Section::new("usage note")
                .paragraph("This program will overwrite any file currently stored at the output path")
        )
        .render();

    println!("{}", page);
}
codesections
  • 8,900
  • 16
  • 50