Expand description
§Derive-Attribute

§A set of macros to automatically deserialize standard attributes
§Compatible with all major versions of Syn
§Supports custom deserialization
§Can return multiple errors at once
§Allows for flexible attribute syntax
§Syn Compatibility
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
§Flexible Attribute Syntax
§Implicit Booleans
#[some_attr(is_bool)]
can also be written as #[some_attr(is_bool = true)]
§Seperated Lists
#[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))]
§Multiple Errors
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.
§Custom Deserialization
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
§Attr Arguments
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.
§Usage
Our attribute type is declared in a procedural macro crate:
#[derive(Attribute)]
#[attr(name = "my_attr")] // We set the attribute name to 'my_attr'
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<NestedList>, // deserializes a meta list named list i.e. list(num = 1)
// booleans are always optional
is_selected: bool,
}
#[derive(List)]
pub struct NestedList {
num: Option<u8>
}
It can then be used to parse the following attribute using the from_attrs method:
#[my_attr(name = "some_name", is_selected)]
lets look at the same attribute used in a derive macro
§Basic derive
procedural macro crate:
use derive_attribute::{Attribute, List};
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
#[derive(Attribute)]
#[attr(name = "my_attr")]
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<NestedList>,
// booleans are always optional
is_selected: bool,
}
#[derive(List)]
pub struct NestedList {
num: Option<u8>
}
#[proc_macro_derive(YOUR_MACRO_NAME, attributes(my_attr))]
pub fn derive_my_trait(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ast = parse_macro_input!(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
#[derive(YOUR_MACRO_NAME)]
#[my_attr(name = "some_name", is_selected)]
struct SomeStruct;
Now lets add our own argument type
§Custom Deserialization
proc-macro crate:
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<V: SynVersion> CustomArgFromMeta<V> for ErrorType {
fn try_from_meta(meta: Self::Metadata) -> Result<Self, ErrorMsg> {
let maybe_error_kind =
match V::deserialize_string(meta) {
Some(string) => {
match string.to_string().as_str() {
"warning" => Some(Self::Warning),
"severe" => Some(Self::Severe),
_ => None
}
}
None => None
};
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:
#[derive(Attribute)]
#[attr(name = "my_attr")]
struct MyAttribute {
// In order to use the simplified trait(CustomArgFromMeta) we need to wrap our struct in 'CustomArg'
error_type: CustomArg<ErrorType>,
name: String,
list: Option<u32>,
is_selected: bool,
}
Another crate using our macro:
#[derive(YOUR_MACRO_NAME)]
#[error(error_type = "warning", name = "some_name", is_selected)]
struct Test;
Modules§
Structs§
- ArgResult
- A result type that can contain multiple errors and a value at the same time.
- Custom
Arg - Allows a type to implement
CustomArgFromMeta
, a simplified version ofTryFromMeta
. - Error
- Syn1
- Deserialization functions & types for Syn version 1
- Syn2
- Deserialization functions & types for Syn version 1
Enums§
Traits§
- Attribute
- Represents a struct that can be deserialized from Syn attributes.
- Attribute
Name - Concat
- Combines an argument with a previously stored instance.
- Custom
ArgFrom Meta - A simplified version of the
TryFromMeta
trait. Types that implement this must be wrapped in theCustomArg
struct. - GetSpan
- Gets the Span of Syn metadata
- SynVersion
- Represents a Syn version and how it can parse attribute data into values
- TryFrom
Meta - A trait for deserializing Syn metadata.
Its recommended that you use theCustomArgFromMeta
trait for deserializing simple arguments.
Functions§
- required_
validation - Validates a simple required type.