TeXitoi / structopt

Parse command line arguments by defining a struct.
Other
2.7k stars 149 forks source link

Not working when have multiple `from_args` calls #492

Closed stephenctw closed 2 years ago

stephenctw commented 3 years ago

I have a program that needs several structs that derive from StructOpt, yet they are in parallel not subcommand to each other. I want to achieve this with code

use structopt::StructOpt;

#[derive(StructOpt, Debug)]
struct AConfig {
    #[structopt(short, long, env)]
    pub ab: Option<String>,
    #[structopt(short, long, env)]
    pub cb: Option<String>,
}

#[derive(StructOpt, Debug)]
struct BConfig {
    #[structopt(short, long, env)]
    pub ba: Option<String>,
    #[structopt(short, long, env)]
    pub db: Option<String>,
}

fn main() {
    let b_config = BConfig::from_args();
    let a_config = AConfig::from_args();

    println!("{:?}", a_config);
    println!("{:?}", b_config);
}

I was expecting this to work just fine, giving me all options in help, and accept them with no problem. But what I am seeing is only the first struct that calls from_args is shown in help, and none of the options actually work.

USAGE:
    structopt [OPTIONS]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

OPTIONS:
    -b, --ba <ba>     [env: BA=]
    -d, --db <db>     [env: DB=]

./target/debug/structopt -b
error: The argument '--ba <ba>' requires a value but none was supplied

USAGE:
    structopt --ba <ba>

For more information try --help

./target/debug/structopt -b 1
error: Found argument '-b' which wasn't expected, or isn't valid in this context

USAGE:
    structopt [OPTIONS]

For more information try --help

./target/debug/structopt --ba=1
error: Found argument '--ba' which wasn't expected, or isn't valid in this context

USAGE:
    structopt [OPTIONS]

For more information try --help

BA=99 AB=12 ./target/debug/structopt
AConfig { ab: Some("12"), cb: None }
BConfig { ba: Some("99"), db: None }

The only thing I find working in this setup is the env. It works for both AConfig and BConfig

TeXitoi commented 3 years ago

That's normal behavior: the options of the other struct are not recognized by the others, and thus there is usage error.

What you can do is regrouping all your struct in one that group everything, and use flatten on each.

use structopt::StructOpt;

#[derive(StructOpt, Debug)]
struct AConfig {
    #[structopt(short, long, env)]
    pub ab: Option<String>,
    #[structopt(short, long, env)]
    pub cb: Option<String>,
}

#[derive(StructOpt, Debug)]
struct BConfig {
    #[structopt(short, long, env)]
    pub ba: Option<String>,
    #[structopt(short, long, env)]
    pub db: Option<String>,
}

#[derive(StructOpt, Debug)]
struct Opt {
    #[structopt(flatten)]
    a: AConfig,
    #[structopt(flatten)]
    b: BConfig,
}

fn main() {
    let opt = Opt::from_args();
    let b_config = opt.b;
    let a_config = opt.a;

    println!("{:?}", a_config);
    println!("{:?}", b_config);
}
stephenctw commented 3 years ago

Thanks I'll give it a try. Is this behavior documented somewhere? I had a hard time looking for it in documentation or issues. Also I think throwing an explicit error can be very helpful.

TeXitoi commented 2 years ago

Your dedicated use case is not explicitly documented, but flatten is already documented, yes.