//! Macros for Ridicule, a mocking library supporting non-static generics. #![deny(clippy::all, clippy::pedantic, missing_docs)] use proc_macro::TokenStream; use proc_macro_error::{proc_macro_error, ResultExt}; use quote::{format_ident, quote}; use syn::{parse, ImplItem}; use crate::expectation::Expectation; use crate::mock::Mock; use crate::mock_input::MockInput; mod expectation; mod mock; mod mock_input; mod syn_ext; mod util; /// Creates a mock. /// /// # Examples /// ``` /// use ridicule::mock; /// /// trait Foo /// { /// fn bar(&self, a: A) -> B; /// } /// /// mock! { /// MockFoo {} /// /// impl Foo for MockFoo /// { /// fn bar(&self, a: A) -> B; /// } /// } /// /// fn main() /// { /// let mut mock_foo = MockFoo::new(); /// /// mock_foo /// .expect_bar() /// .returning(|foo, a: u32| format!("Hello {a}")); /// /// assert_eq!(mock_foo.bar::(123), "Hello 123"); /// } /// ``` #[proc_macro] #[proc_macro_error] pub fn mock(input_stream: TokenStream) -> TokenStream { let input = parse::(input_stream.clone()).unwrap_or_abort(); let mock_ident = input.mock; let mock_mod_ident = format_ident!("__{mock_ident}"); let method_items = input .item_impl .items .into_iter() .filter_map(|item| match item { ImplItem::Method(item_method) => Some(item_method), _ => None, }) .collect::>(); let mock = Mock::new( mock_ident.clone(), input.mocked_trait, &method_items, input.item_impl.generics.clone(), ); let expectations = method_items.iter().map(|item_method| { Expectation::new( &mock_ident, item_method, input.item_impl.generics.params.clone(), ) }); quote! { mod #mock_mod_ident { use super::*; #mock #(#expectations)* } use #mock_mod_ident::#mock_ident; } .into() }