Skip to content
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

Having two unnamed arguments and making the first optional #636

Closed
TomasHubelbauer opened this issue Aug 27, 2016 · 10 comments
Closed

Having two unnamed arguments and making the first optional #636

TomasHubelbauer opened this issue Aug 27, 2016 · 10 comments
Milestone

Comments

@TomasHubelbauer
Copy link

Forgive me if Issues are not meant for questions, neither the contributing guidelines nor the ReadMe say so, so please allow me to ask a question here. I'd like to make clap parse the following:

  • todo TEST "Test things" means CODE is TEST, TEXT is Test things.
  • todo "Test things" means CODE is not set, TEXT is Test things.
  • todo TEST "Test things" -m means CODE is TEST, TEXT is Test things, marked is true.
    I've looked over the examples but am having trouble with making the first argument optional.
fn main() {
    let matches = App::new("todo").version("1.0").author("Tomas Hubelbauer").about("")
        .arg(Arg::with_name("code").required(false).index(1))
        .arg(Arg::with_name("text").index(2))
        .get_matches();
    println!("Hello, world!");
    println!("Code: {} Text: {}", matches.value_of("code").unwrap_or("NO-CODE"), matches.value_of("text").unwrap_or("NO-TEXT"));
}
// cargo run -- hello
// Code: hello Text: NO-TEXT // Should be Code: NO-CODE Text: hello
// cargo run -- hello world
// Code: hello Text: world // Is OK.
fn main() {
    let matches = App::new("todo").version("1.0").author("Tomas Hubelbauer").about("")
        .arg(Arg::with_name("code").required(false).index(2))
        .arg(Arg::with_name("text").index(1))
        .get_matches();
    println!("Hello, world!");
    println!("Code: {} Text: {}", matches.value_of("code").unwrap_or("NO-CODE"), matches.value_of("text").unwrap_or("NO-TEXT"));
}
// cargo run -- hello
// Code: NO-CODE Text: hello // Very cool!
// cargo run -- hello world
// Code: world Text: hello // Oh noes, should be Code: hello Text: world

Is there a way to do this?

@kbknapp
Copy link
Member

kbknapp commented Aug 27, 2016

Issues are fine for questions 😄

Currently you can't exactly do that, but depending on your actual use case there may be a way to make it work. The reason I say you can't exactly do that, is that unnamed arguments are purely index based, clap doesn't know which value is which, simply first come first served.

But in order to prevent an XY problem, could you tell me a little about the actual use case and what you're trying to model? There may be a better way, or something that clap does support in a slightly different manner.

Thanks for taking the time to do the detailed write up! 👍

@TomasHubelbauer
Copy link
Author

TomasHubelbauer commented Aug 28, 2016

Oh I can definitely make this work simply by making TEXT the first unnamed argument and CODE the second, optional one. This is what the second code snippet does. I'm fine with that, too, it's not a big deal to have the arguments reverted, but the reason I wanted them to be the way I initially described is that it feels more ergonomic for my use case.

I'm new to Rust and I'm trying to create an app for managing todo files (I use them for projects I work alone at because I dislike having issues be out-of-the-band, unversioned in relation to the source code) and I'd like to be able to create a todo just by invoking todo "Have a nice day" - this would make a todo titled "Have a nice day". Since I also intend to offer capabilities for attaching files to todoes I figured it would be nice to be able to set explicit code for todos so one can refer to them without grepping. If not provided, it would be either sequential number rising or first letters of the title words combined or something like that. Being able to do todo FixNav "Fix the second menu link style - icon and add menu links for ToS and privacy policy" feels nicer than todo "Fix the second menu link style - icon and add menu links for ToS and privacy policy" FixNav. There's not much more to it. :)

I'm closing the issue for now. I believe code-wise this could be changed when no multiple arguments are present. You could probably be able to detect the case I'm describing. But it's super niche, so it's not really necessary. Thank you!

@joshtriplett
Copy link
Contributor

I'd like this feature as well, and in my case I can't just reorder the arguments. Could you please reopen this?

My use case: I'm adding subcommands to git-series similar to "cp" and "mv", which take "source" and "dest" positional arguments in that order; they need to come in that order to follow well-established convention (cp source dest). I also need to make "source" optional, because it has a default (the current series). So, I'd like to make source optional and dest mandatory. In this case, if clap sees one positional argument, it should go in dest, but two positional arguments should go in source and dest.

Does that seem reasonable?

@TomasHubelbauer
Copy link
Author

Reopening for @joshtriplett.

@kbknapp
Copy link
Member

kbknapp commented Nov 2, 2016

@joshtriplett with the new 2.17.0 allowing things like mv <files>... <target> does that help this issue, or is it something different?

@kbknapp
Copy link
Member

kbknapp commented Dec 10, 2016

@joshtriplett I could special case this if there are only two positional arguments, but my worry is what if there are 3, or 4? It's also hard to determine at that point if the user input the the first arg correctly, but accidently left off the second, but then clap interprets it differnetly.

$ prog current_series

Gets interpretted as src=current_series(default), dest=current_series

Which may not be a huge deal in your case, but imagine a hypothetical mv

$ mv src

Gets interpretted as src=(somedefault file/folder), dest=src which could be catostrophic.

@joshtriplett
Copy link
Contributor

joshtriplett commented Dec 12, 2016

@kbknapp The version from 2.17 almost works, but I don't want to allow multiple arguments (or display it as ...). Could you also allow an optional second-to-last positional argument, which would have the same effect (0-or-1 and 0-or-more seem equivalent here in terms of unambiguous parsing)?

@kbknapp
Copy link
Member

kbknapp commented Jan 3, 2017

Since there's now a form of look-ahead for the <src>... <dest> type positionals, I'd imagine it'd also work for [src] <dest>, but I'm unsure how to inform clap of such.

@joshtriplett and @TomasHubelbauer in both cases is the second positional always required? If that's the case I could implement this, because saying, "the final positional is required, but the one prior is not" is the information clap needs to special case. Otherwise I'd have to add something like an Arg::secondary_index which would be more difficult to explain/document without a decent round of bike-shedding.

@joshtriplett
Copy link
Contributor

@kbknapp Yes, the case I have always requires the second positional argument.

@kbknapp kbknapp added this to the 2.20.0 milestone Jan 4, 2017
@kbknapp
Copy link
Member

kbknapp commented Jan 4, 2017

I have this implemented and it's working. I have one final issue to implement, then I'll put out 2.20 on crates.io

kbknapp added a commit that referenced this issue Jan 4, 2017
@homu homu closed this as completed in 6edde30 Jan 6, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants