3 个版本
0.1.2 | 2023 年 6 月 1 日 |
---|---|
0.1.1 | 2023 年 5 月 29 日 |
0.1.0 | 2023 年 5 月 29 日 |
#1551 in Rust 模式
每月 30 次下载
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