What is the idiomatic way of writing man pages for Rust CLI tools?
Asked Answered
S

2

18

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?

Stunk answered 6/4, 2018 at 18:27 Comment(3)
I believe following the CLI working group will be most fruitful. They have a specific issue for documentation of CLI apps, which includes man pages.Mediaeval
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. I'll have a look into that.Stunk
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.)Mctyre
P
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("[email protected]"))
        .author(Author::new("Bob Human").email("[email protected]"))
        .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);
}
Pliske answered 6/10, 2019 at 14:40 Comment(0)
M
0

At time of writing, there isn't a clear winner for generating man pages. The most common approaches in the ecosystem seem to be:

  1. Write the man page directly, using troff syntax. Rust itself uses this approach, for example cargo-login.1 is used for man cargo-login.

    (If you don't like troff syntax, you can use helper libraries like roff-rs to generate troff.)

  2. Derive a basic man page from the --help output of your binary using help2man. This is the approach used by xh.

  3. Use clap_mangen to generate a man page from a clap command at build time.

Mariel answered 26/3, 2024 at 15:48 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.