murf-macros 0.2.0

Murf's proc macros
Documentation
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};

use crate::{misc::GenericsEx, mock::context::MethodContextData};

use super::context::{Context, ContextData, MethodContext};

pub(crate) struct Shared {
    context: Context,
    expectations: Vec<MethodContext>,
}

impl Shared {
    pub(crate) fn new(context: Context) -> Self {
        Self {
            context,
            expectations: Vec::new(),
        }
    }

    pub(crate) fn add_expectation(&mut self, context: MethodContext) {
        self.expectations.push(context);
    }
}

impl ToTokens for Shared {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let Self {
            context,
            expectations,
        } = self;

        let ContextData {
            ident_murf,
            ident_state,
            ga_mock,
            trait_send,
            trait_sync,
            ..
        } = &**context;

        let ga_mock_phantom = ga_mock.make_phantom_data();
        let (ga_mock_impl, ga_mock_types, ga_mock_where) = ga_mock.split_for_impl();

        let expectation_field_defs = expectations.iter().map(|cx| {
            let field = &cx.ident_expectation_field;

            if cx.is_associated {
                quote! {
                    #field: Vec<Arc<Mutex<Box<dyn #ident_murf :: Expectation + Send + Sync + 'static>>>>
                }
            } else {
                quote! {
                    #field: Vec<Box<dyn #ident_murf :: Expectation #trait_send #trait_sync + 'mock>>
                }
            }
        });

        let expectation_field_ctor = expectations.iter().map(|cx| {
            let field = &cx.ident_expectation_field;

            quote! {
                #field: Vec::new()
            }
        });

        let expectation_err = format!("Mocked object '{ident_state}' has unfulfilled expectations");

        let expectations = expectations.iter().map(|cx| {
            let MethodContextData {
                is_associated,
                ident_expectation_field,
                ident_expectation_module,
                ..
            } = &**cx;

            let expectation_unwrap = is_associated.then(|| {
                quote! {
                    let ex = ex.lock();
                }
            });

            let expectation_cleanup = is_associated.then(|| {
                quote! {
                    #ident_expectation_module::cleanup_associated_expectations();
                }
            });

            quote! {
                for ex in &self.#ident_expectation_field {
                    #expectation_unwrap
                    if ex.is_ready() {
                        ex.set_done();
                    } else {
                        if !raise {
                            println!();
                            println!(#expectation_err);
                            raise = true;
                        }

                        println!("- {}", &ex);
                    }
                }

                self.#ident_expectation_field.clear();

                #expectation_cleanup
            }
        });

        tokens.extend(quote! {
            /// State that is shared between the different helper objects.
            pub struct Shared #ga_mock_types #ga_mock_where {
                #( #expectation_field_defs, )*
                _marker: #ga_mock_phantom,
            }

            impl #ga_mock_impl Shared #ga_mock_types #ga_mock_where {
                pub(super) fn checkpoint(&mut self) {
                    let mut raise = false;

                    #( #expectations )*

                    if raise {
                        println!();
                        panic!(#expectation_err);
                    }
                }
            }

            impl #ga_mock_impl Default for Shared #ga_mock_types #ga_mock_where {
                fn default() -> Self {
                    Self {
                        #( #expectation_field_ctor, )*
                        _marker: PhantomData,
                    }
                }
            }

            impl #ga_mock_impl Debug for Shared #ga_mock_types #ga_mock_where {
                fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
                    write!(f, "Shared")
                }
            }
        });
    }
}