Adding support for global flags that apply for all subcommands with structopt

InstructorChris Biscardi

Share this video with your friends

Send Tweet

We want to build out functionality to let the user write into a file in their garden. To do that, we have to know the location of their garden.

We'll start by implementing a function to get the default garden path.

fn get_default_garden_dir() -> Result<PathBuf> {
    let user_dirs = UserDirs::new().ok_or_else(|| eyre!("Could not find home directory"))?;
    Ok(user_dirs.home_dir().join(".garden"))
}

This uses the user's system directories via the directories crate to find their home dir. We then add .garden to the home dir for the default garden.

Ideally, we would want the user to be able to specify their garden path. Using structopt, we can let them specify it as a short, long, or env var.

In our README, we'll specify how this should work.


```shell
GARDEN_PATH=~/github/my-digital-garden garden write
garden -p ~/github/my-digital-garden write
garden --garden_path ~/github/my-digital-garden write
```

And in our Opt struct, we can tell structopt how to handle it. Putting this in our Opt struct instead of our Command enum make it a global flag that can be specified for all subcommands.

struct Opt {
    #[structopt(parse(from_os_str), short = "p", long, env)]
    garden_path: Option<PathBuf>,

    #[structopt(subcommand)]
    cmd: Command,
}

structopt is only handling the user input, so in main() we still need to fall back to the default if the garden_path is not specified.

let garden_path = match opt.garden_path {
    Some(pathbuf) => Ok(pathbuf),
    None => get_default_garden_dir().wrap_err("`garden_path` was not supplied"),
}?;

if we force an Err in get_default_garden_dir We can see how the wrapped errors end up stacking.

Error:
   0: `garden_path` was not supplied
   1: Could not find home directory

We'll also change the signature of write to accept the pathbuf.

Command::Write { title } => write(garden_path, title),

Note that we need to import PathBuf. We can also dbg! the garden path and title to see their values.

use std::path::PathBuf;

pub fn write(garden_path: PathBuf, title: Option<String>) -> Result<()> {
    dbg!(garden_path, title);
    todo!()
}
Robert Pearce
~ 3 years ago

I'm not sure if it's just me, but cargo add directories is the install command (it sounds like it's skipped over). The audio sounds to me like you say "directory", and that is a different crate.