How to use an internal library Enum for Clap Args - rust

I am currently working on a Rust port of a security tool. Inline with Rust's guides, I want to segregate the core library into its own crate, so that we can create various tools (CLI, API, streams etc.) that interface with with the core library without coupling them together.
The core library exposes two public Enums, one of them being the PermutationMode (truncated):
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum PermutationMode {
All,
Addition,
BitSquatting,
Homoglyph,
}
When creating a CLI utility using Clap, I would like to extend this library Enum as part of the CLI like so:
use clap::Clap;
use twistrs::permutate::PermutationMode;
#[derive(Clap, PartialEq, Debug)]
#[clap(name = "twistrs-cli")]
struct Opts {
#[clap(short, long)]
registered_domains: bool,
#[clap(arg_enum)]
permutation_mode: PermutationMode,
}
So that when calling the CLI, we can pass the permutation mode from the user, to the CLI, to the library seamlessly and without having the CLI needing to be aware of the internal modes (in the event that the library adds more).
./twist-cli --registered-domains --permutation_mode=all example.com
Currently this does not seem to be possible (which makes sense). One attempt was to use type aliasing:
#[derive(Clap)]
type ArgPermutationMode = PermutationMode
However we cannot use derive macros for type-aliases. I tried also "cloning" the enum and trying to map to the libraries enum:
enum ArgPermutationMode {
PermutationMode::All,
}
Which does not compile.
Question - Is it possible to extend an internal library Enum to use it as a Clap argument?

Unfortunately not. You would have to redefine the enum so that the arg_enum! macro can access the tokens.
If you add a conversion function between the two then you can make sure that upstream changes to the library enum force you to update your CLI, by giving you a compilation error:
arg_enum! {
enum ArgPermutationMode {
All,
Addition,
BitSquatting,
Homoglyph,
}
}
impl From<ArgPermutationMode> for PermutationMode {
fn from(other: ArgPermutationMode) -> PermutationMode {
match other {
ArgPermutationMode::All => PermutationMode::All,
ArgPermutationMode::Addition => PermutationMode::Addition,
ArgPermutationMode::BitSquatting => PermutationMode::BitSquatting,
ArgPermutationMode::Homoglyph => PermutationMode::Homoglyph,
}
}
}
impl From<PermutationMode> for ArgPermutationMode {
fn from(other: PermutationMode) -> ArgPermutationMode {
match other {
PermutationMode::All => ArgPermutationMode::All,
PermutationMode::Addition => ArgPermutationMode::Addition,
PermutationMode::BitSquatting => ArgPermutationMode::BitSquatting,
xPermutationMode::Homoglyph => ArgPermutationMode::Homoglyph,
}
}
}
You can reduce that boilerplate with a macro if you find yourself doing it a lot.
Given that you have control over the other crate, you could compromise by trying one of a few other options for a workaround:
Define the actual enum variants in a separate file, and use include! to use the same source in both crates. This assumes your crates are in the same workspace.
Use a macro derive like EnumIter from strum_macros. This will let you iterate over the enum's variants so you can supply them to Clap, without having a Clap dependency in that crate. You'll have a strum_macros dependency instead, so it's up to you if that is really better.
Add the clap_args! call in the internal crate, but feature-gate it. Your application crate can enable this feature, but most users wouldn't.

This is more of an extension to the above answer, incase it may help someone else. Ultimately what I ended up opting for was implementing implementing FromStr in the library as follows:
impl FromStr for PermutationMode {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_lowercase().as_str() {
"all" => Ok(PermutationMode::All),
"addition" => Ok(PermutationMode::Addition),
"bitsquatting" => Ok(PermutationMode::BitSquatting),
"homoglyph" => Ok(PermutationMode::Homoglyph),
_ => Err(),
}
}
}
To avoid having the client having to worry about the modes, we simply try to parse the string passed through the CLI into one of the permutation modes.
let permutation_mode = matches
.value_of("permutation_mode")
.unwrap()
.parse::<PermutationMode>()
.unwrap();
This way we don't need to couple the modes between the client and the library and overall makes the example CLI a lot more malleable.

Related

How do I inspect function arguments at runtime in Rust?

Say I have a trait that looks like this:
use std::{error::Error, fmt::Debug};
use super::CheckResult;
/// A Checker is a component that is responsible for checking a
/// particular aspect of the node under investigation, be that metrics,
/// system information, API checks, load tests, etc.
#[async_trait::async_trait]
pub trait Checker: Debug + Sync + Send {
type Input: Debug;
/// This function is expected to take input, whatever that may be,
/// and return a vec of check results.
async fn check(&self, input: &Self::Input) -> anyhow::Result<Vec<CheckResult>>;
}
And say I have two implementations of this trait:
pub struct ApiData {
some_response: String,
}
pub MetricsData {
number_of_events: u64,
}
pub struct ApiChecker;
impl Checker for ApiChecker {
type Input = ApiData;
// implement check function
}
pub struct MetricsChecker;
impl Checker for MetricsChecker {
type Input = MetricsData;
// implement check function
}
In my code I have a Vec of these Checkers that looks like this:
pub struct MyServer {
checkers: Vec<Box<dyn Checker>>,
}
What I want to do is figure out, based on what Checkers are in this Vec, what data I need to fetch. For example, if it just contained an ApiChecker, I would only need to fetch the ApiData. If both ApiChecker and MetricsChecker were there, I'd need both ApiData and MetricsData. You can also imagine a third checker where Input = (ApiData, MetricsData). In that case I'd still just need to fetch ApiData and MetricsData once.
I imagine an approach where the Checker trait has an additional function on it that looks like this:
fn required_data(&self) -> HashSet<DataId>;
This could then return something like [DataId::Api, DataId::Metrics]. I would then run this for all Checkers in my vec and then I'd end up a complete list of data I need to get. I could then do some complicated set of checks like this:
let mut required_data = HashSet::new();
for checker in checkers {
required_data.union(&mut checker.required_data());
}
let api_data: Option<ApiData> = None;
if required_data.contains(DataId::Api) {
api_data = Some(get_api_data());
}
And so on for each of the data types.
I'd then pass them into the check calls like this:
api_checker.check(
api_data.expect("There was some logic error and we didn't get the API data even though a Checker declared that it needed it")
);
The reasons I want to fetch the data outside of the Checkers is:
To avoid fetching the same data multiple times.
To support memoization between unrelated calls where the arguments are the same (this could be done inside some kind of Fetcher trait implementation for example).
To support generic retry logic.
By now you can probably see that I've got two big problems:
The declaration of what data a specific Checker needs is duplicated, once in the function signature and again from the required_data function. This naturally introduces bug potential. Ideally this information would only be declared once.
Similarly, in the calling code, I have to trust that the data that the Checkers said they needed was actually accurate (the expect in the previous snippet). If it's not, and we didn't get data we needed, there will be problems.
I think both of these problems would be solved if the function signature, and specifically the Input associated type, was able to express this "required data" declaration on its own. Unfortunately I'm not sure how to do that. I see there is a nightly feature in any that implements Provider and Demand: https://doc.rust-lang.org/std/any/index.html#provider-and-demand. This sort of sounds like what I want, but I have to use stable Rust, plus I figure I must be missing something and there is an easier way to do this without going rogue with semi dynamic typing.
tl;dr: How can I inspect what types the arguments are for a function (keeping in mind that the input might be more complex than just one thing, such as a struct or tuple) at runtime from outside the trait implementer? Alternatively, is there a better way to design this code that would eliminate the need for this kind of reflection?
Your problems start way earlier than you mention:
checkers: Vec<Box<dyn Checker>>
This is an incomplete type. The associated type Input means that Checker<Input = ApiData> and Checker<Input = MetricsData> are incompatible. How would you call checkers[0].check(input)? What type would input be? If you want a collection of "checkers" then you'll need a unified API, where the arguments to .check() are all the same.
I would suggest a different route altogether: Instead of providing the input, provide a type that can retrieve the input that they ask for. That way there's no need to coordinate what type the checkers will ask for in a type-safe way, it'll be inherent to the methods the checkers themselves call. And if your primary concern is repeatedly retrieving the same data for different checkers, then all you need to do is implement caching in the provider. Same with retry logic.
Here's my suggestion:
struct DataProvider { /* cached api and metrics */ }
impl DataProvider {
fn fetch_api_data(&mut self) -> anyhow::Result<ApiData> { todo!() }
fn fetch_metrics_data(&mut self) -> anyhow::Result<MetricsData> { todo!() }
}
#[async_trait::async_trait]
trait Checker {
async fn check(&self, data: &mut DataProvider) -> anyhow::Result<Vec<CheckResult>>;
}
struct ApiAndMetricsChecker;
#[async_trait::async_trait]
impl Checker for ApiAndMetricsChecker {
async fn check(&self, data: &mut DataProvider) -> anyhow::Result<Vec<CheckResult>> {
let _api_data = data.fetch_api_data()?;
let _metrics_data = data.fetch_metrics_data()?;
// do something with api and metrics data
todo!()
}
}

Propagate different errors when the function output is fixed by a macro [duplicate]

I want to learn how to properly deal with errors in Rust. I have read the book and this example; now I would like to know how I should deal with errors in this function:
fn get_synch_point(&self) -> Result<pv::synch::MeasPeriods, reqwest::Error> {
let url = self.root.join("/term/pv/synch"); // self.root is url::Url
let url = match url {
Ok(url) => url,
// ** this err here is url::ParseError and can be converted to Error::Kind https://docs.rs/reqwest/0.8.3/src/reqwest/error.rs.html#54-57 **//
Err(err) => {
return Err(Error {
kind: ::std::convert::From::from(err),
url: url.ok(),
})
}
};
Ok(reqwest::get(url)?.json()?) //this return reqwest::Error or convert to pv::sych::MeasPeriods automaticly
}
This code is improper; it causes a compilation error:
error[E0451]: field `kind` of struct `reqwest::Error` is private
--> src/main.rs:34:42
|
34 | Err(err) => return Err(Error{kind: ::std::convert::From::from(err), url: url.ok()})
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `kind` is private
error[E0451]: field `url` of struct `reqwest::Error` is private
--> src/main.rs:34:81
|
34 | Err(err) => return Err(Error{kind: ::std::convert::From::from(err), url: url.ok()})
| ^^^^^^^^^^^^^ field `url` is private
What is a proper pattern to deal with that case? For me, reqwest::Error in this case is a good solution so I would like to avoid defining my own error type:
enum MyError {
Request(reqwest::Error),
Url(url::ParseError) // this already a part of request::Error::Kind!!!
}
Update 2020
The rust programming language is evolving quickly so a new answer can be added! I really liked custom_error but now I think thiserror will be my loved one!
use thiserror::Error;
#[derive(Error, Debug)]
pub enum DataStoreError {
#[error("data store disconnected")]
Disconnect(#[from] io::Error),
#[error("the data for key `{0}` is not available")]
Redaction(String),
#[error("invalid header (expected {expected:?}, found {found:?})")]
InvalidHeader {
expected: String,
found: String,
},
#[error("unknown data store error")]
Unknown,
}
This allow change io::Error to DataStoreError::Disconnect with question mark ?. Go here for details
useful links:
great blog about using thiserror in combine with anyhow
Other interesting crates:
anyhow - Flexible concrete Error type built on std::error::Error
snafu - Situation Normal: All Fouled Up - SNAFU is a library to easily assign underlying errors into domain-specific errors while adding context. (similar to thiserror)
custom_error - This crate contains a macro that should make it easier to define custom errors without having to write a lot of boilerplate code.
for panics:
proc-macro-error - This crate aims to make error reporting in proc-macros simple and easy to use.
human-panic - Panic messages for humans. Handles panics by calling std::panic::set_hook to make errors nice for humans.
Unfortunately, in your case you cannot create a reqwest::Error from other error types, if the reqwest library does not provide a way to do so (and it likely doesn't). To solve this problem, which is very common, especially in applications which use multiple libraries, the proper solution would be one of the following:
Declare your own custom enum with all errors your application works with (or one subsystem of your application; granularity highly depends on the project), and declare From conversions from all errors you work with to this enum type.
As an extension of this approach, you can use error-chain (or quick-error, on which error-chain is basically based) to generate such custom types and conversions in a semi-automatic way.
Use a special, generic error type. There are basically two of them:
a. Box<Error> where Error is defined in the standard library.
b. Use the Error type defined in the failure crate.
Then the question mark operator will be able to convert any compatible error to one of these types because of various Into and From trait implementations.
Note that the failure crate is intended to be the way to define errors promoted in the Rust community. Not only does it provide a common error type and trait (which fixes various issues with the std::error::Error trait; see for example here), it also has facilities to define your own error types (for example, with failure_derive), and for tracking error context, causes and generating backtrace. Additionally, it tries to be as compatible with the existing error handling approaches as possible, therefore it can be used to integrate with libraries which use other, older approaches (std::error::Error, error-chain, quick-error) quite easily. So I strongly suggest you to consider using this crate first, before other options.
I have already started using failure in my application projects, and I just can't express how much easier and nicer error handling has become. My approach is as follows:
Define the Result type:
type Result<T> = std::result::Result<T, failure::Error>;
Use Result<Something> everywhere where an error can be returned, using the question mark operator (?) to convert between errors and functions like err_msg or format_err! or bail! to create my own error messages.
I have yet to write a library using failure, but I imagine that for libraries it would be important to create more specific errors declared as an enum, which can be done with the failure_derive crate. For applications, though, the failure::Error type is more than enough.
In that case, reusing the underlying error type is not possible because you cannot construct its hidden fields. And even when it is possible, I would advise against it, in order to make your code more flexible and future-proof.
Defining custom error types can involve writing a lot of boilerplate, but fortunately several libraries exist to alleviate this pain. failure, error-chain and quick-error were already mentioned above, but I would like to point you to a crate I wrote that involves even less boilerplate than the others : custom_error. With it, you can write:
#[macro_use] extern crate custom_error;
custom_error!{ MyError
Request{source: reqwest::Error} = "request error",
Url{source: url::ParseError} = "invalid url"
}
As already stated by
Vladimir Matveev, the failure crate should be your starting point. Here is my solution:
use std::io;
use std::result;
use failure::{Backtrace, Fail};
/// This is a new error type manged by Oxide library.
/// The custom derive for Fail derives an impl of both Fail and Display.
#[derive(Debug, Fail)]
pub enum OxideError {
#[fail(display = "{}", message)]
GeneralError { message: String },
#[fail(display = "{}", message)]
IoError {
message: String,
backtrace: Backtrace,
#[cause]
cause: io::Error,
},
}
/// Create general error
pub fn general(fault: &str) -> OxideError {
OxideError::GeneralError {
message: String::from(fault),
}
}
/// Create I/O error with cause and backtrace
pub fn io(fault: &str, error: io::Error) -> OxideError {
OxideError::IoError {
message: String::from(fault),
backtrace: Backtrace::new(),
cause: error,
}
}
This error enumeration is extendible which allows it to accommodate future modifications that might be made to the program.
It seems the anyhow crate is good excepts for not supporting Box<dyn StdError>.
Most error types implemented Display trait, so use this magic.
Customize an error
Implement From trait from all object that implements Display
pub struct MyError {
pub code: i32,
pub message: String,
}
impl<E: Display> From<E> for MyError {
fn from(value: E) -> Self {
MyError {
code: 0,
message: value.to_string(),
}
}
}
async fn hello() -> std::result::Result<(), &'static str> {
return Err("Hello world");
}
async fn test() -> std::result::Result<(), MyError> {
// &str
hello().await?;
// std::io::Error
std::fs::read("DD")?;
// serde_json::Error
let data = serde_json::from_str("[1,2,3]")?;
Ok(())
}

Rust proper error handling (auto convert from one error type to another with question mark)

I want to learn how to properly deal with errors in Rust. I have read the book and this example; now I would like to know how I should deal with errors in this function:
fn get_synch_point(&self) -> Result<pv::synch::MeasPeriods, reqwest::Error> {
let url = self.root.join("/term/pv/synch"); // self.root is url::Url
let url = match url {
Ok(url) => url,
// ** this err here is url::ParseError and can be converted to Error::Kind https://docs.rs/reqwest/0.8.3/src/reqwest/error.rs.html#54-57 **//
Err(err) => {
return Err(Error {
kind: ::std::convert::From::from(err),
url: url.ok(),
})
}
};
Ok(reqwest::get(url)?.json()?) //this return reqwest::Error or convert to pv::sych::MeasPeriods automaticly
}
This code is improper; it causes a compilation error:
error[E0451]: field `kind` of struct `reqwest::Error` is private
--> src/main.rs:34:42
|
34 | Err(err) => return Err(Error{kind: ::std::convert::From::from(err), url: url.ok()})
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `kind` is private
error[E0451]: field `url` of struct `reqwest::Error` is private
--> src/main.rs:34:81
|
34 | Err(err) => return Err(Error{kind: ::std::convert::From::from(err), url: url.ok()})
| ^^^^^^^^^^^^^ field `url` is private
What is a proper pattern to deal with that case? For me, reqwest::Error in this case is a good solution so I would like to avoid defining my own error type:
enum MyError {
Request(reqwest::Error),
Url(url::ParseError) // this already a part of request::Error::Kind!!!
}
Update 2020
The rust programming language is evolving quickly so a new answer can be added! I really liked custom_error but now I think thiserror will be my loved one!
use thiserror::Error;
#[derive(Error, Debug)]
pub enum DataStoreError {
#[error("data store disconnected")]
Disconnect(#[from] io::Error),
#[error("the data for key `{0}` is not available")]
Redaction(String),
#[error("invalid header (expected {expected:?}, found {found:?})")]
InvalidHeader {
expected: String,
found: String,
},
#[error("unknown data store error")]
Unknown,
}
This allow change io::Error to DataStoreError::Disconnect with question mark ?. Go here for details
useful links:
great blog about using thiserror in combine with anyhow
Other interesting crates:
anyhow - Flexible concrete Error type built on std::error::Error
snafu - Situation Normal: All Fouled Up - SNAFU is a library to easily assign underlying errors into domain-specific errors while adding context. (similar to thiserror)
custom_error - This crate contains a macro that should make it easier to define custom errors without having to write a lot of boilerplate code.
for panics:
proc-macro-error - This crate aims to make error reporting in proc-macros simple and easy to use.
human-panic - Panic messages for humans. Handles panics by calling std::panic::set_hook to make errors nice for humans.
Unfortunately, in your case you cannot create a reqwest::Error from other error types, if the reqwest library does not provide a way to do so (and it likely doesn't). To solve this problem, which is very common, especially in applications which use multiple libraries, the proper solution would be one of the following:
Declare your own custom enum with all errors your application works with (or one subsystem of your application; granularity highly depends on the project), and declare From conversions from all errors you work with to this enum type.
As an extension of this approach, you can use error-chain (or quick-error, on which error-chain is basically based) to generate such custom types and conversions in a semi-automatic way.
Use a special, generic error type. There are basically two of them:
a. Box<Error> where Error is defined in the standard library.
b. Use the Error type defined in the failure crate.
Then the question mark operator will be able to convert any compatible error to one of these types because of various Into and From trait implementations.
Note that the failure crate is intended to be the way to define errors promoted in the Rust community. Not only does it provide a common error type and trait (which fixes various issues with the std::error::Error trait; see for example here), it also has facilities to define your own error types (for example, with failure_derive), and for tracking error context, causes and generating backtrace. Additionally, it tries to be as compatible with the existing error handling approaches as possible, therefore it can be used to integrate with libraries which use other, older approaches (std::error::Error, error-chain, quick-error) quite easily. So I strongly suggest you to consider using this crate first, before other options.
I have already started using failure in my application projects, and I just can't express how much easier and nicer error handling has become. My approach is as follows:
Define the Result type:
type Result<T> = std::result::Result<T, failure::Error>;
Use Result<Something> everywhere where an error can be returned, using the question mark operator (?) to convert between errors and functions like err_msg or format_err! or bail! to create my own error messages.
I have yet to write a library using failure, but I imagine that for libraries it would be important to create more specific errors declared as an enum, which can be done with the failure_derive crate. For applications, though, the failure::Error type is more than enough.
In that case, reusing the underlying error type is not possible because you cannot construct its hidden fields. And even when it is possible, I would advise against it, in order to make your code more flexible and future-proof.
Defining custom error types can involve writing a lot of boilerplate, but fortunately several libraries exist to alleviate this pain. failure, error-chain and quick-error were already mentioned above, but I would like to point you to a crate I wrote that involves even less boilerplate than the others : custom_error. With it, you can write:
#[macro_use] extern crate custom_error;
custom_error!{ MyError
Request{source: reqwest::Error} = "request error",
Url{source: url::ParseError} = "invalid url"
}
As already stated by
Vladimir Matveev, the failure crate should be your starting point. Here is my solution:
use std::io;
use std::result;
use failure::{Backtrace, Fail};
/// This is a new error type manged by Oxide library.
/// The custom derive for Fail derives an impl of both Fail and Display.
#[derive(Debug, Fail)]
pub enum OxideError {
#[fail(display = "{}", message)]
GeneralError { message: String },
#[fail(display = "{}", message)]
IoError {
message: String,
backtrace: Backtrace,
#[cause]
cause: io::Error,
},
}
/// Create general error
pub fn general(fault: &str) -> OxideError {
OxideError::GeneralError {
message: String::from(fault),
}
}
/// Create I/O error with cause and backtrace
pub fn io(fault: &str, error: io::Error) -> OxideError {
OxideError::IoError {
message: String::from(fault),
backtrace: Backtrace::new(),
cause: error,
}
}
This error enumeration is extendible which allows it to accommodate future modifications that might be made to the program.
It seems the anyhow crate is good excepts for not supporting Box<dyn StdError>.
Most error types implemented Display trait, so use this magic.
Customize an error
Implement From trait from all object that implements Display
pub struct MyError {
pub code: i32,
pub message: String,
}
impl<E: Display> From<E> for MyError {
fn from(value: E) -> Self {
MyError {
code: 0,
message: value.to_string(),
}
}
}
async fn hello() -> std::result::Result<(), &'static str> {
return Err("Hello world");
}
async fn test() -> std::result::Result<(), MyError> {
// &str
hello().await?;
// std::io::Error
std::fs::read("DD")?;
// serde_json::Error
let data = serde_json::from_str("[1,2,3]")?;
Ok(())
}

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

Is it possible to make plugin extension hooks like WordPress actions in Rust?

I'm about to rewrite a highly modular CMS in Rust, so my question is if it's even possible to have the "core" application set up extension points (actions/hooks), which other plugins / crates is able to "tab" into.
Something like this would suffice, but how would you do this in Rust? The architecture above uses a plugin registry and initiates each plugin's main method from the core by iterating over each of them. However in Rust, since you can't have a global "modules" variable in e.g. a plugin_registry lib crate, I guess this is not the correct thinking in Rust.
Is there a better and more flexible way to make "plugins" integrate seamlessly with a core application? For example, something like an event dispatcher like WordPress uses?
As Shepmaster said, this is a very general question; hence there are many ways to do what you want. And as already mentioned, too, iron is a great example of a modular framework.
However, I'll try to give a useful example of how one could implement such a plugin system. For the example I will assume, that there is some kind of main-crate that can load the plugins and "configure" the CMS. This means that the plugins aren't loaded dynamically!
Structure
First, lets say we have four crates:
rustpress: the big main crate with all WordPress-like functionality
rustpress-plugin: needs to be used by plugin authors (is an own crate in order to avoid using a huge crate like rustpress for every plugin)
rustpress-signature: here we create our plugin which will add a signature to each post
my-blog: this will be the main executable that configures our blog and will run as a web server later
1. The trait/interface
The way to go in Rust are traits. You can compare them to interfaces from other languages. We will now design the trait for plugins which lives in rustpress-plugin:
pub trait Plugin {
/// Returns the name of the plugin
fn name(&self) -> &str;
/// Hook to change the title of a post
fn filter_title(&self, title: &mut String) {}
/// Hook to change the body of a post
fn filter_body(&self, body: &mut String) {}
}
Note that the filter_* methods already have a default implementation that does nothing ({}). This means that plugins don't have to override all methods if they only want to use one hook.
2. Write our plugin
As I said we want to write a plugin that adds our signature to each posts body. To do that we will impl the trait for our own type (in rustpress-signature):
extern crate rustpress_plugin;
use rustpress_plugin::Plugin;
pub struct Signature {
pub text: String,
}
impl Plugin for Signature {
fn name(&self) -> &str {
"Signature Plugin v0.1 by ferris"
}
fn filter_body(&self, body: &mut String) {
body.push_str("\n-------\n"); // add visual seperator
body.push_str(&self.text);
}
}
We created a simple type Signature for which we implement the trait Plugin. We have to implement the name() method and we also override the filter_body() method. In our implementation we just add text to the post body. We did not override filter_title() because we don't need to.
3. Implement the plugin stack
The CMS has to manage all plugins. I assume that the CMS has a main type RustPress that will handle everything. It could look like this (in rustpress):
extern crate rustpress_plugin;
use rustpress_plugin::Plugin;
pub struct RustPress {
// ...
plugins: Vec<Box<Plugin>>,
}
impl RustPress {
pub fn new() -> RustPress {
RustPress {
// ...
plugins: Vec::new(),
}
}
/// Adds a plugin to the stack
pub fn add_plugin<P: Plugin + 'static>(&mut self, plugin: P) {
self.plugins.push(Box::new(plugin));
}
/// Internal function that prepares a post
fn serve_post(&self) {
let mut title = "dummy".to_string();
let mut body = "dummy body".to_string();
for p in &self.plugins {
p.filter_title(&mut title);
p.filter_body(&mut body);
}
// use the finalized title and body now ...
}
/// Starts the CMS ...
pub fn start(&self) {}
}
What we are doing here is storing a Vec full of boxed plugins (we need to box them, because we want ownership, but traits are unsized). When the CMS then prepare a blog-post, it iterates through all plugins and calls all hooks.
4. Configure and start the CMS
Last step is adding the plugin and starting the CMS (putting it all together). We will do this in the my-blog crate:
extern crate rustpress;
extern crate rustpress_plugin;
extern crate rustpress_signature;
use rustpress::RustPress;
use rustpress_plugin::Plugin;
use rustpress_signature::Signature;
fn main() {
let mut rustpress = RustPress::new();
// add plugin
let sig = Signature { text: "Ferris loves you <3".into() };
rustpress.add_plugin(sig);
rustpress.start();
}
You also need to add the dependencies to the Cargo.toml files. I omitted that because it should be fairly easy.
And note again that this is one of many possibilities to create such a system. I hope this example is helpful. You can try it on playground, too.

Resources