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