diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib.rs | 245 | 
1 files changed, 245 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..9f404f1 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,245 @@ +#[macro_export] +macro_rules! mock { +    ( +        $mock: ident; + +        impl $mocked_trait: ident for $mocked_trait_target: ident { +            $( +                fn $func: ident$(< +                    $($type_param: tt$(: ($($type_param_bound: tt +)+))?),* +                >)?( +                    self: ($($self_type: tt)+), +                    $($func_param: ident: $func_param_type: ty),* +                )$( -> $return_type: ty)? +                    $(where ($($where: tt)*))?; +            )* +        } +    ) => { +        $crate::__private::paste! { +        mod [<__ $mock>] { +        use super::*; + +        pub struct $mock { +            $( +                [<$func _expectations>]: std::collections::HashMap< +                    Vec<$crate::__private::type_id::TypeID>, +                    [<$mock Expectation _ $func>]$(<$($crate::__to_unit!($type_param)),*>)? +                >, +            )* +        } + +        impl $mock +        { +            pub fn new() -> Self +            { +                Self { +                    $( +                        [<$func _expectations>]: std::collections::HashMap::new() +                    ),* +                } +            } + +            $( +                #[allow(unused)] +                pub(crate) fn [<expect_ $func>]$(< +                    $($type_param$(: $($type_param_bound +)*)?),* +                >)?(&mut self) +                    -> &mut [<$mock Expectation _ $func>]$(<$($type_param),*>)? +                $(where $($where)*)? +                { +                    let ids = vec![ +                        $($($crate::__private::type_id::TypeID::of::<$type_param>()),*)? +                    ]; + +                    let expectation = +                        [<$mock Expectation _ $func>]$(::<$($type_param),*>)?::new() +                            .strip_type_params(); + +                    self.[<$func _expectations>].insert(ids.clone(), expectation); + +                    self.[<$func _expectations>] +                        .get_mut(&ids) +                        .unwrap() +                        .with_type_params_mut() +                } +            )* +        } + +        impl $mocked_trait for $mock { +            $( +                fn $func$(<$($type_param$(: $($type_param_bound +)+)?),*>)?( +                    self: $($self_type)+, +                    $($func_param: $func_param_type),* +                )$( -> $return_type)? +                $(where $($where)*)? +                { +                    let ids = vec![ +                        $($($crate::__private::type_id::TypeID::of::<$type_param>()),*)? +                    ]; + +                    let expectation = self +                        .[<$func _expectations>] +                        .get(&ids) +                        .expect(concat!( +                            "No expectation found for function ", +                            stringify!($func) +                        )) +                        .with_type_params$(::<$($type_param),*>)?(); + +                    let Some(returning) = &expectation.returning else { +                        panic!(concat!( +                            "Expectation for function", +                            stringify!($func), +                            " is missing a function to call") +                        ); +                    }; + +                    returning(self, $($func_param),*) +                } +            )* +        } + +        $( +            #[allow(non_camel_case_types, non_snake_case)] +            pub struct [<$mock Expectation _ $func>]$(<$($type_param),*>)? { +                returning: Option< +                    fn( +                        $crate::__replace_ref_type!($($self_type)*, $mock), +                        $($func_param_type),* +                    )$( -> $return_type)?>, + +                $( +                    $([<$type_param _phantom>]: std::marker::PhantomData<$type_param>,)* +                )? +            } + +            impl$(<$($type_param),*>)? [<$mock Expectation _ $func>]$(< +                $($type_param),* +            >)? { +                #[allow(unused)] +                fn new() -> Self { +                    Self { +                        returning: None, +                        $( +                            $([<$type_param _phantom>]: std::marker::PhantomData,)* +                        )? +                    } +                } + +                #[allow(unused)] +                pub fn returning( +                    &mut self, +                    func: fn( +                        $crate::__replace_ref_type!($($self_type)*, $mock), +                        $($func_param_type),* +                    )$( -> $return_type)? +                ) -> &mut Self +                { +                    self.returning = Some(func); + +                    self +                } + +                #[allow(unused)] +                fn strip_type_params( +                    self, +                ) -> [<$mock Expectation _ $func>]$(<$($crate::__to_unit!($type_param)),*>)? +                { +                    $crate::__if_empty_else!(($($($type_param)*)?); { +                        // No type parameters are present +                        self +                    }, { +                        // Type parameters are present +                        unsafe { std::mem::transmute(self) } +                    }) + +                } +            } + +            impl [<$mock Expectation _ $func>]$(<$($crate::__to_unit!($type_param)),*>)? { +                fn with_type_params$(<$($type_param),*>)?( +                    &self, +                ) -> &[<$mock Expectation _ $func>]$(<$($type_param),*>)? +                { +                    unsafe { &*(self as *const Self).cast() } +                } + +                #[allow(unused)] +                fn with_type_params_mut$(<$($type_param),*>)?( +                    &mut self, +                ) -> &mut [<$mock Expectation _ $func>]$(<$($type_param),*>)? +                { +                    unsafe { &mut *(self as *mut Self).cast() } +                } +            } +        )* +        } + +        use [<__ $mock>]::$mock; +        } +    }; +} + +#[doc(hidden)] +pub mod __private +{ +    pub use paste::paste; + +    pub mod type_id +    { +        #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +        pub struct TypeID +        { +            id: usize, +        } + +        impl TypeID +        { +            #[inline] +            pub fn of<T>() -> Self +            { +                Self { +                    id: Self::of::<T> as usize, +                } +            } +        } +    } + +    #[macro_export] +    #[doc(hidden)] +    macro_rules! __to_unit { +        ($anything: tt) => { +            () +        }; +    } + +    #[macro_export] +    #[doc(hidden)] +    macro_rules! __replace_ref_type { +        (&$old_type: tt, $new_type: tt) => { +            &$new_type +        }; + +        (&mut $old_type: tt, $new_type: tt) => { +            &mut $new_type +        }; +    } + +    #[macro_export] +    #[doc(hidden)] +    macro_rules! __if_empty_else { +        (($($input: tt)*); $if_empty: block, $else: block) => { +            $crate::__if_empty_else!(@($($input)*); $if_empty, $else) +        }; + +        // Empty +        (@(); $if_empty: block, $else: block) => { +            $if_empty +        }; + +        // Not empty +        (@($($input: tt)+); $if_empty: block, $else: block) => { +            $else +        }; +    } +}  | 
