Skip to main content

Module cli

Module cli 

Source
Expand description

The command-line plumbing every tool in the family shares: dispatch on argv[0], --version, doctor, the JSON output and the structured error, the --json/--text switch, and the man pages and shell completions packaging needs. Behind the cli cargo feature; the default build, and the static library, contain none of it.

IT KNOWS NOTHING ABOUT ANY FORMAT, and must not learn. A crate describes its tools once, as a Family — the repository name, the crate and version, the install hints, and one Tool per dotted name, each a clap command and a function that runs it — and hands that to main. Everything here reads only that description.

ONE COPY, NOT ONE PER CRATE. This module began as a directory each tool carried by hand (src/cli/common/), and the copies drifted within weeks: one doctor learnt to drain a long --version answer while the others still stalled on it, one entry point learnt to align its help, and some could write man pages while others could not. Do not copy it back out; fix it here.

THE CONTRACT IT CARRIES, so each crate fills it in rather than defining its own:

  • One binary, many names. Invoked as a tool’s dotted name (mkfs.<fs>, fs.<fs>, img.<fmt>), it is that tool. Invoked under any other name — the repository’s (rust-fs-<fs>), or a renamed copy — the first argument names the tool, by its verb (mkfs) or its full name (mkfs.<fs>). That second form is the one nothing on PATH can shadow.
  • --version prints <tool> (<crate>) <version> for every name, which is how doctor and the test suites tell our binary from another package’s program of the same name.
  • Output. A result is JSON on stdout by default, --text for people; file content is raw bytes and is never wrapped. A failure is {"error": "...", "code": N} on STDERR, and N is the exit status, so stdout carries a result or nothing — never half of one, and never an error a pipe would take for data.
  • Exit statuses, outside a tool that has its own scheme (fsck’s 0/1/4/8): 0 done, 1 failed, 2 the command line was wrong, 3 the verb exists but this crate cannot do it (not implemented, or the format is read-only). A script moved between formats fails loudly on 3 instead of meaning something else.
  • <repo> doctor resolves every dotted name on PATH and says, per name, whether the program found is ours, and if not, what wins and how to fix it.
  • <repo> generate names|man|completions (hidden) prints the dotted names to link, and writes the man pages and shell completions, from the same clap commands the tools parse with.
use fs_core::cli::{self, CliError, Family, Json, Outcome, Tool};
use fs_core::cli::clap::{ArgMatches, Command};

fn command() -> Command {
    Command::new("fs")
        .about("Work on an image")
        .args(cli::format_args())
        .after_help("Examples:\n  fs.demo disk.img")
}

fn run(_: &ArgMatches) -> Result<Outcome, CliError> {
    Ok(Outcome::report(Json::object([("fs", Json::from("demo"))])))
}

static FAMILY: Family = Family {
    repo: "rust-fs-demo",
    crate_name: "am-fs-demo",
    version: "0.1.0",
    about: "Demo tools",
    install_hints: &["`cargo install am-fs-demo --features cli`"],
    tools: &[Tool {
        name: "fs.demo",
        verb: "fs",
        section: 1,
        about: "Work on a demo image",
        usage_exit: cli::output::EXIT_USAGE,
        command,
        run,
    }],
};

fn main() -> std::process::ExitCode {
    cli::main(&FAMILY)
}

Re-exports§

pub use family::Family;
pub use family::Tool;
pub use output::CliError;
pub use output::Format;
pub use output::Json;
pub use output::Outcome;
pub use clap;

Modules§

dispatch
Which tool a process is, from the name it was started under.
docs
Man pages and shell completions, generated from the clap commands the tools actually parse with, so the documentation cannot describe a flag a program does not take.
doctor
<repo> doctor: is every name this repository installs, as PATH resolves it, our program?
family
How a driver describes its tools to the shared plumbing.
output
What a tool prints: a JSON result by default, text on request, and a structured error on stderr whose code is the exit status.
version
The identifying --version line: <tool> (<crate>) <version>.

Structs§

Response
What the plumbing prints for one invocation, and the exit status, before any of it is written.

Functions§

format_args
--json and --text, for any command that reports. The last one given wins, so an alias or a wrapper can add either without breaking a command line that already has the other.
main
The whole program: work out which tool this is, parse its command line, run it and print what it returned.
repo_command
The repository-named entry point’s clap command: every tool as a subcommand (so its help and its man page list them), plus doctor.
respond
run without the writing: resolve, parse, run the tool, and return what would be printed.
run
main over an explicit argument vector, argv[0] included.
tool_command
A tool’s clap command, named and versioned for the name it runs under.