//! 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()
}