Global args with clap

clap is one of those libraries that has a knob for everything, if you know where to find it. I found a new one yesterday while building out a CLI for an example todo service: the global attribute.

My goal was to expose two global flags for every command in the CLI: url and token. You could already set those values in config files and using environment variables, but I wanted the user to be able to override those values on a per-request basis as well. This is the basic shape of the code:

/// A command line interface for the Checklist service.
#[derive(Debug, Parser)]
#[command(version, verbatim_doc_comment, max_term_width = 80)]
struct Cli {
    #[clap(flatten)]
    global_args: GlobalArgs,
    #[command(subcommand)]
    command: Command,
}

/// Global arguments.
#[derive(Debug, Args, Clone)]
struct GlobalArgs {
    /// The URL for the server.
    #[arg(long)]
    url: Option<String>,
    /// The token for the server.
    #[arg(long)]
    token: Option<String>,
}

#[derive(Debug, Subcommand)]
enum Command {
    CreateTodo(CreateTodoArgs),
    ...
}

This setup mostly works in the sense that it correctly parses global flags for subcommands. So if I run something like checklist create-todo --title hello --url http://localhost:3000 --token 12345, the global flags are available to the create command. The one deficiency is that the flags don’t show up in the help text for the subcommand, so you have to run checklist --help to see global flags and checklist <command> --help to see command-specific flags.

This is the problem that the global attribute solves. When you set it on a flag (like so: #[arg(long, global = true)]), clap surfaces it in the top level help text as well as subcommand help text, so you can see all your options in one place:

> checklist create-todo --help

Creates a todo.

The todo is assigned a unique id on creation and begins in an incomplete state,
as one would expect of a todo.

Usage: checklist create-todo [OPTIONS] --title <TITLE> --description <DESCRIPTION>

Options:
      --title <TITLE>
          The title of the task

      --url <URL>
          The URL for the server

      --description <DESCRIPTION>
          The description of the task

      --token <TOKEN>
          The token for the server