matching!() { /* proc-macro */ }
Expand description
Macro to ease call pattern matching for function arguments.
The macro produces a closure reference expression suitable for passing to some_call
, etc.
Its syntax takes inspiration from std::matches and works similarly, except that the value to match can be removed as a macro argument, since it is instead received as the closure argument.
Two main forms of syntaxes are supported:
- Simple form, e.g.
matching!(1, 2)
: A single tuple pattern to match the entire input tuple. - Disjunctive form, e.g.
matching!((1, 2) | (3, 4) | (5, 6))
: Each operand to the|
sigil is a standalone tuple pattern, with the behaviour that the complete pattern is matching if at least one of the standalone tuple patterns are matching.
if
guards are also supported.
Example
#[unimock(api=Mock)]
trait Trait {
fn one(&self, a: &str);
fn three(&self, a: &str, b: &str, c: &str);
}
fn one_str() {
fn args(_: &dyn Fn(&mut macro_api::Matching<Mock::one>)) {}
args(matching!("a"));
}
fn three_strs() {
fn args(_: &dyn Fn(&mut macro_api::Matching<Mock::three>)) {}
args(matching!("a", _, "c"));
args(matching!(("a", "b", "c") | ("d", "e", "f")));
args(matching!(("a", b, "c") if b.contains("foo")));
}
Auto-“coercions”
Since the input expression being matched is generated by the macro,
you would normally suffer from the following problem when matching some non-&str
function input:
let string = String::new();
match &string {
"foo" => true, // expected struct `String`, found `str`
_ => false,
}
To help ergonomics, the matching
macro recognizes certain literals used in the patterns,
and performs appropriate type conversion at the correct places:
pub struct Newtype(String);
#[unimock(api=Mock)]
trait Trait {
fn interesting_args(
&self,
a: String,
b: std::borrow::Cow<'static, str>,
c: Newtype,
d: i32
);
}
fn args(_: &dyn Fn(&mut macro_api::Matching<Mock::interesting_args>)) {}
args(matching! {("a", _, "c", _) | (_, "b", _, 42)});
// Newtype works by implementing the following:
impl std::convert::AsRef<str> for Newtype {
fn as_ref(&self) -> &str {
self.0.as_str()
}
}
Internally it works by calling macro_api::as_str_ref on inputs matched by a string literal.
Matching using Eq
Since patterns in Rust are somewhat limited, the matching macro also supports matching using Eq.
A single argument changes to Eq
matching by enclosing that argument within eq!(_)
or ne!(_)
:
#[derive(Eq, PartialEq)]
pub struct Data(Vec<i32>);
#[unimock(api=Mock)]
trait Trait {
fn func(&self, arg: Data) -> &str;
}
let u = Unimock::new((
Mock::func
.each_call(matching!(eq!(&Data(vec![]))))
.returns("empty"),
Mock::func
.each_call(matching!(ne!(&Data(vec![0]))))
.returns("non-zero"),
Mock::func
.each_call(matching!(_))
.returns("other")
));
assert_eq!("empty", <Unimock as Trait>::func(&u, Data(vec![])));
assert_eq!("non-zero", <Unimock as Trait>::func(&u, Data(vec![42])));
assert_eq!("other", <Unimock as Trait>::func(&u, Data(vec![0])));