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