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

default_value and Option? #123

Closed
lnicola opened this issue Jul 9, 2018 · 12 comments · Fixed by #312
Closed

default_value and Option? #123

lnicola opened this issue Jul 9, 2018 · 12 comments · Fixed by #312
Labels
enhancement We would love to have this feature! Feel free to supply a PR

Comments

@lnicola
Copy link

lnicola commented Jul 9, 2018

Right now, they are incompatible, but I'm not sure that's right. For example, I wanted something like my-app --db-path [DB], where there's a default database path if not set.

Is there another way to achieve that?

@TeXitoi
Copy link
Owner

TeXitoi commented Jul 9, 2018

Can you give example of command line and corresponding struct, because I don't understand what you expect.

@lnicola
Copy link
Author

lnicola commented Jul 9, 2018

#[derive(Clone, Debug, StructOpt)]
pub struct Options {
    #[structopt(long = "db", help = "Database path", default_value = "my-app.db")]
    pub db: Option<String>,
}

Example usage:

$ my-app # db is None
$ my-app --db # db is Some("my-app.db")
$ my-app --db foo.db # db is Some("foo.db")

@TeXitoi
Copy link
Owner

TeXitoi commented Jul 9, 2018

In clap, an argument takes value or not: https://docs.rs/clap/2.32.0/clap/struct.Arg.html#method.takes_value

Thus, I don't think that's possible to have an argument that can optionally take value, That's why default_value is prohibited in Option, as it will always have a value, and thus will always be Some(a).

@lnicola
Copy link
Author

lnicola commented Jul 9, 2018

I suppose default_value and occurrences_of could be used in this case. The latter will return zero if the argument wasn't passed at all.

@TeXitoi
Copy link
Owner

TeXitoi commented Jul 10, 2018

I can't find any way to do it using clap. If you can show me a working program doing what you want with clap, I'll try to allow using the feature in structopt.

@lnicola
Copy link
Author

lnicola commented Jul 10, 2018

extern crate clap;

use clap::{App, Arg, ArgMatches};

fn get_optional_value<'a, 'b>(m: &'a ArgMatches, name: &'b str) -> Option<&'a str> {
    if m.occurrences_of(name) == 0 {
        None
    } else {
        m.value_of(name)
    }
}

fn main() {
    let app = App::new("my-app").arg(Arg::with_name("db").long("db").default_value("my-app.db"));

    let m = app.clone().get_matches_from(vec!["my-app", "--db"]);
    assert!(get_optional_value(&m, "db") == Some("my-app.db"));

    let m = app
        .clone()
        .get_matches_from(vec!["my-app", "--db", "foo.db"]);
    assert!(get_optional_value(&m, "db") == Some("foo.db"));

    let m = app.clone().get_matches_from(vec!["my-app"]);
    assert!(get_optional_value(&m, "db") == None);
}

@TeXitoi
Copy link
Owner

TeXitoi commented Jul 10, 2018

I'll try to see how I can do it cleanly in structopt. Thanks for the example.

@TeXitoi TeXitoi added the enhancement We would love to have this feature! Feel free to supply a PR label Jul 10, 2018
@sphynx
Copy link
Contributor

sphynx commented May 25, 2019

Another way of achieving optional value of the long argument in clap is to use Arg::min_values(0) as described here: clap-rs/clap#892

Then we can distinguish between say --help and --help topic by checking matches.is_present("help") and pattern matching matches.value_of("help") against Some (as demonstrated in the code from the issue linked above).

I am now trying to describe this behaviour with structopt, but can't find a way :( It sets the field of the datatype to None in both cases when it's omitted and when its value is empty, so I can't tell the difference.

@TeXitoi
Copy link
Owner

TeXitoi commented May 27, 2019

@sphynx That's a quite different topic. You'd want support for Option<Option<T>> or Option<Vec<T>>. If that's what you're looking for, then you can create an issue about this.

As a workaround, you can do the parsing "by hand":

let app = Opt::clap();
let matches = app.get_matches();
// do something special to retrive the needed information
let opt = Opt::from_matches(&matches);

@sphynx
Copy link
Contributor

sphynx commented May 27, 2019

@TeXitoi Thanks for your reply! As suggested, I've created a new issue: #188

I think I'll either have to use the workaround or restructure the arguments differently for now.

@andronat
Copy link

Hello, could someone post how the above example can be currently implemented in structopt? I don't really get how default values with Option can be combined. I have a similar use case that I would like to accommodate.

Just pasting the example again here:

$ my-app # db is None
$ my-app --db # db is Some("my-app.db")
$ my-app --db foo.db # db is Some("foo.db")

@epage
Copy link
Contributor

epage commented Nov 22, 2022

I've not checked but hopefully it works if you use Option<Option> for the type

Though, keep in mind that structopt is deprecated in favor of clap v3+ supporting derives directly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement We would love to have this feature! Feel free to supply a PR
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants