otto_vec_derive 0.0.1

The back-end for otto_derive
Documentation
fn extend_ident_with_vec(ident: &syn::Ident) -> syn::Ident {
    syn::Ident::new(&format!("{}_vec", ident), ident.span())
}

pub fn impl_otto_vec_macro(ast: syn::ItemFn) -> proc_macro::TokenStream {
    if parse_arguments_number(&ast) == 0 {
        panic!("Function must have at least 1 argument.")
    } else {
        let function_name = parse_ident(&ast);
        let function_generics = parse_generics(&ast);
        let function_arguments = parse_arguments(&ast);
        let size = function_arguments.len();
        let (function_arguments_name, function_arguments_name_vec, function_arguments_type) =
            unzip_triple(function_arguments.into_iter());
        let function_return_type = parse_return(&ast);
        let function_body = parse_body(&ast);
        proc_macro::TokenStream::from(quote::quote! {
            #ast
            pub fn
            #function_name
            #function_generics
            (#(
                mut #function_arguments_name_vec : std::vec::Vec<#function_arguments_type>
            ),*)
            ->
            std::vec::Vec<#function_return_type> {
                #(
                    #function_arguments_name_vec.reverse();
                )*
                let sizes = [#(#function_arguments_name_vec.len()),*];
                if sizes.iter().all(|x| x.eq(sizes.first().unwrap())) {
                    let size = sizes.first().unwrap();
                    let mut result = std::vec::Vec::with_capacity(#size);
                    for _ in 0..*size {
                        #(
                            let #function_arguments_name = #function_arguments_name_vec.pop().unwrap();
                        )*
                        result.push((||{
                            #function_body
                        })())
                    }
                    result
                } else {
                    panic!("The size of vectors are not equal.");
                }
            }
        })
    }
}

fn parse_ident(ast: &syn::ItemFn) -> syn::Ident {
    extend_ident_with_vec(&ast.sig.ident)
}

fn parse_generics(ast: &syn::ItemFn) -> syn::ImplGenerics<'_> {
    let (impl_generics, _, _) = ast.sig.generics.split_for_impl();
    impl_generics
}

fn parse_arguments_number(ast: &syn::ItemFn) -> usize {
    ast.sig.inputs.iter().len()
}

fn parse_arguments(ast: &syn::ItemFn) -> Vec<(&syn::Pat, syn::Ident, &syn::Type)> {
    ast.sig
        .inputs
        .iter()
        .enumerate()
        .map(|(index, input)| match input {
            syn::FnArg::Receiver(_) => {
                panic!("Vectorization on member functions is not currently supported.")
            }
            syn::FnArg::Typed(pat_type) => (
                &*pat_type.pat,
                match &*pat_type.pat {
                    syn::Pat::Ident(syn::PatIdent { ident, .. }) => extend_ident_with_vec(&ident),
                    _ => syn::Ident::new(&format!("arg_{}", index), proc_macro2::Span::call_site()),
                },
                &*pat_type.ty,
            ),
        })
        .collect()
}

fn parse_return(ast: &syn::ItemFn) -> syn::Type {
    if let syn::ReturnType::Type(_, b) = &ast.sig.output {
        *b.clone()
    } else {
        panic!("Vectorized function must have a return type.")
    }
}

fn parse_body(ast: &syn::ItemFn) -> proc_macro2::TokenStream {
    let statements = ast.block.stmts.iter().map(|statement| statement);
    quote::quote! {
        #(#statements)*
    }
}

fn unzip_triple<A, B, C>(input: impl Iterator<Item = (A, B, C)>) -> (Vec<A>, Vec<B>, Vec<C>) {
    let hint = input.size_hint();
    let size = if let Some(size) = hint.1 {
        size
    } else {
        hint.0
    };
    let mut a = Vec::<A>::with_capacity(size);
    let mut b = Vec::<B>::with_capacity(size);
    let mut c = Vec::<C>::with_capacity(size);
    for x in input.into_iter() {
        let (x, y, z) = x;
        a.push(x);
        b.push(y);
        c.push(z);
    }
    (a, b, c)
}