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

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);
}

Related

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 extract a type from a procedural macro attribute using darling?

I need to be able to extract the type DieselHandler, ideally in a way that allows for repeatable data_loader attributes for transforming against multiple types.
#[derive(Loadable)]
#[data_loader(handler = DieselHandler<FooLoader>)]
If I use handler: syn::Type there is the error:
the trait bound syn::Type: FromMeta is not satisfied
the trait FromMeta is not implemented for syn::Type
How can I pass in a type here and maybe even check the wrapper structure?
#[derive(Debug, FromDeriveInput)]
#[darling(attributes(data_loader))]
struct LoadByArgs {
#[darling(default)]
internal: bool,
handler: ????,
}
#[proc_macro_derive(Loadable, attributes(data_loader))]
pub fn load_by(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let LoadByArgs {
internal, handler
} = FromDeriveInput::from_derive_input(&input).expect("can't parse attribute");
let expanded = quote! {
...
};
proc_macro::TokenStream::from(expanded)
}
darling requires the quotation marks when writing #[attr(foo = "bar")] because syn doesn't consider the quotation-free form to be a valid Meta, and the Meta is the basis of darling's entire API.
That's consistent with serde, which I still view as the closest thing to a standard for proc-macro APIs in Rust.
You can use darling::util::PathList if you want to write something like #[data_loader(DieselHandler)]. Technically type parameters are part of a path, so DieselHandler<FooLoader> should work on paper.

Writing a Rust struct type that contains a string and can be used in a constant

I'm getting started with Rust. I want to have a struct that contains (among other things) a string:
#[derive(Clone, Debug)]
struct Foo {
string_field: &str, // won't compile, but suppose String or Box<str> or &'a str or &'static str...
}
And I want to be able to declare constants or statics of it:
static FOO1: Foo = Foo {
string_field: "",
};
And I also want to be able to have it contain a string constructed at runtime:
let foo2 = Foo {
string_field: ("a".to_owned() + "b").as_str(),
};
I could add a lifetime parameter to Foo so that I can declare that the string reference has the same lifetime. That's fine, except that it then seems to require an explicit lifetime parameter for everything that contains a Foo, which means that it complicates the rest of my program (even parts that don't care about being able to use constant expressions).
I could write
enum StringOfAdequateLifetime {
Static(&'static str),
Dynamic(Box<str>), // or String, if you like
}
struct Foo {
string_field: StringOfAdequateLifetime,
}
and that seems to work so far but clutters up writing out literal Foos.
It seems obvious enough that the desired runtime behavior is sound: when you drop a Foo, drop the string it contains — and if it's static it's never dropped, so no extra information is needed to handle the two cases. Is there a clean way to ask Rust for just that?
(It seems like what I could use is some kind of "smart pointer" type to hold the string that can also be written as a constant expression for the static case, but I haven't seen one in the standard library, and when I tried to genericize StringOfAdequateLifetime to apply to any type, I ran into further complications with implementing and using the various standard traits like Deref, which I suspect were due to something about the differences between Sized and non-Sized types.)
The rust standard library has a built-in type for this exact use case, Cow. It's an enum that can represent either a reference or an owned value, and will clone the value if necessary to allow mutable access. In your particular use case, you could define the struct like so:
struct Foo {
string_field: Cow<'static, str>
}
Then you could instantiate it in one of two ways, depending on whether you want a borrowed constant string or an owned runtime-constructed value:
const BORROWED: Foo = Foo { string_field: Cow::Borrowed("some constant") };
let owned = Foo { string_field: Cow::Owned(String::from("owned string")) };
To simplify this syntax, you can define your own constructor functions for the type using a const fn to allow using the borrowed constructor in a constant context:
impl Foo {
pub const fn new_const(value: &'static str) -> Self {
Self { string_field: Cow::borrowed(value) }
}
pub fn new_runtime(value: String) -> Self {
Self { string_field: Cow::Owned(value) }
}
}
This allows you to use a simpler syntax for initializing the values:
const BORROWED: Foo = Foo::new_const("some constant");
let owned = Foo::new_runtime(String::from("owned string"));

What are the use cases of raw identifiers besides new keywords?

As in Rust 2018, we now have raw identifiers:
This feature is useful for a few reasons, but the primary motivation was inter-edition situations. For example, try is not a keyword in the 2015 edition, but is in the 2018 edition. So if you have a library that is written in Rust 2015 and has a try function, to call it in Rust 2018, you'll need to use the raw identifier.
Are there any other advantages than stated above? Are there plans to make keywords contextual, e.g. you can use type as identifier for variables? Why I should use a cryptic syntax like r#type instead of ty or something else?
Why I should use a cryptic syntax like r#type instead of ty or something else?
Sometimes the names of fields are used outside of your Rust program. For example, when serializing data with Serde, the field name is used in the output (e.g. JSON). So if you need JSON output with this:
"type": 27,
... then raw identifiers can help you:
#[derive(Serialize)]
struct Foo {
r#type: u32,
}
On the other hand... Serde already has a way to achieve what you want: the #[serde(rename = "name")] attribute. Reserved Rust keywords are one of the reasons why this attribute was introduced.
#[derive(Serialize)]
struct Foo {
#[serde(rename = "type")]
ty: u32,
}
Similarly, the Debug output also uses the field name in its output. So if you want the output Foo { type: 27 }, you can use raw identifiers:
#[derive(Debug)]
struct Foo {
r#type: u32,
}
On the other hand... if the exact Debug output is that important to you, you can simply implement it yourself:
impl fmt::Debug for Foo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Foo")
.field("type", &self.ty)
.finish()
}
}
So in practice, I don't see why one would use raw identifier for this purpose, since you have to use the strange r# syntax everywhere you use that name. It's probably easier to just fix this particular problem in another way.
So, as far as I see it, the "using API from another edition" is the only real use case for raw identifiers. Having such a syntax "just for the case" is a nice thing, though.
Ineed with raw_identifiers you can use type and other keywords as a variable/struct/etc. identifiers:
#![feature(rust_2018_preview)]
#![feature(raw_identifiers)]
struct r#let {} // just warnings: struct is never used: `let` and type `let` should have a camel case name such as `Let`
fn main() {
let r#type = 0; // just warning: unused variable: `type`
}
It doesn't work with every keyword, though:
let r#super = 0; // error: `r#super` is not currently supported.

How to introspect all available methods and members of a Rust type?

Is there a way to print out a complete list of available members of a type or instance in Rust?
For example:
In Python, I can use print(dir(object))
In C, Clang has a Python API that can parse C code and introspect it.
Being unfamiliar with Rust tools, I'm interested to know if there is some way to do this, either at run-time or compile-time, either with compiler features (macros for example), or using external tools.
This question is intentionally broad because the exact method isn't important. It is common in any language to want to find all of a variable's methods/functions. Not knowing Rust well, I'm not limiting the question to specific methods for discovery.
The reason I don't define the exact method is that I assume IDEs will need this information, so there will need to be some kinds of introspection available to support this (eventually). For all I know, Rust has something similar.
I don't think this is a duplicate of Get fields of a struct type in a macro since this answer could include use of external tools (not necessarily macros).
Is there a way to print out a complete list of available members of a type or instance in Rust?
Currently, there is no such built-in API that you can get the fields at runtime. However you can retrieve fields by using two different ways.
Declarative Macros
Procedural Macros
Solution By Using Declarative Macro
macro_rules! generate_struct {
($name:ident {$($field_name:ident : $field_type:ty),+}) => {
struct $name { $($field_name: $field_type),+ }
impl $name {
fn introspect() {
$(
let field_name = stringify!($field_name);
let field_type = stringify!($field_type);
println!("Field Name: {:?} , Field Type: {:?}",field_name,field_type);
)*
}
}
};
}
generate_struct! { MyStruct { num: i32, s: String } }
fn main() {
MyStruct::introspect();
}
This will give you the output:
Field Name: "num" , Field Type: "i32"
Field Name: "s" , Field Type: "String"
Playground
Solution Using Procedural Macro
Since procedural macros are more complicated from the declarative macros, you better to read some references(ref1, ref2, ref3) before starting.
We are going to write a custom derive which is named "Instrospect". To create this custom derive, we need to parse our struct as a TokenStream with the help of syn crate.
#[proc_macro_derive(Introspect)]
pub fn derive_introspect(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as ItemStruct);
// ...
}
Since our input can be parsed as ItemStruct and ItemStruct has the fields() method in it, we can use this to get fields of our struct.
After we get these fields, we can parse them as named and we can print their field name and field type accordingly.
input
.fields
.iter()
.for_each(|field| match field.parse_named() {
Ok(field) => println!("{:?}", field),
Err(_) => println!("Field can not be parsed successfully"),
});
If you want to attach this behavior to your custom derive you can use the following with the help of the quote crate:
let name = &input.ident;
let output = quote! {
impl #name {
pub fn introspect(){
input
.fields
.iter()
.for_each(|field| match field.parse_named() {
Ok(field) => println!("{:?}", field),
Err(_) => println!("Field can not be parsed successfully"),
});
}
}
};
// Return output TokenStream so your custom derive behavior will be attached.
TokenStream::from(output)
Since the behaviour injected to your struct as introspect function, you can call it in your application like following:
#[derive(Introspect)]
struct MyStruct {
num: i32,
text: String
}
MyStruct::introspect();
Note: Since the example you are looking for similar to this question. This Proc Macro Answer and Declarative Macro Answer should give you insight as well
To expand on my comment, you can use rustdoc, the Rust documentation generator, to view almost everything you're asking for (at compile time). rustdoc will show:
Structs (including public members and their impl blocks)
Enums
Traits
Functions
Any documentation comments written by the crate author with /// or //!.
rustdoc also automatically links to the source of each file in the [src] link.
Here is an example of the output of rustdoc.
Standard Library
The standard library API reference is available here and is available for anything in the std namespace.
Crates
You can get documentation for any crate available on crates.io on docs.rs. This automatically generates documentation for each crate every time it is released on crates.io.
Your Project
You can generate documentation for your project with Cargo, like so:
cargo doc
This will also automatically generate documentation for your dependencies (but not the standard library).
I have written a very simple crate which uses procedural macro. It gives you access to members information plus some simple information about struct/enum you use. Information about methods can not be given because procedural macros simply can't get this information, and as far as I know, there are no any methods which may give such information.
I don't think there is anything that will do this out of the box.
It may be possible to write a compiler plugin which can do that by examining the AST.
If you need the field names inside your program then you probably need to use macros. Either wrap your struct definition in macro and pattern match to create some function to get their names, or use procedural macro to derive structs for traits with such functions.
See examples in syn for derived traits. In particular, see syn::Data::Struct which has fields.
According to question of #AlexandreMahdhaoui, I would say: at least on latest Rust versions the proc_macro from accepted answer will not work, because you will need to pass tokens into quote! using "#". So you could try smth like next:
use proc_macro::{TokenStream};
use quote::{quote, ToTokens};
use syn::{parse_macro_input, ItemStruct};
#[proc_macro_derive(Introspect)]
pub fn derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as ItemStruct);
let ident = input.ident;
let field_data = input.fields.iter().map(|f| {
let field_type = f.ty.clone();
format!(
"Name={}, Type={}",
f.ident.clone().unwrap().to_string(),
quote!(#field_type).to_string()
)
}).collect::<Vec<_>>();
let output = quote! {
impl #ident {
pub fn introspect() {
println!("{:#?}", vec![#(#field_data),*]);
}
}
};
TokenStream::from(output)
}
#[derive(Introspect)]
struct Test {
size: u8,
test_field: u8,
}
fn main() {
Test::introspect();
}
Regarding methods, defined in impl I didn't find any info in output, so not sure if it possible. Probably somebody could share in comments ?
I use something like this:
println!("{:?}", variable); // struct, enum whatever
If it's a large type, use the # version:
println!("{:#?}", variable); // struct, enum whatever

Resources