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

Download history 40931/week @ 2024-04-27 36007/week @ 2024-05-04 74598/week @ 2024-05-11 112155/week @ 2024-05-18 109354/week @ 2024-05-25 117971/week @ 2024-06-01 122020/week @ 2024-06-08 109370/week @ 2024-06-15 110021/week @ 2024-06-22 109382/week @ 2024-06-29 122166/week @ 2024-07-06 119642/week @ 2024-07-13 122489/week @ 2024-07-20 117156/week @ 2024-07-27 119934/week @ 2024-08-03 125594/week @ 2024-08-10

503,129 每月下载量
用于 175 个crate(17 个直接使用)

Apache-2.0

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类型并使用deriveserde::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_tokenstreamattr解析到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 = "",
}]
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周围的包装器,实现了DeserializeParseWrapper,后者是包装在syn::Parse周围的包装器,实现了Deserialize。后者在传递,例如,一个syn::Path或其他来自syn crate的特定实体时很有用。

OrderedMap

您可能希望使用不能由HashMapBTreeMap等类型使用的键的映射语法,因为它们没有实现HashOrd。在这些情况下,您可以使用一个OrderedMap并将对作为元组的迭代器提取。

假设我们希望我们的"键"是serde_json::Value,而我们的值是...随便什么...String!您不能将serde_json::Value用作HashMapBTreeMap的键,但我们可以在一个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