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