3 个版本

0.1.2 2023 年 6 月 1 日
0.1.1 2023 年 5 月 29 日
0.1.0 2023 年 5 月 29 日

#1551 in Rust 模式

每月 30 次下载

MIT 许可证

44KB
579

Derive-Attribute — 最新版本 文档

一组宏,用于自动反序列化标准属性

  • 兼容所有主要的 Syn 版本

  • 支持自定义反序列化

  • 可以一次返回多个错误

  • 允许灵活的属性语法

Syn 兼容性

这个包旨在与 Syn 结合在过程宏包中使用。
Syn 的一个主要版本可以作为功能选择,例如:features = ["syn_2"]

注意:必须选择一个 Syn 版本

灵活的属性语法

隐式布尔值

#[some_attr(is_bool)] 也可以写成 #[some_attr(is_bool = true)]

分隔列表

#[some_attr(列表(key_a= "",key_b= 123))]
也可以写成
#[some_attr(列表(key_a= ""))]
#[some_attr(列表(key_b= 123))]

多错误

大多数宏一次只会返回一个属性错误。
此包的宏可以一次返回多个错误,从而提供更好的开发体验。

自定义反序列化

任何实现 TryFromMeta 的类型都可以用作有效的属性类型。
虽然推荐使用 CustomArgFromMeta 以简化实现。

示例

属性参数

可以将 #[attr()] 属性添加到属性结构或其字段以添加更多选项。

参数的完整列表如下

name [str] - 重命名字段。

default [bool/str] - 如果找不到参数,则使用默认值。
如果是布尔值,则使用该类型的 Default::default 实现。
如果是字符串,则必须是返回该类型的函数的路径。

用法

我们的属性类型是在过程宏 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>
    }

然后可以使用 from_attrs 方法解析以下属性

#[my_attr(name = "some_name", is_selected)]

让我们看看在 derive 宏中使用的相同属性

基本 derive

过程宏 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()
}

另一个使用我们的宏的 crate

#[derive(YOUR_MACRO_NAME)]
#[my_attr(name = "some_name", is_selected)]
struct SomeStruct;

现在让我们添加自己的参数类型

自定义反序列化

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" "# })
        }
    }
}

我们的属性结构现在看起来是这样的

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

另一个使用我们的宏的 crate

#[derive(YOUR_MACRO_NAME)]
#[error(error_type = "warning", name = "some_name", is_selected)]
struct Test;

依赖项

~0.9–1.4MB
~27K SLoC