This crate is meant to be used in conjunction with Syn within a procedural macro crate.
A major version of Syn can be selected as a feature like: features = ["syn_2"]
.
Note: A Syn version must be selected
#[some_attr(is_bool)]
can also be written as #[some_attr(is_bool = true)]
#[some_attr(list(key_a = "value", key_b = 123))]
can also be written as
#[some_attr(list(key_a = "value"))]
#[some_attr(list(key_b = 123))]
Most macros will only return one attribute error at a time. This crate's macros can return multiple errors at once resulting in a better developer experience.
Any type that implements TryFromMeta
can be used as a valid attribute type.
Although Its recommended that you use CustomArgFromMeta
instead in order to simplify the implementation.
See example
The #[attr()]
attribute can be added to the attribute struct or its fields to add additional options.
The full list of arguments are:
name [str] - Renames the field.
default [bool/str] - Uses a default value if the argument isn't found.
If its a boolean, the type's implementation of Default::default will be used. \
If its a string, it must be a path to a function that returns the type.
Our attribute type is declared in a procedural macro crate: ```rust
struct MyAttribute { // Note: The attribute name will be the struct name in snake_case by default
name: String,
// wrapping a type in an option will make it optional
list: Option
// booleans are always optional
is_selected: bool,
}
#[derive(List)]
pub struct NestedList {
num: Option
```rust
```
lets look at the same attribute used in a derive macro
procedural macro crate: ```rust
use deriveattribute::{Attribute, List}; use procmacro2::TokenStream as TokenStream2; use quote::quote; use syn::{parsemacroinput, DeriveInput};
struct MyAttribute {
name: String,
// wrapping a type in an option will make it optional
// deserializes a meta list named list i.e. list(num = 1)
list: Option
pub fn derivemytrait(tokens: procmacro::TokenStream) -> procmacro::TokenStream { let ast = parsemacroinput!(tokens as DeriveInput);
fn attempt_derive(ast: DeriveInput) -> Result<TokenStream2, Vec<syn::Error>> {
// Wrapping an attribute in an option makes it optional
// A missing error won't be returnwd
let maybe_attribute = <Option<MyAttribute>>::from_attrs(ast.ident.span(), &ast.attrs)?;
let output: TokenStream2 = {
// Your Macro Generation Code
};
Ok(output)
}
let generated_tokens =
match attempt_derive(ast) {
Ok(tokens) => tokens,
Err(errors) => {
let compile_errors = errors.into_iter().map(|e| e.to_compile_error());
quote!(#(#compile_errors)*)
}
};
generated_tokens.into()
} ```
Another crate using our macro
```rust
struct SomeStruct; ```
Now lets add our own argument type
proc-macro crate: ```rust use derive_attribute::{CustomArg, CustomArgFromMeta};
struct ErrorType { Warning, Severe }
// Any type that implements 'TryFromMeta' can be deserialized however its a bit verbose
// In order to simplify the implementation we can implement 'CustomArgFromMeta' instead and wrap our type in the 'CustomArg' struct
impl
match maybe_error_kind {
Some(error_kind) => Ok(error_kind),
None => Err(InvalidType { expected: r#" "warning" or "severe" "# })
}
}
}
```
Our attribute struct now looks like this: ```rust
struct MyAttribute {
// In order to use the simplified trait(CustomArgFromMeta) we need to wrap our struct in 'CustomArg'
error_type: CustomArg
name: String,
list: Option<u32>,
is_selected: bool,
}
Another crate using our macro:
rust
struct Test; ```