Setting up git-like subcommands using structopt

Chris Biscardi
InstructorChris Biscardi

Share this video with your friends

Send Tweet

The StructOpt crate allows us to derive our human person functionality from our Struct definitions.

We will create a structop for our write subcommand that will take a short and long flag for our title argument


cargo add structopt

structopt allows us to derive argument parsing functionality from our structs. The name is a play on struct Opt {}. To do this we need to derive(StructOpt) on our struct. In this case we have a subcommand, so we'll set up one of the fields on the Opt struct to be a structopt(subcommand). This subcommand value will be an enum, indicating that only one subcommand can be used at a time.

structopt also gives us tools to specify one of three argument types:

  • positional
  • short
  • long

By default every argument is a positional argument, which means that in this case if we left it as-is, the CLI would function as

garden write my-title

We want to use a flag, so we'll use structopt to generate short and long flags for the title argument with structopt(short, long). That gives the use the option of -t and --title to specify a title.

structopt also contains some type processing, so all we need to do is specify that title is an optional string and the flags will become optional. That is: a user can specify or not specify a title.

struct Opt {
    #[structopt(subcommand)]
    cmd: Command,
}
enum Command {
    Write {
        #[structopt(short, long)]
        title: Option<String>,
    },
}

After setting up struct Opt, we can parse the args and use the dbg!() macro to print them to the console so we can see what they look like.

let opt = Opt::from_args();
dbg!(opt);

To do that we also need to derive(Debug) in addition to structopt on both the enum and the struct.

Given input like

garden write

We will see the following output.

[src/main.rs:21] opt = Opt {
    cmd: Write {
        title: None,
    },
}

We can choose to cargo build and then ./target/debug/garden write or we can use cargo run -- write. The -- ensures that our arguments get passed to the CLI we're building and not cargo run. This is especially relevant when passing something like --help.