mry 0.7.1

A simple but powerful mocking library that supports struct, trait, and function.
Documentation
#[cfg(test)]
use parking_lot::Mutex;
#[cfg(test)]
use std::sync::Arc;

/// An enum describes what arguments are expected
pub struct Matcher<I>(Box<dyn Match<I> + Send>);

impl<I> Matcher<I> {
    #[cfg(test)]
    pub(crate) fn wrapped(self) -> Arc<Mutex<Matcher<I>>> {
        Arc::new(Mutex::new(self))
    }

    pub(crate) fn matches(&self, input: &I) -> bool {
        self.0.matches(input)
    }
}

#[cfg(test)]
impl<I> Matcher<I> {
    pub(crate) fn from_match(matcher: impl Match<I> + Send + 'static) -> Self {
        Self(Box::new(matcher))
    }

    pub(crate) fn any() -> Self {
        struct Any;
        impl<I> Match<I> for Any {
            fn matches(&self, _: &I) -> bool {
                true
            }
        }
        Self::from_match(Any)
    }

    pub(crate) fn never() -> Self {
        struct Never;
        impl<I> Match<I> for Never {
            fn matches(&self, _: &I) -> bool {
                false
            }
        }
        Self::from_match(Never)
    }
}

pub trait Match<I> {
    fn matches(&self, input: &I) -> bool;
}

pub enum ArgMatcher<I> {
    Fn(Box<dyn Fn(&I) -> bool + Send + 'static>),
    Eq {
        value: I,
        partial_eq: fn(&I, &I) -> bool,
    },
    Any,
    Never,
}

impl<I> ArgMatcher<I> {
    pub(crate) fn new_eq(value: I) -> Self
    where
        I: PartialEq + Send + 'static,
    {
        ArgMatcher::Fn(Box::new(move |input| *input == value))
    }

    pub(crate) fn matches(&self, input: &I) -> bool {
        match self {
            ArgMatcher::Fn(f) => f(input),
            ArgMatcher::Eq { value, partial_eq } => partial_eq(value, input),
            ArgMatcher::Any => true,
            ArgMatcher::Never => false,
        }
    }
}

impl<I: PartialEq + Send + 'static> From<I> for ArgMatcher<I> {
    fn from(value: I) -> Self {
        ArgMatcher::new_eq(value)
    }
}

impl From<&str> for ArgMatcher<String> {
    fn from(value: &str) -> Self {
        ArgMatcher::new_eq(value.to_string())
    }
}

mry_macros::create_matchers!();

#[cfg(test)]
mod tests {
    use super::*;

    struct EqMatcher<T>(T);

    impl<T: PartialEq> Match<T> for EqMatcher<T> {
        fn matches(&self, input: &T) -> bool {
            self.0 == *input
        }
    }

    impl<T: PartialEq + Send + 'static> Matcher<T> {
        pub(crate) fn new_eq(value: T) -> Self {
            Self(Box::new(EqMatcher(value)))
        }
    }

    #[test]
    fn from_str() {
        let matcher: ArgMatcher<String> = "A".to_string().into();
        assert!(matcher.matches(&"A".to_string()));
        assert!(!matcher.matches(&"B".to_string()));
    }

    #[test]
    fn matcher_two_values() {
        let matcher: Matcher<(u8, u16)> = Matcher::from_match((3u8.into(), 2u16.into()));
        assert!(matcher.matches(&(3, 2)));
        assert!(!matcher.matches(&(3, 1)));
        assert!(!matcher.matches(&(1, 2)));
        assert!(!matcher.matches(&(1, 1)));
    }
}