Derive-Attribute   ![Latest Version] ![Documentation]

A set of macros to automatically deserialize standard attributes

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: ```rust

[derive(Attribute)]

[attr(name = "myattr")] // We set the attribute name to 'myattr'

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, // 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 } ``` It can then be used to parse the following attribute using the from_attrs method:

```rust

[myattr(name = "somename", is_selected)]

```

lets look at the same attribute used in a derive macro

Basic derive

procedural macro crate: ```rust

use deriveattribute::{Attribute, List}; use procmacro2::TokenStream as TokenStream2; use quote::quote; use syn::{parsemacroinput, 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, // booleans are always optional is_selected: bool, } #[derive(List)] pub struct NestedList { num: Option }

[procmacroderive(YOURMACRONAME, attributes(my_attr))]

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

[derive(YOURMACRONAME)]

[myattr(name = "somename", is_selected)]

struct SomeStruct; ```


Now lets add our own argument type

Custom Deserialization

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 CustomArgFromMeta for ErrorType { fn tryfrommeta(meta: Self::Metadata) -> Result { let maybeerrorkind = match V::deserializestring(meta) { Some(string) => { match string.tostring().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: ```rust

[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,

name: String,
list: Option<u32>,
is_selected: bool,

} Another crate using our macro: rust

[derive(YOURMACRONAME)]

[error(errortype = "warning", name = "somename", is_selected)]

struct Test; ```