How to check if argument is valid in clap v3?

The question if there is a way to check if particular argument is valid for given ArgMatches.

Motivation:
In the clap v2.33.1 I could ask match if it some argument is present or not or ask value of it and this would never panic. Just return false or None. For example:

//v 2.33.1
use clap::{App, Arg};
fn main() {
    let m = App::new("myprog")
        .arg(Arg::with_name("debug").short("d"))
        .get_matches_from(vec!["myprog", "-d"]);
    let _ = match.is_present("release"); // <-- false
}

In the new clap v3, the behavior changed and these calls lead to panic when argument is not present among valid arguments:

// v3.1.11
use clap::{Arg, Command};
fn main() {
    let match = Command::new("myprog")
    .arg(Arg::new("debug")
        .short('d'))
    .get_matches_from(vec![
        "myprog", "-d"
    ]);
    let _ = match.is_present("release"); // <-- panic!
}

I would like to have a way to check if particular argument is correct to use some generic code which works in several applications. Before it was just checking is_present or value_of without bothering about validity of particular argument. So I could refactor it by adding additional check: if valid argument, check if it is present.

For clap v3, you really should be using the derive macros, unless you have special requirements. With the derive macro, it is super simple - have the field as Option<String> and you're good to go.

If you want to check if some set of arguments is valid, you could try it out and see:

let m = Command::new("myprog")
    .arg(Arg::new("debug")
        .short('d'))
    .try_get_matches_from(vec![
        "myprog", "--release"
    ]);
assert!(m.is_err());

I don't think there's any way to check if an argument could possibly be valid. The code above would fail even if --release was valid if debug was a required argument, for example.

Alternatively, and this is a little awkward, you could write a function that captures the panic:

fn is_present(m: &clap::ArgMatches, id: &str) -> bool {
    // this panic hook shuffling is to prevent the error from being printed
    let hook = std::panic::take_hook();
    std::panic::set_hook(Box::new(|_| {}));
    let present = std::panic::catch_unwind(|| m.is_present(id)).unwrap_or_default();
    std::panic::set_hook(hook);
    present
}

Since clap only checks the validity of the argument when debug assertions are on, you could use this function only for that case, and just call is_present otherwise:

#[cfg(debug_assertions)]
fn is_present(m: &clap::ArgMatches, id: &str) -> bool {
    // this panic hook shuffling is to prevent the error from being printed
    let hook = std::panic::take_hook();
    std::panic::set_hook(Box::new(|_| {}));
    let present = std::panic::catch_unwind(|| m.is_present(id)).unwrap_or_default();
    std::panic::set_hook(hook);
    present
}

#[cfg(not(debug_assertions))]
fn is_present(m: &clap::ArgMatches, id: &str) -> bool {
    m.is_present(id)
}

I'm updating existing code base with huge part dedicated to parsing cli. So it is too time consuming to migrate it to derive. Yet I agree that all the new functionality must be written using derive

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.