anonymous-trait 0.1.3

Anonymous trait implementation with capturing the environment
Documentation
use proc_macro2::TokenStream;
use quote::{format_ident, quote};

use crate::{attr_syntax::LetDefault, impl_syntax::AnonymousImpl};

pub(crate) fn generate(attr: &LetDefault, input: &AnonymousImpl) -> TokenStream {
    let target = input.target();
    let ident = input.struct_name(attr);
    let state_ident = format_ident!("__anonymous_trait_state");
    let state_lifetime = quote!('__anonymous_trait_state);
    let generics = input.methods().map(|method| {
        let method_ident = &method.sig.ident;
        let closure_type = crate::closure_type::generate(target, method);
        quote! {
            #method_ident: #closure_type,
        }
    });
    let fields = input.methods().map(|method| {
        let method_ident = &method.sig.ident;
        quote! {
            #method_ident: std::sync::Mutex<&#state_lifetime mut #method_ident>,
        }
    });
    quote! {
        #[allow(non_camel_case_types)]
        struct #ident<
            #state_lifetime,
            #(#generics)*
        > {
            #state_ident: & #state_lifetime mut #target,
            #(#fields)*
        }
    }
}

#[cfg(test)]
mod tests {
    use pretty_assertions::assert_eq;
    use syn::parse_quote;

    use super::*;

    #[test]
    fn empty() {
        let attr = parse_quote! {
            let my_mock = Cat
        };
        let input = parse_quote! {
            impl Something for Cat {}
        };
        let actual = generate(&attr, &input);
        let expected = quote! {
            #[allow(non_camel_case_types)]
            struct my_mock__Something<
                '__anonymous_trait_state,
            > {
                __anonymous_trait_state: &'__anonymous_trait_state mut Cat,
            }
        };
        assert_eq!(actual.to_string(), expected.to_string());
    }

    #[test]
    fn fields() {
        let attr = parse_quote! {
            let my_mock = Cat
        };
        let input = parse_quote! {
            impl Something for Cat {
                fn meow(&self) -> String {
                    unimplemented!()
                }
                fn change_name(&mut self, name: String) {
                    unimplemented!()
                }
            }
        };
        let actual = generate(&attr, &input);
        let expected = quote! {
            #[allow(non_camel_case_types)]
            struct my_mock__Something<
                '__anonymous_trait_state,
                meow: FnMut(&Cat) -> String,
                change_name: FnMut(&mut Cat, String),
            > {
                __anonymous_trait_state: &'__anonymous_trait_state mut Cat,
                meow: std::sync::Mutex<&'__anonymous_trait_state mut meow>,
                change_name: std::sync::Mutex<&'__anonymous_trait_state mut change_name>,
            }
        };
        assert_eq!(actual.to_string(), expected.to_string());
    }
}