Skip to main content

batch_impl/
lib.rs

1#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
2#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/tutorial.md"))]
3// The library uses no unsafe; missing docs are rejected as errors (only for pub items;
4// internal pub(crate) is exempt).
5#![forbid(unsafe_code)]
6#![deny(missing_docs)]
7// The MSVC linker prints "creating library ... and object ..." to stdout, which rustc
8// treats as linker_messages warnings; these are harmless Windows link-product notices,
9// suppressed globally.
10#![allow(linker_messages)]
11// The `delimiter!` macro is defined at the top of preprocess and imported into the crate
12// root via `#[macro_use]`; textual scope requires its declaration to precede all users
13// (fuzz / parse / this module).
14#[macro_use]
15pub(crate) mod preprocess;
16#[cfg(test)]
17mod testing;
18use syn::{ItemTrait, parse_macro_input};
19
20mod analyze;
21mod apply;
22mod ast;
23mod codegen;
24mod entry;
25mod parse;
26mod util;
27
28pub(crate) use analyze::TraitBounds;
29pub(crate) use entry::{expand_attr_macro, expand_batch_trait};
30
31/// Attribute macro that generates `impl` blocks for a trait in batch.
32///
33/// Annotate a trait definition with `#[batch_impl(...)]`; every impl-spec in the macro
34/// arguments generates a corresponding `impl` block for that trait.
35///
36/// ## Syntax
37///
38/// ```text
39/// #[batch_impl( impl-spec [, impl-spec]* [{ body }]? )]
40/// ```
41///
42/// An impl-spec has three parts (the tail of each part may be omitted):
43/// - `<impl generics>` — generic params of the `impl` block
44/// - `Trait name<trait generics>` — the trait's generic args and associated type bindings
45/// - target type — wrapped in `[]` for a parallel list, `^`/`-` for generic application
46///
47/// ## ItemImpl entry (0.8.0, Ext 1)
48///
49/// The same attribute also accepts an `impl` block: the DSL describes a shape
50/// template × matrix source, and every matrix leaf instantiates the impl
51/// (the slot mapping rewrites the for-Type / where / body; the original impl
52/// is withheld):
53///
54/// ```
55/// # use batch_impl::batch_impl;
56/// # use std::rc::Rc;
57/// # trait Mk { fn make() -> Self; }
58/// #[batch_impl(Wrapper<T> : [Box, Rc]^u8)]
59/// impl Mk for Wrapper<T> { fn make() -> Wrapper<T> { Wrapper::new(T::default()) } }
60/// // → impl Mk for Box<u8> { fn make() -> Box<u8> { Box::new(u8::default()) } }
61/// // → impl Mk for Rc<u8>  { fn make() -> Rc<u8>  { Rc::new(u8::default()) } }
62/// ```
63///
64/// ## Examples
65///
66/// ```
67/// # use batch_impl::batch_impl;
68/// #[batch_impl(usize, isize)]
69/// trait Numeric {}
70///
71/// #[batch_impl(<T> Vec<T>)]
72/// trait Collection {}
73///
74/// #[batch_impl(<T> FromValue<T> [i32 { fn wrap(_: T) -> Self { 0 }}, u32 #wrap{0}] )]
75/// trait FromValue<T> { fn wrap(val: T) -> Self; }
76///
77/// // #name{body} also supports const and type items
78/// #[batch_impl(usize #MY_CONST{42})]
79/// trait HasConst { const MY_CONST: usize; }
80///
81/// ```
82#[proc_macro_attribute]
83pub fn batch_impl(
84    attr: proc_macro::TokenStream, item: proc_macro::TokenStream,
85) -> proc_macro::TokenStream {
86    // Ext 1 (0.8.0): the attribute also accepts an `impl` block — batch
87    // instantiation from a shape-template × matrix-source description. The
88    // trait branch is untouched (top-level dispatch only, per todos §A).
89    if let Ok(trait_item) = syn::parse::<ItemTrait>(item.clone()) {
90        return expand_attr_macro(attr.into(), trait_item, true)
91            .map(proc_macro::TokenStream::from)
92            .unwrap_or_else(Into::into);
93    }
94    if let Ok(impl_item) = syn::parse::<syn::ItemImpl>(item.clone()) {
95        return entry::expand_impl_entry(attr.into(), impl_item)
96            .map(proc_macro::TokenStream::from)
97            .unwrap_or_else(Into::into);
98    }
99    proc_macro::TokenStream::from(crate::util::compile_error_str(
100        "batch-impl: expected a trait definition (`trait ...`) or an impl block \
101         (`impl Trait for Type { ... }`)",
102        proc_macro2::Span::call_site(),
103    ))
104}
105
106#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/batch_impl_only.md"))]
107#[proc_macro_attribute]
108pub fn batch_impl_only(
109    attr: proc_macro::TokenStream, item: proc_macro::TokenStream,
110) -> proc_macro::TokenStream {
111    let trait_item = parse_macro_input!(item as ItemTrait);
112    expand_attr_macro(attr.into(), trait_item, false)
113        .map(proc_macro::TokenStream::from)
114        .unwrap_or_else(Into::into)
115}
116
117#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/batch_trait.md"))]
118#[proc_macro]
119pub fn batch_trait(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
120    expand_batch_trait(input).unwrap_or_else(Into::into)
121}
122
123#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/batch_preprocess_test.md"))]
124#[proc_macro]
125pub fn batch_preprocess_test(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
126    entry::preprocess_test(input.into())
127        .map(proc_macro::TokenStream::from)
128        .unwrap_or_else(Into::into)
129}
130
131#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/batch_preview.md"))]
132#[proc_macro]
133pub fn batch_preview(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
134    entry::preview(input.into()).map(proc_macro::TokenStream::from).unwrap_or_else(Into::into)
135}
136
137// ============================================================
138// Documentation placeholders for the DSL directive / macro-meta layers.
139//
140// The `#` directives and `@` constants live inside macro arguments, so IDE
141// hover and docs.rs cannot reach them. Each placeholder below is a public
142// no-op function whose doc block documents one directive — a hoverable,
143// searchable rustdoc entry. Never call these functions.
144// ============================================================
145
146#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/directive_delegate.md"))]
147#[proc_macro]
148pub fn batch_impl_delegate(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
149    proc_macro::TokenStream::new()
150}
151
152#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/directive_fill.md"))]
153#[proc_macro]
154pub fn batch_impl_fill(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
155    proc_macro::TokenStream::new()
156}
157
158#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/directive_blanket.md"))]
159#[proc_macro]
160pub fn batch_impl_blanket(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
161    proc_macro::TokenStream::new()
162}
163
164#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/directive_name.md"))]
165#[proc_macro]
166pub fn batch_impl_name(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
167    proc_macro::TokenStream::new()
168}
169
170#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/directive_open.md"))]
171#[proc_macro]
172pub fn batch_impl_open(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
173    proc_macro::TokenStream::new()
174}
175
176#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/directive_consts.md"))]
177#[proc_macro]
178pub fn batch_impl_consts(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
179    proc_macro::TokenStream::new()
180}