-
Notifications
You must be signed in to change notification settings - Fork 152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expose related and mutually exclusive arguments via struct / enum? #104
Comments
First thought on the proposition, that is interesting, but not yet mature enough:
Maybe something like that: #[derive(StructOpt, Debug)]
#[structopt(name = "staging")]
pub struct Arguments {
#[structopt(short = "i", long = "input", name = "STAGE", parse(from_os_str))]
pub input_stage: path::PathBuf,
#[structopt(short = "d", long = "data", name = "DATA_DIR", parse(from_os_str))]
pub data_dir: Vec<path::PathBuf>,
#[structopt(group)]
pub flags: Flags,
#[structopt(short = "v", long = "verbose", parse(from_occurrences))]
pub verbosity: u8,
}
#[derive(StructOptGroup, Debug)]
pub enum Flags
{
// no annotation? an annotation?
Output(Output), // <- How to handle that with clap?
#[structopt(long = "completion", name = "OUT", parse(from_os_str))]
Completions(PathBuf),
#[structopt(long = "dump-config")]
DumpConfig,
#[structopt(long = "dump-data")]
DumpData,
}
#[derive(StructOpt, Debug)]
pub struct Output {
#[structopt(short = "o", long = "output", name = "OUT", parse(from_os_str))]
pub dir: path::PathBuf,
#[structopt(long = "format",
raw(possible_values = "&Format::variants()", case_insensitive = "true"),
raw(default_value = "DEFAULT_FORMAT"))]
pub format: Format,
#[structopt(short = "n", long = "dry-run")]
pub dry_run: bool,
} For this particular need, subcommands seem more appropriate, and is working today. But that may be interesting for other cases. |
Yeah, it was more for illustrative purposes of the wider idea. As you say, even better if I can directly annotate an enum variant to say that its just a flag.
Except that isn't what I was trying to point out. This is supposed to take in the
I'd considered that but its pretty common for CLIs to not go the subommand route for non-routine alternative behaviors of the application
At that point, it felt awkward to create subcommands just for my debugging, so I went ahead and made my dump state flags do similar. |
Sorry, I mean Completions
Is it possible to express such constraints in pure clap? because if that's not possible, we can't do it in structopt. I'm also afraid that this machinery will be too complicated to be understood by the users. Finding a clear, flexible and usable interface is a challenge here. |
.group(ArgGroup::with_name("output_struct")
.multiple(true)
.args(&["dir", "format", "dry_run"])
.conflicts_with_all(&["completions", "dump"]))
I'll admit, this one was more aspirational. One option is to iterate on the fields and, if there are required fields, to mark the optional fields as depending on the required fields.
Are you referring to the developer or to the user? For the user, I think its understandable that some arguments only work in some settings. I normally visually group these in the help (with python's argparse) but haven't played too much with doing that with clap yet. For the developer, I'd say they are. These are the things I'm intuitively trying to do but can't. Instead I'm having to drop down into |
Yeah, I mean the user of StructOpt, the developer of the cli. |
I wanted something very similar to these proposals, and was actually slightly surprised when nothing like them was available. I'd imagine that you could have something like: #[derive(StructOpt)]
#[structopt(flag_group)]
enum Mode {
#[structopt(short = "s", long = "stack")]
Stack,
#[structopt(short = "q", long = "queue")]
Queue,
}
#[derive(StructOpt)]
#[structopt(name = "letter")]
struct Opt {
#[structopt(from_flag_group)]
mode: Mode,
} and then you can either use |
One random thought I had is that I've noticed some remarkable similarities between structopt's and serde's data models. It seems like subcommands are equivalent to serde's externally tagged enums, while mutually exclusive options, if modeled through enums, are similar to untagged enums. It may be worth aligning with serde's design in this respect. One thing it suggests is the possibility for internally tagged enums, which may be reflected in the CLI as e.g. |
I just skimmed this because I want this feature. IIUC in #104 (comment) it is possible in For my current case, I simply want exclusive options: either |
@nathan-at-least https://github.com/TeXitoi/structopt/blob/master/examples/group.rs is almost what you want. And what you want is: use structopt::StructOpt;
#[derive(Debug, StructOpt)]
struct Opt {
#[structopt(short, long, group = "verbosity")]
verbose: bool,
#[structopt(short, long, group = "verbosity")]
quiet: bool,
#[structopt(short, long)]
name: Option<String>,
}
fn main() {
let opt = Opt::from_args();
println!("{:?}", opt);
} |
This is an enhancement, and structopt is now feature frozen. |
I'm working on a CLI that currently looks like
I'm looking at adding some options that are mutually exclusive with
Arguments::output
.This gave me the ideas:
Option<Output>
on an entire flattened struct to say the arguments require each otherenum
to define mutually exclusive flags. For example:The text was updated successfully, but these errors were encountered: