How to extract config value from env variable with clap derive? - rust

I cannot find in the docs how to set this up. Let's say I have
#[derive(Parser, Debug)]
pub struct Opts {
#[clap(long)]
dry_run: bool,
}
What do I need to do to get dry_run from APP_DRY_RUN env variable?

You have to enable the env feature:
Cargo.toml
...
clap = { version = "...", features = ["env"] }
Then you have to add the env clap derive option, which will by default read from an ALLCAPS recased env var:
#[derive(Parser, Debug)]
pub struct Opts {
#[clap(long, env)]
dry_run: bool, // --dry-run or DRY_RUN env var
}

Related

Retrieve the version of a dependency in build.rs script

I would like to expose in my REPL (built on top of Boa) the version of the js engine:
I'm trying to use a build.rs file for that:
use std::env;
fn main() {
println!("cargo:rustc-env=BOA_VERSION={}", "0.16.0");
}
And somewhere in my main.rs:
context.register_global_property("BOA_VERSION", env!("BOA_VERSION"), Attribute::all());
Obviously I have hardcoded the version of the crate but wondering if there is a programmatically way to get the version.
If all you need is to get access to the version information in your code, you can skip making a build.rs file for it. Instead, we can use the serde and toml crates to parse your Cargo.toml file directly. Below is a sample of how that might look.
Cargo.toml:
[package]
name = "dependency-information"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde = { version = "1.0.152", features = ["derive"] }
toml = "0.7.0"
main.rs:
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
enum DependencyValue {
String(String),
Object {
version: String,
features: Vec<String>,
},
}
#[derive(Debug, Serialize, Deserialize)]
struct CargoToml {
dependencies: HashMap<String, DependencyValue>,
}
fn main() {
let cargo_toml_raw = include_str!("../Cargo.toml");
let cargo_toml: CargoToml = toml::from_str(cargo_toml_raw).unwrap();
println!("{cargo_toml:#?}");
}
And if we run it, we get this output:
CargoToml {
dependencies: {
"toml": String(
"0.7.0",
),
"serde": Object {
version: "1.0.152",
features: [
"derive",
],
},
},
}
Note that you probably would want to include the Cargo.toml file dynamically instead of using include_str!. I used include_str! for the sake of the example.

Using clap-derive with two groups of arguments

Given this Clap argument struct, I would like to allow users either to supply the config param, or any of the other params that are being flattened from the sub-structs. The connection param would then be required, but other flattened params are optional. Note that the watch param is allowed in both cases. How can this be done in Clap-derive v4+?
#[derive(Parser, Debug)]
#[command(about, version)]
pub struct Args {
connection: Vec<String>,
#[arg(short)]
watch: bool,
#[arg(short)]
config: Option<PathBuf>,
#[command(flatten)]
srv: SrvArgs,
#[command(flatten)]
pg: PgArgs,
}
#[derive(clap::Args, Debug)]
#[command(about, version)]
pub struct SrvArgs {
#[arg(short)]
pub keep: Option<usize>,
}
#[derive(clap::Args, Debug)]
#[command(about, version)]
pub struct PgArgs {
#[arg(short)]
pub pool: Option<i32>,
}
Allowed usages:
-c filename [-w]
[-p 10] [-k 5] [-w] connection [...connection]
I tried to do it by moving all fields except config and watch to another struct and using the #[arg(group = "cfg")] on both, but it doesn't work when the field also has the #[command(flatten)] attribute.

How to create a StructOpt command where all subcomands are optional

I want to arrange subcommands like:
mycmd status : Prints a short status - NOT WORKING
mycmd status full : Prints verbose status - OK
mycmd status dump : Dumps full debug status to a file - OK
I'm unable to achieve the simple mycmd status because StructOpt believes I am missing a required subcommand (sub-subcommand?) and prints the usage. The docs indicate that I need to use the Option<> trait somehow, but I cannot figure out how in this case.
I have something very like the following:
main.rs
use structopt::StructOpt;
// ... other use cmds ...
#[derive(Debug, StructOpt)]
#[structopt(
name = "mycmd",
about = "A utility to do stuff."
)]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
#[structopt(setting = structopt::clap::AppSettings::SubcommandRequired)]
struct Opts {
#[structopt(short = "v", parse(from_occurrences))]
/// Increase message verbosity
verbosity: usize,
#[structopt(subcommand)]
cmd: Tool,
}
#[derive(Debug, StructOpt)]
enum Tool {
#[structopt(name = "dofoo")]
DoFoo(dofoo::Command),
#[structopt(name = "status")]
Status(status::Command),
}
status.rs
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
#[structopt(name = "status", about = "Get the status of stuff.")]
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
#[structopt(max_term_width = 80)]
pub enum Command {
#[structopt(name = "full")]
/// Print full (i.e. verbose) status
Full {},
#[structopt(name = "dump")]
/// Creates a zipped dump of the full system status to a file
Dump {
#[structopt(short = "o", long = "out", value_name = "FILE", parse(from_os_str))]
/// Filename of the output file.
out_fname: PathBuf,
},
}
impl Command {
pub fn execute(self) -> Result<()> {
match self {
Command::Full {} => cmd_do_verbose_print(),
Command::Dump { out_fname } => cmd_do_file_dump(out_fname),
// TODO: Bad. This is dead code.
_ => cmd_do_print(),
}
}
}
The docs indicate that I need to use the Option<> trait somehow, but I cannot figure out how in this case.
Option is not a trait, and there's an example right there in the documentation's index at "optional subcommand".
It's just doing the same thing as your Opts but making the [structopt(subcommand)] member an Option<T> instead of a T:
#[derive(Debug, StructOpt)]
pub struct Command {
#[structopt(subcommand)]
cmd: Option<Cmd>,
}
#[derive(Debug, StructOpt)]
pub enum Cmd {
/// Print full (i.e. verbose) status
Full {},
...
I have something very like the following:
An actual runnable reproduction case is useful when you have an issue...

The trait `std::convert::From<cli::Opts>` is not implemented

I try to create a simple application parsing command line arguments using clap library and converting them to a Config custom structure. I implemented From trait for my structure, however, when I try to call from function, I receive the following error:
the trait bound `minimal_example::Config: std::convert::From<cli::Opts>` is not satisfied
the following implementations were found:
<minimal_example::Config as std::convert::From<minimal_example::cli::Opts>>
required by `std::convert::From::from`
Here is the code:
main.rs:
mod cli;
use clap::Clap;
use minimal_example::Config;
fn main() {
println!("Hello, world!");
let opts = cli::Opts::parse();
let config = Config::from(opts);
}
cli.rs:
use clap::{Clap, crate_version};
/// This doc string acts as a help message when the user runs '--help'
/// as do all doc strings on fields
#[derive(Clap)]
#[clap(version = crate_version!(), author = "Yury")]
pub struct Opts {
/// Simple option
pub opt: String,
}
lib.rs:
mod cli;
pub struct Config {
pub opt: String,
}
impl From<cli::Opts> for Config {
fn from(opts: cli::Opts) -> Self {
Config {
opt: opts.opt,
}
}
}
cargo.toml:
[package]
name = "minimal_example"
version = "0.1.0"
authors = ["Yury"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = {version="3.0.0-beta.2", features=["wrap_help"]}
What am I doing wrong?
You have added mod cli to both lib.rs and main.rs.
They are different from the standpoint of each other.
Rust modules confusion when there is main.rs and lib.rs
may help in understanding that.
That's what the error says. It's satisfied for std::convert::From<minimal_example::cli::Opts> but not for std::convert::From<cli::Opts>.
A simple fix:
main.rs
mod cli;
use clap::Clap;
use minimal_example::Config;
impl From<cli::Opts> for Config {
fn from(opts: cli::Opts) -> Self {
Config {
opt: opts.opt,
}
}
}
fn main() {
println!("Hello, world!");
let opts = cli::Opts::parse();
let config = Config::from(opts);
}
Now std::convert::From<cli::Opts> is implemented for Config.
How you actually want to place all this depends on your package architecture.

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