Skip to main content

batch_impl/
lib.rs

1use proc_macro::TokenStream;
2use proc_macro2::{Ident, TokenStream as TokenStream2, TokenTree};
3use quote::quote;
4use syn::{parse_macro_input, ItemTrait};
5
6mod types;
7mod apply;
8mod parse;
9mod codegen;
10
11use codegen::generate_impl;
12use parse::{parse_item, Cursor};
13use types::{reset_fresh_counter, Op};
14
15/// 为 trait 批量生成 `impl` 块的属性宏。
16///
17/// 在 trait 定义上标注 `#[batch_impl(...)]`,宏参数中的每个 impl-spec 都会
18/// 为该 trait 生成一个对应的 `impl` 块。
19///
20/// ## 语法
21///
22/// ```text
23/// #[batch_impl( impl-spec [, impl-spec]* [{ body }]? )]
24/// ```
25///
26/// impl-spec 由三部分组成(均可省略后半部分):
27/// - `<impl-泛型>` — `impl` 块的泛型参数
28/// - `TraitName<trait-泛型>` — trait 的泛型参数与关联类型绑定
29/// - 目标类型 — 用 `[]` 包裹表示并列,用 `^`/`-` 表示泛型应用
30///
31/// ## 示例
32///
33/// ```ignore
34/// #[batch_impl(usize, isize)]
35/// trait Numeric {}
36///
37/// #[batch_impl(<T> Vec<T>)]
38/// trait Collection {}
39///
40/// #[batch_impl(<T> FromValue<T> i32 { fn wrap(_: T) -> Self { 0 } })]
41/// trait FromValue<T> { fn wrap(val: T) -> Self; }
42/// ```
43#[proc_macro_attribute]
44pub fn batch_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
45    reset_fresh_counter();
46    let trait_item = parse_macro_input!(item as ItemTrait);
47    let trait_name = trait_item.ident.clone();
48    let attr_vec = TokenStream2::from(attr).into_iter().collect::<Vec<_>>();
49    let trait_name_ts: TokenStream2 = quote![#trait_name];
50    let mut cursor = Cursor::new(&attr_vec);
51    let impls = parse_batch_trait_entry(
52        &mut cursor, Op::Comma, &trait_name_ts, &trait_name,
53        trait_item.unsafety.is_some(), Some(trait_item),
54    );
55    impls.into()
56}
57
58/// 对已声明的 trait 批量生成 `impl` 块的函数式宏。
59///
60/// 语法:`unsafe? Trait路径: impl-specs;`,以 `;` 分隔多个 trait 段。
61/// 每段的 `:` 之后是 DSL 表达式,与 `#[batch_impl]` 接受相同的语法。
62///
63/// ## 示例
64///
65/// ```ignore
66/// trait A {}
67/// trait B<T> {}
68/// mod foo { pub trait C {} }
69///
70/// batch_trait!(
71///     A: usize, isize;
72///     B: <T> B<T> Vec<T>;
73///     foo::C: u32;
74///     unsafe UnsafeTrait: usize
75/// );
76/// ```
77#[proc_macro]
78pub fn batch_trait(input: TokenStream) -> TokenStream {
79    reset_fresh_counter();
80    let tokens = TokenStream2::from(input).into_iter().collect::<Vec<_>>();
81    let mut cursor = Cursor::new(&tokens);
82    let mut result = quote![];
83    loop {
84        // 跳过前导 `;`(允许连续多个分号、尾随分号)
85        while cursor.is_punct(';') {
86            cursor.bump();
87        }
88        if cursor.at_end() {
89            break;
90        }
91
92        // `unsafe` 前缀:标记该段所有 impl 为 unsafe impl
93        let is_unsafe = if matches!(cursor.peek(), Some(TokenTree::Ident(id)) if *id == "unsafe") {
94            cursor.bump();
95            true
96        } else {
97            false
98        };
99
100        // 收集 trait 路径(遇到 `<>` 深度为 0 的 `:` 停止;`::` 路径分隔符一并收集)
101        let path_start = cursor.pos();
102        let mut depth = 0i32;
103        while let Some(token) = cursor.peek() {
104            match token {
105                TokenTree::Punct(p) if p.as_char() == '<' => {
106                    depth += 1;
107                    cursor.bump();
108                }
109                TokenTree::Punct(p) if p.as_char() == '>' => {
110                    depth -= 1;
111                    cursor.bump();
112                }
113                TokenTree::Punct(p) if p.as_char() == ':' && depth == 0 => {
114                    if matches!(cursor.peek_at(1), Some(TokenTree::Punct(p2)) if p2.as_char() == ':') {
115                        cursor.bump();
116                        cursor.bump();
117                    } else {
118                        break;
119                    }
120                }
121                _ => cursor.bump(),
122            }
123        }
124        let trait_path = cursor.slice_since(path_start);
125        if trait_path.is_empty() {
126            result.extend(generate_compile_error("batch_trait! 中期望 trait 名称"));
127            break;
128        }
129        let trait_full_path = match extract_trait_path(trait_path) {
130            Ok(path) => path,
131            Err(e) => { result.extend(e); break; }
132        };
133        let trait_last_ident = match extract_last_ident(trait_path) {
134            Ok(ident) => ident,
135            Err(e) => { result.extend(e); break; }
136        };
137        if !cursor.is_punct(':') {
138            result.extend(generate_compile_error(
139                "batch_trait! 中期望 ':' 分隔 trait 名称和 impl-specs",
140            ));
141            break;
142        }
143        cursor.bump();
144        let impl_code = parse_batch_trait_entry(
145            &mut cursor, Op::Semi, &trait_full_path,
146            trait_last_ident, is_unsafe, None,
147        );
148        result.extend(impl_code);
149    }
150    result.into()
151}
152
153/// 共享驱动:从游标解析 impl-specs,展开并列列表,生成 impl 块。
154///
155/// `top_level` 控制顶层优先级:
156/// - `Op::Comma` 用于 `#[batch_impl]`(整个参数按 `,` 分隔)
157/// - `Op::Semi` 用于 `batch_trait!` 的单段 specs(按 `,` 分隔,遇到 `;` 段落边界停止)
158///
159/// 展开阶段通过 BFS 工作清单把 `Ty::Array`(并列列表)逐层摊平为叶子 `Ty`,
160/// 再对每个叶子调用 `generate_impl` 生成对应的 impl 块。
161fn parse_batch_trait_entry(
162    cursor: &mut Cursor,
163    top_level: Op,
164    trait_full_path: &TokenStream2,
165    trait_last_ident: &Ident,
166    is_unsafe_trait: bool,
167    start_trait: Option<ItemTrait>,
168) -> TokenStream2 {
169    let mut tys = vec![];
170    while let Some(ty) = parse_item(cursor, top_level, Some(trait_last_ident)) {
171        let mut queue = vec![ty];
172        while let Some(item) = queue.pop() {
173            match item.expand() {
174                Ok(expanded) => {
175                    for e in expanded.into_iter().rev() {
176                        queue.push(e);
177                    }
178                }
179                Err(leaf) => tys.push(leaf),
180            }
181        }
182    }
183    let mut impls = start_trait.map_or(quote![], |t| quote![#t]);
184    for t in tys {
185        impls.extend(generate_impl(t, trait_full_path, is_unsafe_trait));
186    }
187    impls
188}
189
190/// 从 trait 路径 token 序列中提取完整路径(用于输出 `impl ... for`)
191fn extract_trait_path(trait_path: &[TokenTree]) -> Result<TokenStream2, TokenStream2> {
192    let path: TokenStream2 = trait_path.iter().cloned().collect();
193    if path.is_empty() {
194        Err(generate_compile_error("batch_trait! 中期望 trait 名称"))
195    } else {
196        Ok(path)
197    }
198}
199
200/// 从 trait 路径 token 序列中提取最后一个标识符(用作 trait_name 匹配)
201fn extract_last_ident(trait_path: &[TokenTree]) -> Result<&Ident, TokenStream2> {
202    trait_path
203        .iter()
204        .filter_map(|tt| if let TokenTree::Ident(id) = tt { Some(id) } else { None })
205        .next_back()
206        .ok_or_else(|| generate_compile_error("batch_trait! 中期望标识符作为 trait 名称"))
207}
208
209/// 构造 `compile_error!(msg)` 用于编译期报错
210fn generate_compile_error(msg: &str) -> TokenStream2 {
211    quote! { compile_error!(#msg); }
212}