1 个不稳定版本
0.1.0 | 2020 年 5 月 27 日 |
---|
694 在 过程宏 中排名 #694
34 次每月下载
用于 4 个 crate(直接使用 3 个)
16KB
150 行代码
macro-compose 是一个旨在简化和组织过程宏的库。它提供了特质(Lint
、Expand
)来将宏展开分割成多个更小、可重用的部分,并提供了结构(Collector
、Context
)来收集结果。
示例宏
以下小节展示了该库不同部分的示例。
示例来自 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
特质来展开宏。
一旦 Lint
或 Expand
向收集器报告了错误,宏将不再展开。这样,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_parse
从 TokenStream
(proc_macro::TokenStream)创建上下文。这个上下文可以用来运行 Lint
和 Expand
并获取结果输出。
示例
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