1#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
2#![forbid(unsafe_code)]
4#![deny(missing_docs)]
5#![allow(linker_messages)]
8#[macro_use]
11pub(crate) mod preprocess;
12#[cfg(test)]
13mod fuzz;
14use proc_macro2::{TokenStream, TokenTree};
15use syn::{ItemTrait, parse_macro_input};
16
17mod apply;
18mod ast;
19mod batch_trait_entry;
20mod codegen;
21mod diagnostic;
22mod empty_generics;
23mod expand;
24mod parse;
25mod path_prefix;
26mod scan;
27mod trait_bounds;
28
29pub(crate) use expand::{expand_attr_macro, expand_batch_trait};
30pub(crate) use trait_bounds::TraitBounds;
31
32use diagnostic::compile_error_str;
33use preprocess::{build_from_item, get_trait_item, parse_names_from_tokens};
34
35#[proc_macro_attribute]
70pub fn batch_impl(
71 attr: proc_macro::TokenStream, item: proc_macro::TokenStream,
72) -> proc_macro::TokenStream {
73 let trait_item = parse_macro_input!(item as ItemTrait);
74 expand_attr_macro(attr, trait_item, true).unwrap_or_else(Into::into)
75}
76
77#[proc_macro_attribute]
95pub fn batch_impl_only(
96 attr: proc_macro::TokenStream, item: proc_macro::TokenStream,
97) -> proc_macro::TokenStream {
98 let trait_item = parse_macro_input!(item as ItemTrait);
99 expand_attr_macro(attr, trait_item, false).unwrap_or_else(Into::into)
100}
101
102#[proc_macro]
124pub fn batch_trait(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
125 expand_batch_trait(input).unwrap_or_else(Into::into)
126}
127
128#[doc(hidden)]
141#[proc_macro]
142pub fn batch_preprocess_test(
143 input: proc_macro::TokenStream,
144) -> proc_macro::TokenStream {
145 let tokens = TokenStream::from(input).into_iter().collect::<Vec<_>>();
146 let tokens = match preprocess::angle_collect(&tokens) {
147 Ok(v) => v,
148 Err(e) => return e.into(),
149 };
150 let Some(TokenTree::Group(names_group)) = tokens.first() else {
152 return compile_error_str(
153 "batch-impl: batch_preprocess_test 期望 `(方法名列表){body} trait ...`",
154 )
155 .into();
156 };
157 if names_group.delimiter() != delimiter![()] {
158 return compile_error_str(
159 "batch-impl: batch_preprocess_test 期望 `(方法名列表){body} trait ...`",
160 )
161 .into();
162 }
163 let Some(TokenTree::Group(body_group)) = tokens.get(1) else {
164 return compile_error_str(
165 "batch-impl: batch_preprocess_test 期望 `(方法名列表){body} trait ...`",
166 )
167 .into();
168 };
169 if body_group.delimiter() != delimiter![{}] {
170 return compile_error_str(
171 "batch-impl: batch_preprocess_test 期望 `(方法名列表){body} trait ...`",
172 )
173 .into();
174 }
175 let trait_ts = tokens[2..].iter().cloned().collect();
176 let trait_item = match syn::parse2(trait_ts) {
177 Ok(t) => t,
178 Err(_) => {
179 return compile_error_str(
180 "batch-impl: batch_preprocess_test 无法解析 trait 定义",
181 )
182 .into();
183 }
184 };
185 let names = match parse_names_from_tokens(
186 &names_group.stream().into_iter().collect::<Vec<_>>(),
187 &trait_item,
188 ) {
189 Ok(names) => names,
190 Err(e) => return e.into(),
191 };
192 let body = body_group.stream();
193 let mut methods = TokenStream::new();
194 for name in &names {
195 let item = match get_trait_item(&trait_item, name) {
196 Ok(item) => item,
197 Err(e) => return e.into(),
198 };
199 methods.extend(build_from_item(item, &body));
200 }
201 preprocess::render_angles(methods).into()
202}