#macro #proc-macro #lint #collector #context #compose #trying

macro-compose

macro-compose 是一个旨在简化和组织过程宏的库。

1 个不稳定版本

0.1.0 2020 年 5 月 27 日

694过程宏 中排名 #694

34 次每月下载
用于 4 个 crate(直接使用 3 个)

WTFPL 许可证

16KB
150 行代码

macro-compose 是一个旨在简化和组织过程宏的库。它提供了特质(LintExpand)来将宏展开分割成多个更小、可重用的部分,并提供了结构(CollectorContext)来收集结果。

示例宏

以下小节展示了该库不同部分的示例。

示例来自 examples/enum_from_str_macro 中的示例宏,该宏实现了为枚举实现 FromStr 的 derive 宏。

代码检查和错误处理

使用 Lint 特质来检查宏输入。可以使用 Collector::error 来输出错误。

示例

use macro_compose::{Collector, Lint};
use syn::{Data, DeriveInput, Error, Fields};

struct EnsureEnumLint;

impl Lint<DeriveInput> for EnsureEnumLint {
    fn lint(&self, input: &DeriveInput, c: &mut Collector) {
        match &input.data {
            Data::Enum(e) => {
                for variant in e.variants.iter() {
                    if variant.fields != Fields::Unit {
                        c.error(Error::new_spanned(&variant.fields, "unexpected fields"))
                    }
                }
            }
            _ => c.error(Error::new_spanned(input, "expected an enum")),
        }
    }
}

展开宏

使用 Expand 特质来展开宏。

一旦 LintExpand 向收集器报告了错误,宏将不再展开。这样,Expand 实现可以假设 Lint 检查的数据是有效的。从 Expand 返回 None 并不会自动报告错误。

示例

use macro_compose::{Collector, Expand};
use proc_macro2::Ident;
use syn::{parse_quote, Arm, Data, DeriveInput, Error, Fields, ItemImpl};

struct ImplFromStrExpand;

impl Expand<DeriveInput> for ImplFromStrExpand {
    type Output = ItemImpl;

    fn expand(&self, input: &DeriveInput, _: &mut Collector) -> Option<Self::Output> {
        let variants = match &input.data {
            Data::Enum(e) => &e.variants,
            _ => unreachable!(),
        };
        let ident = &input.ident;

        let arms = variants.iter().map(|v| -> Arm {
            let v = &v.ident;
            let name = v.to_string();
            parse_quote!(
                #name => ::core::result::Result::Ok(#ident :: #v)
            )
        });

        let ident = &input.ident;
        let error = error_struct_ident(input);
        Some(parse_quote!(
            impl ::core::str::FromStr for #ident {
                type Err = #error;

                fn from_str(s: &::core::primitive::str) -> ::core::result::Result<Self, Self::Err> {
                    match s {
                        #(#arms,)*
                        invalid => ::core::result::Result::Err( #error (::std::string::ToString::to_string(invalid))),
                    }
                }
            }
        ))
    }
}

实现宏

可以使用 Context::new_parseTokenStream(proc_macro::TokenStream)创建上下文。这个上下文可以用来运行 LintExpand 并获取结果输出。

示例

use macro_compose::{Collector, Context};
use proc_macro::TokenStream;

#[proc_macro_derive(FromStr)]
pub fn derive_from_str(item: TokenStream) -> TokenStream {
    let mut collector = Collector::new();

    let mut ctx = Context::new_parse(&mut collector, item);
    ctx.lint(&EnsureEnumLint);

    ctx.expand(&ErrorStructExpand);
    ctx.expand(&ImplDebugErrorStructExpand);
    ctx.expand(&ImplFromStrExpand);

    collector.finish()
}

依赖项

~1.5MB
~35K SLoC