1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
use super::ArgMatcher;
use std::fmt::{self, Formatter};
struct FromFn<F> {
message: String,
matcher: F,
}
impl<Arg, F> ArgMatcher<Arg> for FromFn<F>
where
Arg: ?Sized,
F: Fn(&Arg) -> bool,
{
fn matches(&self, argument: &Arg) -> bool {
let matcher = &self.matcher;
matcher(argument)
}
}
impl<F> fmt::Display for FromFn<F> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(&self.message)
}
}
#[doc(hidden)]
pub fn from_fn<Arg>(
matcher: impl Fn(&Arg) -> bool,
message: impl fmt::Display,
) -> impl ArgMatcher<Arg>
where
Arg: ?Sized,
{
FromFn {
matcher,
message: message.to_string(),
}
}
/// Returns an [`ArgMatcher`] that succeeds if the provided closure
/// returns `true`.
///
/// The returned `Argmatcher` implements [`fmt::Display`] using the
/// string representation of the closure, so it is only recommended
/// for use with simple closures. For complex argument matching,
/// implement your own [`ArgMatcher`] to make the expectation message
/// more specific and less verbose.
///
/// # Examples
///
/// ```
/// use faux::{from_fn, matcher::ArgMatcher};
///
/// let contains_hello = from_fn!(|message: &str| message.contains("hello"));
/// assert!(contains_hello.matches("hello world"));
/// assert!(!contains_hello.matches("bye world"));
/// println!("{}", contains_hello); // '|message: &str| message.contains("hello")'
/// ```
///
/// ## Usage within when!
///
/// [`faux::when!`](crate::when!) does not have a special syntax for
/// this matcher. See the [matcher
/// syntax](macro.when.html#argument-matchers) for more
/// information.
///
/// ```ignore
/// // we can call it within `when!`
/// faux::when!(my_struct.some_method(_ = faux::from_fn!(|_: &i32| true)))
/// .then_return(5);
///
/// // or call outside `when!`
/// faux::when!(my_struct.some_method)
/// .with_args((faux::from_fn!(|_: &i32| true),)).then_return(5);
/// ```
#[macro_export]
macro_rules! from_fn {
($matcher:expr) => {
faux::matcher::from_fn($matcher, stringify!($matcher))
};
}
/// Returns an [`ArgMatcher`] that succeeds if the provided pattern
/// matches.
///
/// The returned `Argmatcher` implements [`fmt::Display`] using the
/// string representation of the pattern.
///
/// This macro has two forms:
/// * `pattern!(pattern)`
/// * `pattern!(type => pattern)`
///
/// Use the latter if you need to be specific about the type being
/// matched against.
///
/// # Examples
///
/// ```
/// use faux::{pattern, matcher::ArgMatcher};
///
/// // the type can be implicit
/// let is_alphabet = pattern!('A'..='Z' | 'a'..='z');
/// assert!(is_alphabet.matches(&'f'));
/// assert!(!is_alphabet.matches(&' '));
///
/// // or the type can be explicit
/// let exists_more_than_two = pattern!(Option<_> => Some(x) if *x > 2);
/// assert!(exists_more_than_two.matches(&Some(4)));
/// assert!(!exists_more_than_two.matches(&Some(1)));
///
/// println!("{}", exists_more_than_two); // 'Some(x) if *x > 2'
/// ```
///
/// ## Usage within when!
///
/// [`faux::when!`](crate::when!) does not have a special syntax for
/// this matcher. See the [matcher
/// syntax](macro.when.html#argument-matchers) for more
/// information.
///
/// ```ignore
/// // we can call it within `when!`
/// faux::when!(my_struct.some_method(_ = faux::pattern!(|_: &i32| true)))
/// .then_return(5);
///
/// // or call outside `when!`
/// faux::when!(my_struct.some_method)
/// .with_args((faux::pattern!(|_: &i32| true),)).then_return(5);
#[macro_export]
macro_rules! pattern {
($( $pattern:pat )|+ $( if $guard: expr )? $(,)?) => (
faux::matcher::from_fn(
move |arg| matches!(arg, $($pattern)|+ $(if $guard)?),
stringify!($($pattern)|+ $(if $guard)?),
)
);
($ty:ty => $( $pattern:pat )|+ $( if $guard: expr )? $(,)?) => (
faux::matcher::from_fn(
move |arg: &$ty| matches!(arg, $($pattern)|+ $(if $guard)?),
stringify!($($pattern)|+ $(if $guard)?),
)
);
}