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 let_token = &attr.let_token;
    let pat_ident = &attr.pat_ident;
    let default = &attr.expr;
    let target = input.target();
    let ident = input.struct_name(attr);
    let state_ident = format_ident!("__anonymous_trait_state");
    let let_closures = input.methods().map(|method| {
        let ident = format_ident!("__anonymous_trait__{}", &method.sig.ident);
        let closure = crate::closure_expr::generate(target, method);
        quote! {
            #[allow(non_snake_case)]
            let mut #ident = #closure;
        }
    });
    let closures = input.methods().map(|method| {
        let method_ident = &method.sig.ident;
        let ident = format_ident!("__anonymous_trait__{}", &method.sig.ident);
        quote! {
            #method_ident: std::sync::Mutex::new(&mut #ident),
        }
    });
    quote! {
        let mut #state_ident = #default;
        #(#let_closures)*
        #let_token #pat_ident = #ident {
            #state_ident: &mut #state_ident,
            #(#closures)*
        };
    }
}

#[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::default()
        };
        let input = parse_quote! {
            impl Something for Cat {}
        };
        let actual = generate(&attr, &input);
        let expected = quote! {
            let mut __anonymous_trait_state = Cat::default();
            let my_mock = my_mock__Something {
                __anonymous_trait_state: &mut __anonymous_trait_state,
            };
        };
        assert_eq!(actual.to_string(), expected.to_string());
    }

    #[test]
    fn let_mut() {
        let attr = parse_quote! {
            let mut my_mock = Cat::default()
        };
        let input = parse_quote! {
            impl Something for Cat {}
        };
        let actual = generate(&attr, &input);
        let expected = quote! {
            let mut __anonymous_trait_state = Cat::default();
            let mut my_mock =my_mock__Something {
                __anonymous_trait_state: &mut __anonymous_trait_state,
            };
        };
        assert_eq!(actual.to_string(), expected.to_string());
    }

    #[test]
    fn closures() {
        let attr = parse_quote! {
            let my_mock = Cat::default()
        };
        let input = parse_quote! {
            impl Something for Cat {
                fn meow(&self) -> String {
                    "meow".to_string()
                }
                fn change_name(&mut self, name: String) {
                    self.name = name;
                }
            }
        };
        let actual = generate(&attr, &input);
        let expected = quote! {
            let mut __anonymous_trait_state = Cat::default();
            #[allow(non_snake_case)]
            let mut __anonymous_trait__meow = |__anonymous_trait_state: &Cat| -> String {
                "meow".to_string()
            };
            #[allow(non_snake_case)]
            let mut __anonymous_trait__change_name = |__anonymous_trait_state: &mut Cat, name: String| {
                __anonymous_trait_state.name = name;
            };
            let my_mock = my_mock__Something {
                __anonymous_trait_state: &mut __anonymous_trait_state,
                meow: std::sync::Mutex::new(&mut __anonymous_trait__meow),
                change_name: std::sync::Mutex::new(&mut __anonymous_trait__change_name),
            };
        };
        assert_eq!(actual.to_string(), expected.to_string());
    }

    #[test]
    fn state_initial_value() {
        let attr = parse_quote! {
            let my_mock = Cat::new(aaa)
        };
        let input = parse_quote! {
            impl Something for Cat {
            }
        };
        let actual = generate(&attr, &input);
        let expected = quote! {
            let mut __anonymous_trait_state = Cat::new(aaa);
            let my_mock = my_mock__Something {
                __anonymous_trait_state: &mut __anonymous_trait_state,
            };
        };
        assert_eq!(actual.to_string(), expected.to_string());
    }
}