StructOpt - how to provide a default value for a Vec?

2k Views Asked by At

I'm looking for a way to initialize a structopt Vec field with multiple items by default. I can do it for a single item with:

use structopt::StructOpt;

#[derive(Debug, StructOpt)]
pub struct Cli {
    #[structopt(default_value = "foo")]
    foo: Vec<String>,
}

fn main() {
    let cli = Cli::from_iter(Vec::<String>::new());
    assert_eq!(cli.foo, vec!["foo"]);
}

But how to make cli.foo to be equal let's say vec!["foo", "bar"] by default?

2

There are 2 best solutions below

0
imbolc On BEST ANSWER

I've followed the L. Riemer advice, and it seems it's enough to implement FromStr only:

use structopt::StructOpt;

#[derive(Debug, PartialEq)]
struct Foo(Vec<String>);

impl std::str::FromStr for Foo {
    type Err = Box<dyn std::error::Error>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(Foo(s.split(",").map(|x| x.trim().to_owned()).collect()))
    }
}

#[derive(StructOpt)]
pub struct Cli {
    #[structopt(long, default_value = "foo, bar")]
    foo: Foo,
}

fn main() {
    let cli = Cli::from_iter(Vec::<String>::new());
    assert_eq!(cli.foo, Foo(vec!["foo".into(), "bar".into()]));

    let cli = Cli::from_iter(vec!["", "--foo", "foo"]);
    assert_eq!(cli.foo, Foo(vec!["foo".into()]));

    let cli = Cli::from_iter(vec!["", "--foo", "foo,bar,baz"]);
    assert_eq!(cli.foo, Foo(vec!["foo".into(), "bar".into(), "baz".into()]));
}
0
Masklinn On

I don't think you can do that: while StructOpt has some tricks around default values, I expect this still ends with the default value being injected in the CLI parsing as if it had been provided explicitly, which means there is likely no way to provide multiple default values (though I could certainly be wrong).

You probably want to handle this at the application-level e.g. right after having parsed the CLI, check for the arity of foo and update it if it's empty.