#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/tutorial.md"))]
#![forbid(unsafe_code)]
#![deny(missing_docs)]
#![allow(linker_messages)]
#[macro_use]
pub(crate) mod preprocess;
#[cfg(test)]
mod testing;
use proc_macro2::{TokenStream, TokenTree};
use syn::{ItemTrait, parse_macro_input};
mod analyze;
mod apply;
mod ast;
mod codegen;
mod entry;
mod parse;
mod util;
pub(crate) use analyze::TraitBounds;
pub(crate) use entry::{expand_attr_macro, expand_batch_trait};
use preprocess::{build_from_item, get_trait_item, parse_names_from_tokens};
use util::compile_error_str;
#[proc_macro_attribute]
pub fn batch_impl(
attr: proc_macro::TokenStream, item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let trait_item = parse_macro_input!(item as ItemTrait);
expand_attr_macro(attr.into(), trait_item, true)
.map(proc_macro::TokenStream::from)
.unwrap_or_else(Into::into)
}
#[proc_macro_attribute]
pub fn batch_impl_only(
attr: proc_macro::TokenStream, item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let trait_item = parse_macro_input!(item as ItemTrait);
expand_attr_macro(attr.into(), trait_item, false)
.map(proc_macro::TokenStream::from)
.unwrap_or_else(Into::into)
}
#[proc_macro]
pub fn batch_trait(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
expand_batch_trait(input).unwrap_or_else(Into::into)
}
#[doc(hidden)]
#[proc_macro]
pub fn batch_preprocess_test(
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let tokens = TokenStream::from(input).into_iter().collect::<Vec<_>>();
let tokens = match preprocess::angle_collect(&tokens) {
Ok(v) => v,
Err(e) => return e.into(),
};
let Some(TokenTree::Group(names_group)) = tokens.first() else {
return compile_error_str(
"batch-impl: batch_preprocess_test 期望 `(方法名列表){body} trait ...`",
)
.into();
};
if names_group.delimiter() != delimiter![()] {
return compile_error_str(
"batch-impl: batch_preprocess_test 期望 `(方法名列表){body} trait ...`",
)
.into();
}
let Some(TokenTree::Group(body_group)) = tokens.get(1) else {
return compile_error_str(
"batch-impl: batch_preprocess_test 期望 `(方法名列表){body} trait ...`",
)
.into();
};
if body_group.delimiter() != delimiter![{}] {
return compile_error_str(
"batch-impl: batch_preprocess_test 期望 `(方法名列表){body} trait ...`",
)
.into();
}
let trait_ts = tokens[2..].iter().cloned().collect();
let trait_item = match syn::parse2(trait_ts) {
Ok(t) => t,
Err(_) => {
return compile_error_str(
"batch-impl: batch_preprocess_test 无法解析 trait 定义",
)
.into();
}
};
let names = match parse_names_from_tokens(
&names_group.stream().into_iter().collect::<Vec<_>>(),
&trait_item,
) {
Ok(names) => names,
Err(e) => return e.into(),
};
let body = body_group.stream();
let mut methods = TokenStream::new();
for name in &names {
let item = match get_trait_item(&trait_item, name) {
Ok(item) => item,
Err(e) => return e.into(),
};
methods.extend(build_from_item(item, &body));
}
preprocess::render_angles(methods).into()
}