BashMan
is a Cargo plugin that helps you generate BASH completions, MAN pages, and/or a CREDITS.md
page for your Rust apps using metadata from your projects' Cargo.toml
manifests. It pairs well with the (unaffiliated) cargo-deb.
(This can technically be used for non-Rust apps. It just parses the data out of a TOML file. Any TOML'll do.)
BASH completions are sub-command aware — one level deep — and avoid making duplicate suggestions. For example, if the line already has -h
, it will not suggest -h
or its long variant --help
.
MAN pages are automatically populated with the primary sections — NAME
, DESCRIPTION
, USAGE
, SUBCOMMANDS
, FLAGS
, OPTIONS
, ARGUMENTS
— and the top level page can be extended with additional arbitrary sections as needed. If subcommands are defined, additional pages for each are generated, showing their particular usage, flags, etc.
MAN pages are saved in both plain ("app.1") and GZipped ("app.1.gz") states. Linux man
can read either format, so just pick whichever you prefer for distribution purposes. (Though Gzip is smaller…)
This software is a work-in-progress.
Feel free to use it, but if something weird happens — or if you have ideas for improvement — please open an issue!
Debian and Ubuntu users can just grab the pre-built .deb
package from the latest release.
This application is written in Rust and can alternatively be built from source using Cargo:
# Clone the source.
git clone https://github.com/Blobfolio/bashman.git
# Go to it.
cd bashman
# Build as usual. Specify additional flags as desired.
cargo build \
--bin bashman \
--release
(This should work under other 64-bit Unix environments too, like MacOS.)
BashMan
pulls all the data it needs to compile BASH completions and MAN pages straight from the specified Cargo.toml
file. Once your manifest is set, all you need to do is run:
# Generate the stuff for the thing:
cargo bashman [--manifest-path /path/to/Cargo.toml]
# You can also pull up help via:
cargo bashman [-h/--help]
The flags --no-bash
, --no-man
, and --no-credits
can be used to skip the generation of BASH completions, MAN pages, and/or CREDITS.md
respectively.
The binary name, version, and description are taken from the standard Cargo.toml
fields.
For everything else, start by adding a section to your Cargo.toml
manifest like:
[package.metadata.bashman]
name = "Cargo BashMan"
bash-dir = "../release/completions"
man-dir = "../release/man"
credits-dir = "../"
Key | Type | Description | Default |
---|---|---|---|
name | string | The proper name of your application. | If not provided, the binary name is used. |
bash-dir | directory | The output directory for BASH completions. This can be an absolute path, or a path relative to the manifest. | If not provided, the manifest's parent directory is used. |
credits-dir | directory | The output directory for the CREDITS.md dependency list. This can be an absolute path, or a path relative to the manifest. |
If not provided, the manifest's parent directory is used. |
man-dir | directory | The output directory for MAN page(s). This can be an absolute path, or a path relative to the manifest. | If not provided, the manifest's parent directory is used. |
subcommands | array | An array of your app's subcommands, if any. | |
switches | array | An array of your app's true/false flags, if any. | |
options | array | An array of your app's key=value options, if any. | |
arguments | array | An array of any trailing arguments expected by your app. | |
sections | array | Arbitrary sections to append to the MAN page. |
While bash-dir
, man-dir
, and credits-dir
are required, the actual content generation can be skipped by using the CLI flags --no-bash
, --no-man
, and/or --no-credits
respectively.
When adding subcommands, each entry requires the following fields:
Key | Type | Description | Default |
---|---|---|---|
name | string | The proper name of the command. | If not provided, the cmd value will be used. |
cmd | string | The subcommand. | |
description | string | A description of what the subcommand does. |
Subcommands can have their own switches, options, arguments. These are specified in the switches
, options
, and arguments
sections respectively. Keep reading…
Example:
[[package.metadata.bashman.subcommands]]
name="Whale Talk"
cmd="whale"
description="Print an underwater message."
A "switch" is a CLI flag that either is or isn't. It can be a short key, like -h
, or a long key like --help
, or both. The value is implicitly true
if the flag is present, or false
if not.
Switches have the following fields:
Key | Type | Description |
---|---|---|
short | string | A short key, like -h . |
long | string | A long key, like --help . |
description | string | A description for the flag. |
duplicate | bool | If true , the BASH completions will suggest this switch even if already present (so i.e. it can be supplied more than once). |
subcommands | array | If this switch applies to one or more subcommands, list the commands here. If a switch applies to the top-level app, omit this field, or include an empty "" entry in the array. |
Example:
[[package.metadata.bashman.switches]]
short = "-V"
long = "--version"
description = "Print application version."
[[package.metadata.bashman.switches]]
short = "-h"
long = "--help"
description = "Print help information."
subcommands = [ "call", "text", "" ]
An "option" is exactly like a "switch", except it takes a value. As such, they have a couple more fields:
Key | Type | Description |
---|---|---|
short | string | A short key, like -h . |
long | string | A long key, like --help . |
description | string | A description for the flag. |
label | string | A placeholder label for the value bit, like <FILE> . |
duplicate | bool | If true , the BASH completions will suggest this option even if already present (so i.e. it can be supplied more than once). |
path | bool | If true , the BASH completions will suggest files/directories as potential values. If false , no value suggestion will be hazarded. |
subcommands | array | If this option applies to one or more subcommands, list the commands here. If an option applies to the top-level app, omit this field, or include an empty "" entry in the array. |
Example:
[[package.metadata.bashman.options]]
short = "-m"
long = "--manifest-path"
description = "Path to the Cargo.toml file to use."
label = "<Cargo.toml>"
path = true
[[package.metadata.bashman.options]]
short = "-c"
long = "--color"
description = "Use this foreground color."
label = "<NUM>"
subcommands = [ "print", "echo" ]
A trailing argument is what comes after everything else.
Key | Type | Description |
---|---|---|
label | string | A placeholder label for the value bit, like <FILE(s)…> . |
description | string | A description for the argument. |
subcommands | array | If this argument applies to one or more subcommands, list the commands here. If it applies to the top-level app, omit this field, or include an empty "" entry in the array. |
Example:
[[package.metadata.bashman.arguments]]
label = "<FILE(s)…>"
description = "Files and directories to search."
subcommands = [ "search" ]
BashMan
will automatically generate manual sections for:
NAME
DESCRIPTION
USAGE
SUBCOMMANDS
(if any)FLAGS
(i.e. "switches", if any)OPTIONS
(if any)ARGUMENTS
(if any)
If you would like to include other information (for the top-level MAN page), such as a list of sister software repositories, you can do so by adding sections with the following fields:
Key | Type | Description |
---|---|---|
name | string | The section name, e.g. RECIPES . |
inside | bool | If true , the section will be indented (like most sections are). |
lines | array | An array of paragraph lines (strings) to append. Line breaks are forced between entries, but you could jam everything into one string to just have it wrap. |
items | array | An array of key/value pairs to list in a manner similar to how arguments are presented. Each entry should be an array with exactly two string values, [ "Label", "A description or whatever." ] |
Generally speaking, you'll want either "lines" or "items" for a given section, but not both.
Example:
[[package.metadata.bashman.sections]]
name = "FILE TYPES"
inside = true
lines = [
"This program will search for files with the following extensions:",
".foo; .bar; .img",
]
[[package.metadata.bashman.sections]]
name = "CREDITS"
inside = true
items = [
["Bob", "https://bob.com"],
["Alice", "https://github.com/Alice"],
]
Taking BashMan
as an example, the Cargo.toml
will end up containing something like:
[package]
name = "cargo-bashman"
version = "0.1.0"
description = "BashMan is a Cargo plugin that helps you generate BASH completions and/or MAN pages for your Rust project."
...
[package.metadata.bashman]
name = "Cargo BashMan"
bash-dir = "../release/completions"
man-dir = "../release/man"
credits-dir = "../"
[[package.metadata.bashman.switches]]
short = "-h"
long = "--help"
description = "Print help information."
[[package.metadata.bashman.switches]]
short = "-V"
long = "--version"
description = "Print application version."
[[package.metadata.bashman.options]]
short = "-m"
long = "--manifest-path"
description = "Path to the Cargo.toml file to use."
label = "<Cargo.toml>"
path = true
See also: CREDITS.md
Copyright © 2024 Blobfolio, LLC <hello@blobfolio.com>
This work is free. You can redistribute it and/or modify it under the terms of the Do What The Fuck You Want To Public License, Version 2.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.