impl-template 1.0.0-alpha

A procedural macro for generating impl-blocks based on a simple template.
Documentation
extern crate proc_macro;

mod find_patterns;
mod render;

use crate::find_patterns::{FindPatterns, Pattern};
use crate::render::Render;
use crate::render::Replacement;
use itertools::Itertools;
use proc_macro::TokenStream;
use quote::quote;
use syn::parse_macro_input;
use syn::Item;

#[proc_macro_attribute]
pub fn impl_template(_: TokenStream, input: TokenStream) -> TokenStream {
    let item = parse_macro_input!(input as Item);

    let template = match item {
        Item::Impl(item_impl) => item_impl,
        _ => panic!("impl-template can only be used on impl items"),
    };

    let patterns = template.find_patterns();

    if patterns.is_empty() {
        return TokenStream::from(quote! {
            #template
        });
    }

    let replacements = patterns
        .into_iter()
        .enumerate()
        .map(
            |(
                pattern_index,
                Pattern {
                    type_paths,
                    declaration,
                },
            )| {
                type_paths.into_iter().map(move |type_path| Replacement {
                    pattern_index,
                    type_path,
                    declaration,
                })
            },
        )
        .multi_cartesian_product()
        .collect::<Vec<_>>();

    let impl_blocks = replacements
        .into_iter()
        .map(|r| {
            let impl_block = template.render(r);

            quote! {
                #impl_block
            }
        })
        .collect::<Vec<_>>();

    let expanded = quote! {
        #(#impl_blocks)*
    };

    TokenStream::from(expanded)
}