How do I allow uppercase flags in Clap? - rust

For my RUST program, I am using Clap to parse my command line arguments. I want to let users input flags like so:
my_program -L testfile.txt
I set up my struct like so:
struct Args {
#[arg(short)]
L: bool,
#[arg(short)]
s: bool,
name: String,
}
When I test out my program, it gives me this error:
error: Found argument '-L' which wasn't expected, or isn't valid in this context.
I can't use ignore_case() either, since this is a flag and doesn't take a value.
Does anyone know how to solve this problem?

From Arg Attributes in the clap derive documentation:
short [= <char>]: Arg::short
When not present: no short set
Without <char>: defaults to first character in the case-converted field name
use clap::Parser;
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
#[arg(short = 'L')]
L: bool,
#[arg(short)]
s: bool,
name: String,
}
fn main() {
let args = Cli::parse();
}
Built executable help:
Usage: xxxxxx [OPTIONS] <NAME>
Arguments:
<NAME>
Options:
-L
-s
-h, --help Print help information
-V, --version Print version information

Related

How do I indicate that an argument must be passed?

I'm writing a CLI tool using Clap. Usage should look like this:
$ my_tool command argument
The argument is necessary for the command to work. This is the code I have at the moment:
use::clap::{Parser, Subcommand};
#[derive(Parser)]
#[clap(author, version, about)]
struct Cli {
#[clap(subcommand)]
subcommand: Subcommands,
}
#[derive(Subcommand)]
enum Subcommands {
Command {
#[clap(value_parser)]
argument: Vec<String>,
}
// --snip--
}
fn main() {
let _cli = Cli::parse();
// --snip--
}
I get this when trying to use the tool:
~/my_tool$ cargo run -- command
Compiling my_tool v0.1.0 (/home/derch/my_tool)
Finished dev [unoptimized + debuginfo] target(s) in 0.78s
Running `target/debug/my_tool command`
~/my_tool$
I was expecting an error message because I didn't provide an argument after the command. How do I make the program to require the argument?
Because this is a Vec, by default it allows zero values. You can fix that by making it explicitly required:
#[derive(Subcommand)]
enum Subcommands {
Command {
#[clap(value_parser, required = true)]
argument: Vec<String>,
}
// --snip--
}

How to verify that the command line arguments can be found in a given array?

I'm writing a CLI program using clap. Usage will be:
$ my_tool command word1 word2 word3
I have a hard-coded array that contains a set of accepted words (1):
pub const WORDS: [&str; 2048] = ["abandon", "ability", "able", "about", "above", ...]
I want to check that all the words (word1, word2, word3) provided by the user can be found in the array because I'll be looking for the indexes of the words in a function.
What I'm doing is:
Collecting the words in a vector:
use clap::{Parser, Subcommand};
// --snip--
#[derive(Parser)]
#[clap(author, version, about)]
pub struct Cli {
/// Operation to perform
#[clap(subcommand)]
pub command: Subcommands,
}
#[derive(Subcommand)]
pub enum Subcommands {
/// Splits seedphrase
Split {
/// Seedprhase to split
#[clap(value_parser, required(true))]
seedphrase: Vec<String>,
},
// --snip--
And propagating an error from the function that looks for the indexes (using anyhow) (2):
pub fn words_to_indexes(words: Vec<&str>) -> Result<Vec<i32>, anyhow::Error> {
let mut indexes: Vec<i32> = Vec::new();
for word in words {
indexes.push(
WORDS
.iter()
.position(|x| x == &word)
.context(format!("Word '{}' is not on the BIP39 wordlist", word))?
as i32,
);
}
Ok(indexes)
}
This error is propagated all the way up to the main function so anyhow takes care of printing it with context.
This solution works, but I realized that as it's only caused by bad arguments from the user, it would make more sense to do this check right at the beginning of the program. That is, indicating clap to verify that the words provided are in the wordlist. Is there a way to do this kind of check with clap? And if there is, How?
Another solution I thought of is to simply manually verify arguments at the beginning of the program. That is, in the main function right after calling Cli::parse() or in the run function that calls all the other necessary logic functions in the program (3). However, I realized that if this can be done "natively" with clap it should be simpler and have already defined "standard" prints.
(1) It is the BIP39 wordlist.
(2) Another function converts the Vec<String> to a Vec<&str>, don't worry about that.
(3) The organization of the project is based on chapter 12 of the book.
You can use clap's PossibleValuesParser for this:
use clap::{builder::PossibleValuesParser, Parser, Subcommand};
const WORDS: [&str; 3] = ["crypto", "is", "garbage"];
#[derive(Parser)]
#[clap(author, version, about)]
pub struct Cli {
/// Operation to perform
#[clap(subcommand)]
pub command: Command,
}
#[derive(Subcommand)]
pub enum Command {
/// Splits seedphrase
Split {
/// Seedprhase to split
#[clap(value_parser = PossibleValuesParser::new(WORDS), required(true))]
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// tell clap to only allow the words in WORDS to appear in seedphrase.
seedphrase: Vec<String>,
},
}

How can I access a runtime-defined variable in stuctopt definitions?

I would like to be able to use the value of a variable (or better yet, the return of a function(arg)) as the about string for a CLI program defined using structopt. The end goal is a fully localized CLI that detects the system language or an ENV var and loads up localized strings that get baked into the --help message, etc.
By default it uses the documentation comment:
/// My about string
#[derive(StructOpt)]
struct Cli {}
I've found I can pass a manually entered string instead:
#[derive(StructOpt)]
#[structopt(about = "My about string")]
struct Cli {}
That's one step closer, but what I really want to do is pass a variable:
let about: &str = "My about string";
#[derive(StructOpt)]
#[structopt(about = var!(about))]
struct Cli {}
That last block is pseudo-code because I don't know what syntax to use to achieve this. In the end, I'll need more than just a single string slice, but I figured that was a place to start.
How can I pass through values like this to structopt? Do I need to access the underlying clap interfaces somehow?
StructOpt just adds a derive macro and a corresponding trait over clap. The clap crate has a function to set the about message at runtime, so we just need to add that. If we look at how from_args works we can see that it creates the clap App struct before turning it into the user-defined struct.
Thus, to do what you want:
use structopt::StructOpt;
fn get_localized_about() -> &'static str {
"localized string"
}
#[derive(Debug, StructOpt)]
struct Foo {}
fn main() {
let foo = Foo::from_clap(&Foo::clap().about(get_localized_about()).get_matches());
println!("{:#?}", foo);
}

How to get the command behind std::process::Command in Rust?

I'm passing around instances of std::process::Command. Before executing a command I'd like to log the entire command. For instance, if I'm given a command instance that has been constructed like that:
let command = Command::new("sh")
.arg("-c")
.arg("echo hello")
I would like to write a log message like:
Executing command: 'sh' '-c' 'echo hello'
The API looks quite limited though. Is there a way to accomplish that?
Debug is implemented for Command.
use std::process::Command;
fn main() {
let mut command = Command::new("ls");
command.arg("-l");
println!("{:?}", command);
}
Output: "ls" "-l"
You would like to get access to private fields in the command struct. Private fields are not accessible by design.
However, when a Debug trait has been implemented for the struct, the private members are 'printed' using the {:?} format option.
To access those private members programmatically, use the format!() macro. This returns a std::String and accepts the {:?} formatting option. This only works because the Debug trait has been implemented for Command.
fn main() {
let mut command = Command::new("ls");
command.arg("-l");
let command_string: String = std::format!("{:?}", command);
// Print the command_string to standard output
println!("cmd: {}", command_string);
// split the command string accordinig to whitespace
let command_parts = command_string.split(' ');
// print the individual parts of the command_string
for (index, part) in command_parts.enumerate() {
println!("part[{}]: {}", index, part);
}
}
Output:
$> test_prog
cmd: "ls" "-l"
part[0]: "ls"
part[1]: "-l"
$>

How to make an argument optional based on the presence of another one in structopt?

I have a command line tool that has two possible arguments:
--version (which will print out the version number and quit)
--out (which is the path to some output file into which magic is going to be poured).
If a user passes --version I do not care about --out since I print the version and I am done but if they do not pass --version I want --out to be required.
This is what I have but I wonder if there's any way to do this using only structopt?
It seems like I may end up needing to make all my arguments optional and do all the validation myself...
#![feature(custom_attribute)]
#[macro_use]
extern crate structopt;
use structopt::StructOpt;
use std::path::PathBuf;
#[derive(Debug, StructOpt)]
#[structopt(name = "structopt_playing", about = "Just playing")]
struct Opt {
#[structopt(short = "v", long = "version")]
version: bool,
#[structopt(short = "o", long = "out", parse(from_os_str))]
output_file: Option<PathBuf>,
}
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
fn main() {
let opt = Opt::from_args();
if opt.version {
println!("Version: {}", VERSION);
return;
}
if !opt.output_file.is_some() {
println!("Oh, now I feel like I'm alone...you need to pass --out");
return;
}
println!("Now I'm going to need to do something with {:?}",
opt.output_file.unwrap());
}
Use required_unless:
#[derive(Debug, StructOpt)]
#[structopt(name = "structopt_playing", about = "Just playing")]
struct Opt {
#[structopt(short = "v", long = "version")]
version: bool,
#[structopt(short = "o", long = "out", parse(from_os_str), required_unless = "version")]
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
output_file: Option<PathBuf>,
}
$ ./target/debug/stropt
error: The following required arguments were not provided:
--out <output_file>
USAGE:
stropt --out <output_file>
For more information try --help
$ ./target/debug/stropt --out hello
Now I'm going to need to do something with "hello"
$ ./target/debug/stropt --version
Version: 0.1.0
There are a large number of related configuration options provided by Clap:
required_unless
required_unless_all
required_unless_one
conflicts_with
conflicts_with_all
requires
requires_if
requires_ifs
required_if
required_ifs
requires_all
A side note: you don't need to use #![feature(custom_attribute)] in this code at all.
I would use subcommand:
#![feature(custom_attribute)]
#[macro_use] extern crate structopt;
use structopt::StructOpt;
use std::path::PathBuf;
#[derive(StructOpt)]
#[structopt(name = "test")]
enum Git {
#[structopt(name = "--version")]
Version,
#[structopt(name = "--out")]
Fetch(PathBuf),
}

Resources