11 个版本
| 新版本 0.2.2 | 2024年8月14日 |
|---|---|
| 0.2.1 | 2024年6月12日 |
| 0.2.0 | 2023年3月19日 |
| 0.1.6 | 2022年12月22日 |
| 0.1.0 | 2020年5月15日 |
#1 in #token-stream
503,129 每月下载量
用于 175 个crate(17 个直接使用)
72KB
1.5K SLoC
serde_tokenstream
这个Rust包旨在与需要特定配置的宏一起使用。它实现为一个操作于proc_macro2::TokenStream(可轻松从标准proc_macro::TokenStream转换)的serde::Deserializer。
用法
假设我们正在构建一个属性过程宏,希望用户像这样使用它
#[MyMacro {
name = "SNPP",
owner = "Canary M Burns",
details = {
kind = Fission,
year_of_opening = 1968,
}
}]
fn some_func() {
...
}
它也适用于类似函数的宏
my_macro!(
name = "SNPP",
owner = "Hans",
layoffs_in_alphabetical_order = [
"Simpson, Homer"
]
);
实现过程宏的函数必须有两个参数(两种类型均为proc_macro::TokenStream):属性(宏名称后的括号中的标记),以及项目(宏应用到的函数、类型等)
#[proc_macro_attribute]
pub fn MyMacro(
attr: proc_macro::TokenStream,
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
...
}
我们首先定义代表配置的struct类型并使用derive为serde::Deserialize生成代码
#[derive(Deserialize)]
struct Config {
name: String,
owner: String,
details: ConfigDetails,
}
#[derive(Deserialize)]
struct ConfigDetails {
kind: ConfigDetailsType,
year_of_opening: usize,
}
#[derive(Deserialize)]
enum ConfigDetailsType {
Coal,
Fission,
Hydroelectric,
}
现在我们可以使用serde_tokenstream::from_tokenstream将attr解析到Config结构体中
use proc_macro2::TokenStream;
use serde_tokenstream::from_tokenstream;
#[allow(non_snake_case)]
#[proc_macro_attribute]
pub fn MyMacro(
attr: proc_macro::TokenStream,
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let config = match from_tokenstream::<Config>(&TokenStream::from(attr)) {
Ok(c) => c,
Err(err) => return err.to_compile_error().into(),
};
...
}
有关可以应用于类型及其成员的完整控制范围的说明,请参阅serde文档。
错误处理
错误指示了消费代码中问题部分,以协助宏用户
#[MyMacro{
name = "Rocinante",
owner = "Rocicorp",
details = {
kind = Fusion,
year_of_opening = 2347
}
}]
fn deploy() {
...
}
error: unknown variant `Fusion`, expected one of `Coal`, `Fission`, `Hydroelectric`
--> tests/test_err1.rs:7:16
|
7 | kind = Fusion,
| ^^^^^^
嵌套属性
对于解析外部宏内部的属性,请使用from_tokenstream_spanned。此函数为顶级错误提供更好的范围属性。
最常见的用法是与syn::MetaList一起。例如,如果您的宏是一个 derive 宏
#[derive(MyRobot)]
#[robot {
name = "Mawhrin-Skel",
kind = Drone,
planet = "Eä",
}]
fn monitor() {
...
}
则robot可以解释为syn::MetaList实例。有了这个
use serde_tokenstream::from_tokenstream;
#[derive(Deserialize)]
struct Robot {
...
}
#[proc_macro_derive(MyRobot, attributes(robot))]
pub fn my_robot(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let list = /* obtain the `syn::MetaList` from the input */;
let config = match from_tokenstream_spanned::<Robot>(
list.delimiter.span(),
&list.tokens
) {
Ok(c) => c,
Err(err) => return err.to_compile_error().into(),
};
}
TokenStream 和 syn::* 值
在某些情况下,将TokenStream值作为参数传递给宏很有用。在这种情况下,我们可以使用TokenStreamWrapper,它是一个包装在TokenStream周围的包装器,实现了Deserialize或ParseWrapper,后者是包装在syn::Parse周围的包装器,实现了Deserialize。后者在传递,例如,一个syn::Path或其他来自syn crate的特定实体时很有用。
OrderedMap
您可能希望使用不能由HashMap或BTreeMap等类型使用的键的映射语法,因为它们没有实现Hash或Ord。在这些情况下,您可以使用一个OrderedMap并将对作为元组的迭代器提取。
假设我们希望我们的"键"是serde_json::Value,而我们的值是...随便什么...String!您不能将serde_json::Value用作HashMap或BTreeMap的键,但我们可以在一个OrderedMap中使用它。
let config = from_tokenstream::<OrderedMap<serde_json::Value, String>>(tokens)?;
然后可以像这样调用宏
my_macro!(
{
"type" = "string",
"format" = "uuid",
} = "uuid::Uuid",
{
"type" = "string",
"format" = "ip",
} = "std::net::IpAddr",
);
依赖项
~0.4–1MB
~23K SLoC