Why is this branch always true using clap 4? - rust

I would like to have a command line in Rust using clap 4 which allows this:
app --wait
If --wait is present start a function. If is NOT present do nothing.
I'm trying the below code with no luck: the if command.contains_id("wait") is always true! With or without --wait as argument, why?
let command = Command::new("")
.arg(
Arg::new("wait")
.long("wait")
.required(false)
.num_args(0)
.help("Wait..."),
)
.get_matches();
if command.contains_id("wait") {
do_wait().await;
}

From the clap4 documentation for default_value:
Value for the argument when not present.
NOTE: If the user does not use this argument at runtime ArgMatches::contains_id will still return true. If you wish to determine whether the argument was used at runtime or not, consider ArgMatches::value_source.
So it would seem that default_value is taking affect. Why?
I'm not completely certain but it would seem that Arg::new uses defaults from Arg::default. This defaults default_vals to an empty vec, which wouldn't work on anything other than num_args(0), but does work in your case b/c you have no args.
So it looks like your solution is to use value_source instead of contains_id, however you should not use num_args like this.
Instead use .action(ArgAction::SetTrue) like so:
Arg::new("wait")
.long("wait")
.action(ArgAction::SetTrue)
.help("Wait ...")
See the flags section here for more details.

Related

Dynamically generating arguments Clap

I'm trying to figure out how to dynamically generating arguments from input arguments with Clap.
What I'm trying to emulate with Clap is the following python code:
parser = argparse.ArgumentParser()
parser.add_argument("-i", type=str, nargs="*")
(input_args, additional_args) = parser.parse_known_args()
for arg in input_args:
parser.add_argument(f'--{arg}-bar', required=true, type=str)
additional_config = parser.parse_args(additional_args)
So that you can do the following in your command:
./foo.py -i foo bar baz --foo-bar foo --bar-bar bar --baz-bar bar
and have the additional arguments be dynamically generated from the first arguments. Not sure if it's possible to do in Clap but I assumed it was maybe possible due to the Readme stating you could use the builder pattern to dynamically generate arguments[1].
So here is my naive attempt of trying to do this.
use clap::{Arg, App};
fn main() {
let mut app = App::new("foo")
.arg(Arg::new("input")
.short('i')
.value_name("INPUT")
.multiple(true)
.required(true));
let matches = app.get_matches_mut();
let input: Vec<_> = matches.values_of("input").unwrap().collect()
for i in input {
app.arg(Arg::new(&*format!("{}-bar", i)).required(true))
}
}
Which does not obviously having the compiler scream at you for both !format lifetime and app.arg I'm mostly interesting in solving how I could generate new arguments to the app which then could be matched against again. I'm quite new to rust so it's quite possible this is not possible with Clap.
[1] https://github.com/clap-rs/clap
I assumed it was maybe possible due to the Readme stating you could use the builder pattern to dynamically generate arguments[1].
Dynamically generating argument means that, you can .arg with runtime values and it'll work fine (aka the entire CLI doesn't need to be fully defined at compile-time, this distinction doesn't exist in Python as everything is done at runtime).
What you're doing here is significantly more complicated (and specialised, and odd) as you're passing through unknown parameters then re-parsing them.
Now first of all, you literally can't reuse App in clap: most of its methods (very much including get_matches) take self and therefore "consume" the App and return something else, either the original App or a result. Although you can clone the original App before you get_matches it I guess.
But I don't think that's useful here: though I have not tried it should be possible do do what you want using TrailingVarArg: this would collect all trailing arguments into a single positional arg slice (you will probably need AllowLeadingHyphen as well), then you can create a second App with dynamically generated parameters in order to parse that sub-set of arguments (get_matches_from will parse from an iterator rather than the env args, this is useful for testing... or for this exact sort of situations).

NodeJS: how disable auto output after every line in console?

As u can see on the image, after every line, an automatic output appears. I want do disable this. I do not intend to use it as a workaround editor, the problem is that on some functions this output is more than some screens big and is hard to look at the expected result.
The default CLI, IIRC, uses the standard Node REPL, but does not provide a trivial way to customize it.
You could start your own REPL and provide options as described in the REPL docs, specifically focusing on:
eval
ignoreUndefined
The easiest solution is to append something to whatever is returning the long value, e.g., if there's a function called foo that returns an object that spans pages, like:
> foo()
// countless lines of output
Tack on something to the command, like:
> foo(); null
null

Passing arguments to Node.js program

I'm trying to pass 2 mandatory arguments for a Node.js program I'm creating.
I'm using Yargs to do it like this:
const yarg = require("yargs")
.usage("hi")
.options("m", {demandOption: true})
.options("m2", {demandOption: true})
.argv;
This works fine with a slight issue. I want to activate the script like this:
node index.js -m val -m2 val2
It doesn't work and I get an error saying m2 is missing. Only when I add another - before m2 it works, meaning I have to do it like this:
node index.js -m val1 --m2 val2
Is there a way to make it accept the args like I wanted to in the first place?
You can't do what you're asking for with yargs, and arguably you shouldn't. It's easy to see why you can't by looking at what yargs-parser (the module yargs uses to parse your arguments) returns for the different argument styles you mentioned:
console.log(yargsParser(['-m', 'A']));
console.log(yargsParser(['-m2', 'B']));
console.log(yargsParser(['--m2', 'C']));
<script src="https://bundle.run/yargs-parser#11.1.1"></script>
As you can see:
-m A is parsed as option m with value A.
-m2 B is parsed as option m with value 2 and, separately, array parameter B.
--m2 C is parsed as option m2 with value C.
There is not a way to make the parser behave differently. This is with good reason: Most command line tools (Windows notwithstanding) behave this way.
By decades-old convention, "long" options use two dashes followed by a human-readable name, like --max-count. "Short" options, which are often aliases for long ones, use a single dash followed by a single character, like -m, and—this is the crucial bit—if the option takes a value, the space between the option and the value can be omitted. This is why when yargs sees -m2, it assumes 2 is the value for the m option.
If you want to use yargs (and not confuse command line-savvy users) you'll you need to change your options to either 1. --m and --m2 or, 2. -m and (for example) -n.

python argparse add_argument_group required

In this question
argparse: require either of two arguments
I find a reference to the solution I want, but it isn't right.
I need at least 1 of 2 options to be present, option1, option2 or both...
The add_argument_group function doesn't have a required argument.
The add_mutually_exclusive function has it, but it forces me to choose between the 2 options, which is not what I want.
rds,
argument_group just controls the help display. It does not affect the parsing or check for errors. mutually_exclusive_group affects usage display and tests for occurrence, but as you note, its logic is not what you want.
There is a Python bug issue requesting some form of nested 'inclusive' group. But a general form that allows nesting and all versions of and/or/xor logic is not a trivial addition.
I think your simplest solution is to test the args after parsing. If there is a wrong mix of defaults, then raise an error.
Assuming the default for both arguments is None:
if args.option1 is None and args.option2 is None:
parser.error('at least one of option1 and option2 is required')
What would be meaningful usage line? required mutually exclusive' uses(opt1 | opt2).(opt1 & opt2)might indicate that both are required. Your case is anon-exclusive or`
usage: PROG [-h] (--opt1 OPT1 ? --opt2 OPT2)

Implementing a switch/command like -h

It seems like it is possible to create optional arguments with argparse that "override" otherwise required arguments (be it positional or required options).
A example would be the --help/-h switch, which just displays the help and exit. Now I need to implement behaviour exactly like this ; I need a switch/option that can be used without using any of the otherwise required arguments.
Take a look at how the 'help' switch is implemented:
self.add_argument(
default_prefix+'h', default_prefix*2+'help',
action='help', default=SUPPRESS,
help=_('show this help message and exit'))
You need to provide an action that short circuits argument processing; the 'help' action does this by exiting the program.

Resources