How to refer to the program name in help strings? - rust

In my CLI program the usage examples are provided as part of the help message. Using the clap derive interface I can do the following
#[derive(Parser, Debug, Default)]
#[clap( after_help = "EXAMPLES:\n $ foo abc.txt")]
pub struct CmdLine {...}
The program name foo is hard coded in the literal string above.
How can I avoid hard-coding the program name and get it dynamically; for example, from std::env::args[0] or clap::App:get_bin_name() ?

clap provides a macro called crate_name! that will take the name from your cargo.toml.
For example, suppose you have this in your cargo.toml.
[package]
name = "myapp"
description = "myapp description"
version = "0.1.0"
edition = "2021"
authors = [ "John Doe" ]
Then, in your application, you can fetch these values using the macros, like this:
let matches = Command::new(clap::crate_name!())
.version(clap::crate_version!())
.author(clap::crate_authors!())
.about(clap::crate_description!())
//
// abbreviated
//
The section below is appended to respond to the original poster's specific question. See the comments below for context. Also, including some learnings as well.
Appended per the discussion in comments.
Based on the comments/discussion below, initial thought is just to stuff the binary name from the arguments into a string and pass into the after_help() function. For example, something like this:
let bin_name = std::env:args().into_iter().next().unwrap();
let matches = Command::new(bin_name)
.after_help(format!("Text that includes {}", bin_name)) // This won't compile
.get_matches();
Taking this approach, you quickly run into a lifetime requirement in the function signature for after_help(). From clap's repo:
pub fn after_help<S: Into<&'help str>>(mut self, help: S)
In fact, if you look, there are many fields in the Command struct that have the lifetime annotation (&'help) on them. The Command::new() method doesn't have this lifetime annotation so it worked fine to just pass it bin_name as shown above.
Below is an abbreviated solution that dynamically generates after-help text in a manner that adheres to the lifetime requirements. Assuming a clean binary (application), called "foo", add the following code:
cargo.toml
[package]
name = "foo"
version = "0.1.0"
description = "A foo cli application"
authors = [ "John Doe" ]
edition = "2021"
[dependencies]
clap = { version = "3.1.6", features = ["cargo"] }
main.rs
fn main() {
// Get the binary name from the command line
let bin_name = std::env::args().into_iter().next().unwrap();
// Construct text that will be used in after_help.
let after_help_text = format!(
"Some after-help text that includes the binary name: {}",
bin_name
);
// clap, by default, will reference the name of your package. So, if you're
// doing the above, you might as well override the usage text too so you're
// being consistent.
let usage_text = format!("{}", bin_name);
if let Err(e) = foo::get_args(bin_name, after_help_text, usage_text).and_then(foo::run) {
eprintln!("{e}");
std::process::exit(1);
}
}
lib.rs
use clap::{ArgMatches, Command};
pub fn get_args(
bin_name: String,
after_help_text: String,
usage_text: String,
) -> std::io::Result<ArgMatches> {
let matches = Command::new(bin_name)
.override_usage(usage_text.as_str())
.version(clap::crate_version!())
.after_help(after_help_text.as_str())
.author(clap::crate_authors!())
.about(clap::crate_description!())
// add and configure args...
.get_matches();
Result::Ok(matches)
}
pub fn run(matches: ArgMatches) -> std::io::Result<()> {
// Do your CLI logic here based on matches.
Ok(())
}
Running the solution ( cargo run -- --help ) will produce the following output:
./foo 0.1.0
John Doe
A foo cli application
USAGE:
./foo
OPTIONS:
-h, --help Print help information
-V, --version Print version information
Some after-help text that includes the binary name: ./foo

Related

How do I extract hex values as strings? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 days ago.
This post was edited and submitted for review 4 days ago.
Improve this question
I am trying to extract a hash value from a function output:
let receipt = do_something();
println!("{receipt:?}");
let receipt_txn_hash = receipt.transaction_hash;
println!("receipt_txn_hash: {}", receipt_txn_hash);
let tx = provider.get_transaction(receipt_txn_hash).await?;
println!("tx: {}\n", serde_json::to_string(&tx)?);
let tx_hash = if let Some(txn) = tx {
txn.hash.to_string()
} else {
"hash_not_found".to_owned() //H256([0; 32])
};
println!("tx.hash: {}\n", &tx_hash);
And it prints out in the terminal:
TransactionReceipt { transaction_hash: 0xd6a0e48e6a0f80ae4467193f40721da1ad53ec854a738ea57d7201619e60f3b7, ... }
receipt_txn_hash: 0xd6a0…f3b7
tx: {"hash":"0xd6a0e48e6a0f80ae4467193f40721da1ad53ec854a738ea57d7201619e60f3b7",...}
tx.hash: 0xd6a0…f3b7
Somehow Rust-Analyzer identifies this receipt has {unknown} type, but tx has Option<Transaction> type.
But still, both the receipt.transaction_hash, tx.hash have been truncated... *why?
How can I get the original complete hash value and return it as a String?
Here are the dependencies:
ethers = { version = "1.0.2", features = ["legacy", "rustls"] }
ethers-solc = { version = "1.0.2", features = ["full"] }
ethers-providers = "1.0.2"
eyre = "0.6.8"
hex = "0.4.3"
reqwest = { version = "0.11.14", default-features = false }
serde_json = "1.0.93"
[Update]
Thanks to the answer below, both receipt_txn_hash and tx_hash are of the type H256.
The Display trait of H256 type is defined here:
https://github.com/paritytech/parity-common/blob/223af1dc6c176e35698aed9285f44e428da0050e/fixed-hash/src/hash.rs#L217
impl $crate::core_::fmt::Display for $name {
fn fmt(&self, f: &mut $crate::core_::fmt::Formatter) -> $crate::core_::fmt::Result {
$crate::core_::write!(f, "0x")?;
for i in &self.0[0..2] {
$crate::core_::write!(f, "{:02x}", i)?;
}
$crate::core_::write!(f, "…")?;
for i in &self.0[$n_bytes - 2..$n_bytes] {
$crate::core_::write!(f, "{:02x}", i)?;
}
Ok(())
}
}
From the Display trait definition above, we know Display trait is causing the truncation.
So we need to modify the Display trait(defined in our dependencies).
But we cannot modify or override the foreign trait definition due to: only traits defined in the current crate can be implemented for types defined outside of the crate. Define and implement a trait or new type instead
So we have to make our local type to modify the foreign trait!
Hence, this question is basically asking how to implement a local type(NewH256), on a foreign trait(fmt::Display)?
Going by a quick search I'm guessing the type is a H256 from here.
This has a Display implementation here which gives you your ellipses.
If you want to show the full hex, you might be best just printing the debug output:
println!("receipt_txn_hash: {:?}", receipt_txn_hash);
If that doesn't work, you can just copy the Display impl and format it however you want.

How to parse custom string with Clap derive

I found a link for what i want here: Parse user input String with clap for command line programming
But it's not completely clear. I see lots of post using App::new() but i can't find any trace of it in clap documentation.
I want to parse a string inside my rust application (it doesn't come from the cmdline)
Currently i'm doing it like this:
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
/// Optional name to operate on
test: String,
name: Option<String>,
}
pub fn test(mut to_parse: &String) {
let Ok(r) = shellwords::split(to_parse) else {
info!("error during process");
return;
};
let test = Cli::parse_from(r);
if let Some(name) = test.name.as_deref() {
println!("Value for name: {}", name);
}
}
And it kind of work, but maybe i don't understand enough how clap work. It only take positional argument like --test or --name. there must be a way to have a command before argument?
And the other question i have is : if i'm using a struct to define a command and use Cli::parse_from() to parse my string, how do i parse my string with multiple command (maybe unclear, but how do i use multiple commands?)
------EDIT
I've played with clap a little, and now i've something like this:
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
#[command(subcommand)]
command: Com,
}
/// Doc comment
#[derive(Subcommand)]
enum Com {
/// First command
Test(TestArg),
///Second command
SecondTest(OtherArg),
}
#[derive(Args)]
struct TestArg {
name: String,
}
#[derive(Args)]
struct OtherArg {
name: Option<String>,
and: Option<String>,
}
Nevertheless, when i enter a command in my in-game console (still parse with parse_from), nothing is recognize, i always have the error message.
Your question does not show what you pass in and what error you get, so it's rather hard to point to an issue.
I think you might be missing the first argument which clap (apparently) requires to work when parsing from an array of string slices, which is the binary name/path.
Using your code from above (2nd snippet), the snippet below does not work.
let cli = Cli::parse_from(["test", "asd"]);
dbg!(cli); // You need to add #[derive(Debug)] to your structs for it to work
as it ends with an error like
error: The subcommand 'asd' wasn't recognized
Usage: test <COMMAND>
For more information try '--help'
If you look closely, you'll see that the test argument, which we are trying to pass as a subcommand to our Cli struct, is recognized as the binary name.
If we add an additional argument at the beginning (the content does not matter, it can be even an empty string slice), it does work.
fn main() {
let cli = Cli::parse_from(["", "test", "asd"]);
dbg!(cli);
}
The above snippet prints successfully parsed arguments
[src/main.rs:32] cli = Cli {
command: Test(
TestArg {
name: "asd",
},
),
}
It does work also with the second sub-command SecondTest
let cli = Cli::parse_from(["", "second-test", "some name"]);
dbg!(cli);
Prints
[src/main.rs:35] cli = Cli {
command: SecondTest(
OtherArg {
name: Some(
"some name",
),
and: None,
},
),
}
Nevertheless, it seems like your code is missing some attribute macros in order to work as (I think) you expect it to work.
Assuming you want to parse it from a string like
mycmd test --name someName
your TestArg struct should look like this
#[derive(Args)]
struct TestArg {
#[clap(short, long)]
name: String,
}
now the name is taken not as a positional argument but as a flag(?) argument (required in this case), where the argument needs to be provided as --name <NAME> or -n <NAME>
Same for the second subcommand, you could declare it like this
#[derive(Args, Debug)]
struct OtherArg {
#[clap(long)]
name: Option<String>,
#[clap(long)]
and: Option<String>,
}
And then you need to pass the arguments with flags with long names, like so:
let cli = Cli::parse_from([
"",
"second-test",
"--name",
"some name",
"--and",
"other name",
]);
dbg!(cli);
which results in
[src/main.rs:41] cli = Cli {
command: SecondTest(
OtherArg {
name: Some(
"some name",
),
and: Some(
"other name",
),
},
),
}

Idiomatic rust way to properly parse Clap ArgMatches

I'm learning rust and trying to make a find like utility (yes another one), im using clap and trying to support command line and config file for the program's parameters(this has nothing to do with the clap yml file).
Im trying to parse the commands and if no commands were passed to the app, i will try to load them from a config file.
Now I don't know how to do this in an idiomatic way.
fn main() {
let matches = App::new("findx")
.version(crate_version!())
.author(crate_authors!())
.about("find + directory operations utility")
.arg(
Arg::with_name("paths")
...
)
.arg(
Arg::with_name("patterns")
...
)
.arg(
Arg::with_name("operation")
...
)
.get_matches();
let paths;
let patterns;
let operation;
if matches.is_present("patterns") && matches.is_present("operation") {
patterns = matches.values_of("patterns").unwrap().collect();
paths = matches.values_of("paths").unwrap_or(clap::Values<&str>{"./"}).collect(); // this doesn't work
operation = match matches.value_of("operation").unwrap() { // I dont like this
"Append" => Operation::Append,
"Prepend" => Operation::Prepend,
"Rename" => Operation::Rename,
_ => {
print!("Operation unsupported");
process::exit(1);
}
};
}else if Path::new("findx.yml").is_file(){
//TODO: try load from config file
}else{
eprintln!("Command line parameters or findx.yml file must be provided");
process::exit(1);
}
if let Err(e) = findx::run(Config {
paths: paths,
patterns: patterns,
operation: operation,
}) {
eprintln!("Application error: {}", e);
process::exit(1);
}
}
There is an idiomatic way to extract Option and Result types values to the same scope, i mean all examples that i have read, uses match or if let Some(x) to consume the x value inside the scope of the pattern matching, but I need to assign the value to a variable.
Can someone help me with this, or point me to the right direction?
Best Regards
Personally I see nothing wrong with using the match statements and folding it or placing it in another function. But if you want to remove it there are many options.
There is the ability to use the .default_value_if() method which is impl for clap::Arg and have a different default value depending on which match arm is matched.
From the clap documentation
//sets value of arg "other" to "default" if value of "--opt" is "special"
let m = App::new("prog")
.arg(Arg::with_name("opt")
.takes_value(true)
.long("opt"))
.arg(Arg::with_name("other")
.long("other")
.default_value_if("opt", Some("special"), "default"))
.get_matches_from(vec![
"prog", "--opt", "special"
]);
assert_eq!(m.value_of("other"), Some("default"));
In addition you can add a validator to your operation OR convert your valid operation values into flags.
Here's an example converting your match arm values into individual flags (smaller example for clarity).
extern crate clap;
use clap::{Arg,App};
fn command_line_interface<'a>() -> clap::ArgMatches<'a> {
//Sets the command line interface of the program.
App::new("something")
.version("0.1")
.arg(Arg::with_name("rename")
.help("renames something")
.short("r")
.long("rename"))
.arg(Arg::with_name("prepend")
.help("prepends something")
.short("p")
.long("prepend"))
.arg(Arg::with_name("append")
.help("appends something")
.short("a")
.long("append"))
.get_matches()
}
#[derive(Debug)]
enum Operation {
Rename,
Append,
Prepend,
}
fn main() {
let matches = command_line_interface();
let operation = if matches.is_present("rename") {
Operation::Rename
} else if matches.is_present("prepend"){
Operation::Prepend
} else {
//DEFAULT
Operation::Append
};
println!("Value of operation is {:?}",operation);
}
I hope this helps!
EDIT:
You can also use Subcommands with your specific operations. It all depends on what you want to interface to be like.
let app_m = App::new("git")
.subcommand(SubCommand::with_name("clone"))
.subcommand(SubCommand::with_name("push"))
.subcommand(SubCommand::with_name("commit"))
.get_matches();
match app_m.subcommand() {
("clone", Some(sub_m)) => {}, // clone was used
("push", Some(sub_m)) => {}, // push was used
("commit", Some(sub_m)) => {}, // commit was used
_ => {}, // Either no subcommand or one not tested for...
}

How do I assert that a Rust futures-preview future has not yet resolved

I'm trying to bring my dependencies up-to-date. I have some code that uses futures-preview and futures-timer. Earlier versions of futures-timer included the ability to put a timeout on a future, and I have a test that uses this feature, but it was removed from recent releases.
Is there a straightforward way of just checking to see if the future has resolved and then fail the test if it has? (Also, is "resolved" the correct word?)
I tried playing around with f1.poll() but couldn't quite figure out the return type and how to work with it.
I also tried using the timeout from async_std but I got a bunch of errors along these lines:
error[E0008]: cannot bind by-move into a pattern guard
--> /Users/nathan/.cargo/registry/src/github.com-1ecc6299db9ec823/async-std-0.99.9/src/net/driver/mod.rs:207:17
|
207 | Err(err) if err.kind() == io::ErrorKind::WouldBlock => {}
| ^^^ moves value into pattern guard
|
= help: add `#![feature(bind_by_move_pattern_guards)]` to the crate attributes to enable
Unless I'm mistaken, that's asking me to modify async-std, right? Is there a way around that?
Here's my test:
#[cfg(test)]
mod tests {
use super::*;
use crate::threadpool::ThreadPool;
use futures_timer::TryFutureExt;
// ....
#[test]
fn test_trigger_future_untriggered() {
let tf = TriggerFuture::default();
let f1 = tf.future();
ThreadPool::default()
.spawn_wait::<_, _, ()>(async {
f1.map(|_| Result::<(), std::io::Error>::Ok(()))
.timeout(std::time::Duration::from_millis(50))
.await
.expect_err("expected timeout");
Ok(())
})
.expect("expected!");
}
}
Update: Here's one more thing I tried. With futures-timer v1.0.1, it fails as expected if I uncomment the tf.trigger(), but it runs forever when I don't. What's wrong here?
#[test]
fn test_trigger_future_untriggered() {
let tf = TriggerFuture::default();
let f1 = tf.future();
// tf.trigger();
ThreadPool::default()
.spawn_wait::<_, _, _>(async {
use futures_timer::Delay;
let delay = Delay::new(std::time::Duration::from_millis(50));
future::select(f1, delay).then(|either| {
match either {
Either::Left((_, b)) => b.map(move |y| (Err("this future shoudn't resolve"), y)).left_future(),
Either::Right((_, a)) => a.map(move |x| (Ok("expected timeout"), x)).right_future(),
}
}).await.0
})
.expect("expected!");
}
Here is my Cargo.toml as requested:
[package]
name = "shared_futures"
version = "0.1.0"
publish = false
edition = "2018"
[dependencies]
futures-preview = { version = "0.3.0-alpha.19", features = [ "async-await", "compat" ] }
crossbeam-channel = "0.3.9"
num_cpus = "1.10.1"
# Note: the timeout feature was removed in futures-timer v0.6
futures-timer = "1.0.1"
[features]
sanitizer_safe = []
[lib]
name = "shared_futures"
crate-type = ["rlib"]
Could you kindly post your Cargo.toml and any use statements that belong to the example code? I can't run your code but after looking over your test example and error message I believe there are some related questions (and here) that may help you.
Can you try updating to the latest version of nightly and see if it fixes the issue? It appears the feature was recently stabilized.
rustup update nightly
This is assuming you are using the nightly version of the rust compiler.
Best of luck.
Figured it out (with a little help). My final attempt was close but was doing too much. This is a simplified version that actually works:
#[test]
fn test_trigger_future_untriggered() {
let tf = TriggerFuture::default();
let f1 = tf.future();
tf.trigger();
ThreadPool::default()
.spawn_wait::<_, _, &str>(async {
use futures_timer::Delay;
let delay = Delay::new(std::time::Duration::from_millis(50));
let res = future::select(f1, delay).await;
match res {
Either::Left(..) => Err("The TriggerFuture resolved first"),
Either::Right(..) => Ok(()),
}
})
.expect("The Delay should have resolved first");
}

How can I import JavaVM from the jni crate without causing compile errors?

I am trying to emulate the test cases at https://github.com/jni-rs/jni-rs/blob/master/tests/jni_api.rs and https://github.com/jni-rs/jni-rs/blob/master/tests/util/mod.rs . I have created a project with main.rs
use jni::{InitArgsBuilder, JNIVersion, JavaVM};
fn main() {
let jvm_args = InitArgsBuilder::new()
.version(JNIVersion::V8)
.option("-Xcheck:jni")
//.option(format!("-Djava.class.path={}", heinous_classpath()))
.build()
.unwrap_or_else(|e| panic!("{}", e.display_chain().to_string()));
let jvm = JavaVM::new(jvm_args);
}
and Cargo.toml:
[package]
name = "rust_call_jni"
version = "0.1.0"
authors = ["Robert Forsman <git#thoth.purplefrog.com>"]
edition = "2018"
[dependencies]
jni = "0.12.3"
When I do a cargo build I get the following error:
error[E0432]: unresolved import `jni::InitArgsBuilder`
--> src/main.rs:1:11
|
1 | use jni::{InitArgsBuilder, JNIVersion, JavaVM};
| ^^^^^^^^^^^^^^^ no `InitArgsBuilder` in the root
error[E0599]: no function or associated item named `new` found for type `jni::wrapper::java_vm::vm::JavaVM` in the current scope
--> src/main.rs:12:23
|
12 | let jvm = JavaVM::new(jvm_args);
| --------^^^
| |
| function or associated item not found in `jni::wrapper::java_vm::vm::JavaVM`
I'm using Rust 1.34.2.
How can I modify my source code to properly import and invoke the constructors?
Based on the pointer from Svetlin Zarev, I was able to rummage around in the documentation and figure out the proper syntax to enable the invocation feature.
The most important thing was a modification to Cargo.toml. The docstring for the jni crate might benefit from including this clause for folks with less experience writing Cargo.toml:
[package]
name = "rust_call_jni"
version = "0.1.0"
authors = ["Robert Forsman <git#thoth.purplefrog.com>"]
edition = "2018"
[dependencies.jni]
version = "0.12.3"
features = ["invocation"]
I had to modify main.rs to fix some errors that were masked by the earlier errors:
use jni::{InitArgsBuilder, JNIVersion, JavaVM};
fn main() {
let jvm_args = InitArgsBuilder::new()
.version(JNIVersion::V8)
.option("-Xcheck:jni")
//.option(&format!("-Djava.class.path={}", heinous_classpath()))
.build()
.unwrap_or_else(|e| //panic!("{}", e.display_chain().to_string())
panic!("{:?}", e));
let jvm = JavaVM::new(jvm_args);
}
It turns out there is another Cargo.toml syntax for dependencies with multiple attributes I discovered later on:
jni = {version="0.12.3", features=["invocation"]}

Resources