#[doc(hidden)]
#[macro_export]
macro_rules! collect_or_drain {
($($bind:ident)+ = $iter:ident) => {
let $($bind)+ = $crate::force_type!(Vec<_>: ::core::iter::Iterator::collect($iter));
};
($iter:ident) => {
::core::iter::Iterator::for_each($iter, |_| ())
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! assert_matches_iter_all {
(
block: $block:expr,
$( fmt: ( $fmt_pattern:literal $($fmt_arg:tt)* ), )?
) => {
$block
};
(
if: $guard:expr,
block: $block:expr,
$( fmt: ( $fmt_pattern:literal $($fmt_arg:tt)* ), )?
) => {
if $guard {
$block
} else {
$crate::assertion_failure!(
"iterator pattern failed guard",
guard: stringify!($guard);
$($fmt_pattern $($fmt_arg)*)?
)
}
};
(
$(if: $guard:expr,)?
block: $block:expr,
iter: $iter:ident,
index: $index:expr,
expected_length: $target:expr,
patterns: [],
$( fmt: ( $fmt_pattern:literal $($fmt_arg:tt)* ), )?
) => {
match ::core::iter::Iterator::next(&mut $iter) {
::core::option::Option::Some(overflow) => $crate::assertion_failure!(
"iterable was too long",
actual_length: $index + 1 + ::core::iter::Iterator::count($iter),
expected_length: $index,
first_overflow debug: overflow;
$($fmt_pattern $($fmt_arg)*)?
),
::core::option::Option::None => $crate::assert_matches_iter_all!(
$(if: $guard,)?
block: $block,
$(fmt: ($fmt_pattern $($fmt_arg)*),)?
),
}
};
(
$(if: $guard:expr,)?
block: $block:expr,
iter: $iter:ident,
index: $index:expr,
expected_length: $target:expr,
patterns: [
$($($bind:ident)+ @)? .. $(,)?
],
$( fmt: ( $fmt_pattern:literal $($fmt_arg:tt)* ), )?
) => {{
let _discard = $target;
$(let $($bind)+ = $iter;)?
$crate::assert_matches_iter_all!(
$(if: $guard,)?
block: $block,
$(fmt: ($fmt_pattern $($fmt_arg)*),)?
)
}};
(
$(if: $guard:expr,)?
block: $block:expr,
iter: $iter:ident,
index: $index:expr,
expected_length: $target:expr,
patterns: [
$($($bind:ident)+ @)? .. ,
$($tail:tt)+
],
$( fmt: ( $fmt_pattern:literal $($fmt_arg:tt)* ), )?
) => {{
let buffer: [_; $crate::compute_target_length_no_slice!(0; $($tail)+)] = $crate::build_tail_array!(
iter: $iter,
index: $index,
expected_length: $target,
patterns: [$($tail)+],
$(fmt: ( $fmt_pattern $($fmt_arg)* ),)?
);
let mut buffer = $crate::iterators::LoopBuffer::new(buffer);
let mut index = $index;
let iter = buffer.bind_iterator($iter);
let iter = ::core::iter::Iterator::inspect(iter, |_| {index += 1});
let iter = ::core::iter::Iterator::fuse(iter);
$crate::collect_or_drain!($($($bind)+ = )? iter);
let tail = buffer.into_array();
let mut tail = ::core::iter::IntoIterator::into_iter(tail);
$crate::assert_matches_iter_all!(
$(if: $guard,)?
block: $block,
iter: tail,
index: index,
expected_length: $target,
patterns: [$($tail)+],
$(fmt: ($fmt_pattern $($fmt_arg)*),)?
)
}};
(
$(if: $guard:expr,)?
block: $block:expr,
iter: $iter:ident,
index: $index:expr,
expected_length: $target:expr,
patterns: [
$pattern:pat $(, $($tail:tt)*)?
],
$( fmt: ( $fmt_pattern:literal $($fmt_arg:tt)* ), )?
) => {
match ::core::iter::Iterator::next(&mut $iter) {
::core::option::Option::Some(item) => $crate::assert_item_matches!(
$index,
item,
$pattern => $crate::assert_matches_iter_all!(
$(if: $guard,)?
block: $block,
iter: $iter,
index: $index + 1,
expected_length: $target,
patterns: [
$($($tail)*)?
],
$(fmt: ($fmt_pattern $($fmt_arg)*),)?
),
$($fmt_pattern $($fmt_arg)*)?
),
::core::option::Option::None => $crate::assertion_failure!(
"iterable was too short",
actual_length: $index,
expected_length: $target;
$($fmt_pattern $($fmt_arg)*)?
),
}
};
}
#[cfg(test)]
mod tests {
use crate::{assert_matches, assert_panics};
fn get_data() -> impl IntoIterator<Item = Option<i32>> {
[Some(0), Some(1), None, None, Some(2), Some(3), None]
}
#[test]
fn basic_group_match() {
let data = get_data();
let value = assert_matches!(data, [
Some(a),
Some(b),
None,
_,
_,
Some(c),
None
] => a + b + c);
assert_eq!(value, 4);
}
#[test]
fn with_guard() {
let data = vec![&1, &2, &3];
let value = assert_matches!(
data.as_slice(),
[a, b, c] if **a == 1 => **a + **b + **c
);
assert_eq!(value, 6);
}
#[test]
fn nameless_temporary_borrow() {
fn make_vec() -> Vec<u32> {
vec![2, 3, 4]
}
let sum = assert_matches!(
make_vec().as_slice(),
[a, b, c] => a + b + c
);
assert_eq!(sum, 9);
}
#[test]
fn with_fmt_success() {
let data = get_data();
let value = assert_matches!(
data,
[Some(a), Some(1), None, None, Some(2), Some(b), None] => a + b,
"error: {}",
10
);
assert_eq!(value, 3);
}
#[test]
fn with_fmt_fail() {
let data = vec![None, Some(3), None];
assert_panics!(
assert_matches!(data, [None, Some(a), Some(b)] => a + b, "formatted error: {}", 10),
includes("assertion failed"),
includes("value from iterator does not match pattern"),
includes(" index: 2"),
includes(" value: None"),
includes("pattern: Some(b)"),
includes("formatted error: 10")
);
}
}