These are chat archives for kbknapp/clap-rs

20th
May 2015
Andres Vahter
@andresv
May 20 2015 12:44

Subcommand -h should override other required parameters.
For example here 2 required parameters are defined (samplerate, bandwidth) and 2 subcommands (fm, am).
If I do:

app fm -h

It says that samplerate and bandwidth is not given, however I just wanted to peek which parameters "fm" subcommand wants.
So currently I have to do this to see subcommand help:

app --samplerate 0 --bandwidth 0 fm -h

Workaround is to define samplerate and bandwidth under each subcommand, however it means I have to copy them manually.
So I guess subcommand -h should override other requirements.

use clap::{App, Arg, SubCommand};
use self::DataType::{F32, I16};
use self::Modulation::{FM, AM};

use std::fmt;
use std::process::exit;


#[derive(Clone, Copy)]
pub enum DataType {
    F32,
    I16,
}

impl fmt::Display for DataType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            DataType::F32 => {write!(f, "f32")},
            DataType::I16 => {write!(f, "i16")},
        }
    }
}

pub enum Modulation {
    FM,
    AM,
}

pub struct FmModulationArgs {
    pub deviation: Option<u32>,
}

pub struct CommandArgs {
    pub samplerate: Option<u32>,
    pub resamplerate: Option<u32>,
    pub inputtype: Option<DataType>,
    pub bandwidth: Option<u32>,
    pub modulation: Option<Modulation>,

    pub fmargs: FmModulationArgs,
}

pub fn args() -> CommandArgs {
    let datatypes = ["i16", "f32"];

    let matches = App::new("demod")
                .version(env!("CARGO_PKG_VERSION"))
                .about("Reads IQ data from stdin, filters and demodulates according to parameters and writes demodulated data back to stdout")

                .arg(Arg::with_name("SAMPLERATE")
                    .long("samplerate")
                    .short("s")
                    .help("IQ data input samplerate")
                    .required(true)
                    .takes_value(true))
                .arg(Arg::with_name("BANDWIDTH")
                    .long("bandwidth")
                    .help("bandpass filter bandwidth")
                    .required(true)
                    .takes_value(true))

                .subcommand(SubCommand::new("fm")
                    .about("FM demodulation")

                    .arg(Arg::with_name("DEVIATION")
                        .long("deviation")
                        .help("FM deviation")
                        .required(true)
                        .takes_value(true)))

                .subcommand(SubCommand::new("am")
                    .about("AM demodulation")

                    .arg(Arg::with_name("SOMEARG")
                        .long("arg")
                        .help("AM arg")
                        .required(true)
                        .takes_value(true)))

                .get_matches();


    let mut args = CommandArgs {
                    samplerate : None,
                    resamplerate : None,
                    inputtype : None,
                    bandwidth : None,
                    modulation : None,

                    fmargs : FmModulationArgs {
                        deviation: None,
                    },
                };

    args
}
Kevin K.
@kbknapp
May 20 2015 12:49
Yeah, I actually op
Andres Vahter
@andresv
May 20 2015 12:52

Here I used this copy paste approach: https://github.com/cubehub/doppler/blob/master/doppler/src/usage.rs
However it gets quite messy if there are more than 2 subcommands that share the same required args.

I guess app subcommand -h should actually show also required arguments in usage string, because they are indeed required to run this command.

Kevin K.
@kbknapp
May 20 2015 12:53
Opened an issue on this a few days ago, but its a difficult problem to solve without pre scanning the arguments for a help switch or some sort of lookahead. What makes this hard is each subcommand can override the help switches.
Andres Vahter
@andresv
May 20 2015 12:55
I made a mistake in my last comment, actually it shows required args, they were commented out in my test. So just overriding would be wonderful.
Kevin K.
@kbknapp
May 20 2015 12:55
This is definitely on my radar, but I'll have to play with some different implementations to see if there's an efficient way to do it
Andres Vahter
@andresv
May 20 2015 12:57
I think I have tried 4 different arg parsers. Keep up the good work, clap is the best so far.
Kevin K.
@kbknapp
May 20 2015 13:00
There is a workaround that works, but it's not the best solution. You can have your app use .subcommands_negate_reqs(true) and if your subcommands actually do require app's required arguments, you can simply check for their existance at runtime and print an error/std::process::exit(1) if they aren't there (i.e. the user tried app subcommand <required> instead of app subcommand -h
Thanks! I'll definitely let you know once this particular issue gets solved, because it would also make a utility I'm writing better as well ;)
See issue #124 to follow the progress
Kevin K.
@kbknapp
May 20 2015 15:53
Checkout v0.9.2 on crates.io, this issue should be fixed now ;)
@andresv