use alloc::borrow::ToOwned;
use alloc::format;
use alloc::vec::Vec;
use core::fmt::Debug;
use crate::actual::Actual;
use crate::{AssertThat, AssertrPartialEq, Mode, tracking::AssertionTracking};
#[allow(clippy::return_self_not_must_use)]
#[cfg_attr(feature = "fluent", assertr_derive::fluent_aliases)]
pub trait IteratorAssertions<'t, T: Debug, M: Mode> {
fn contains<'u, E>(self, expected: E) -> AssertThat<'u, (), M>
where
E: Debug,
T: AssertrPartialEq<E>,
't: 'u;
fn does_not_contain<'u, E>(self, not_expected: E) -> AssertThat<'u, (), M>
where
E: Debug,
T: AssertrPartialEq<E>,
't: 'u;
fn contains_exactly<'u, E>(self, expected: impl AsRef<[E]>) -> AssertThat<'u, (), M>
where
E: Debug,
T: PartialEq<E>,
T: AssertrPartialEq<E> + Debug,
't: 'u;
}
impl<'t, T, I, M: Mode> IteratorAssertions<'t, T, M> for AssertThat<'t, I, M>
where
T: Debug,
I: Iterator<Item = T>,
{
#[track_caller]
fn contains<'u, E>(self, expected: E) -> AssertThat<'u, (), M>
where
E: Debug,
T: Debug + AssertrPartialEq<E>,
't: 'u,
{
self.track_assertion();
let (actual, this) = self.replace_actual_with(Actual::Owned(()));
let actual = actual.unwrap_owned().collect::<Vec<_>>();
let expected = expected;
if !actual
.iter()
.any(|it| AssertrPartialEq::eq(it, &expected, None))
{
this.fail(format_args!(
"Actual: {actual:#?}\n\ndoes not contain expected: {expected:#?}\n",
));
}
this
}
#[track_caller]
fn does_not_contain<'u, E>(self, not_expected: E) -> AssertThat<'u, (), M>
where
E: Debug,
T: Debug + AssertrPartialEq<E>,
't: 'u,
{
self.track_assertion();
let (actual, this) = self.replace_actual_with(Actual::Owned(()));
let actual = actual.unwrap_owned().collect::<Vec<_>>();
if actual
.iter()
.any(|it| AssertrPartialEq::eq(it, ¬_expected, None))
{
this.fail(format_args!(
"Actual: {actual:#?}\n\ncontains unexpected: {not_expected:#?}\n",
));
}
this
}
#[track_caller]
fn contains_exactly<'u, E>(self, expected: impl AsRef<[E]>) -> AssertThat<'u, (), M>
where
E: Debug,
T: PartialEq<E>, T: AssertrPartialEq<E> + Debug,
't: 'u,
{
self.track_assertion();
let (actual, this) = self.replace_actual_with(Actual::Owned(()));
let actual = actual.unwrap_owned().collect::<Vec<_>>();
let expected = expected.as_ref();
let result = crate::util::slice::compare(actual.as_slice(), expected);
if !result.strictly_equal {
if !result.not_in_b.is_empty() {
this.add_detail_message(format!("Elements not expected: {:#?}", result.not_in_b));
}
if !result.not_in_a.is_empty() {
this.add_detail_message(format!("Elements not found: {:#?}", result.not_in_a));
}
if result.only_differing_in_order() {
this.add_detail_message("The order of elements does not match!".to_owned());
}
this.fail(format_args!(
"Actual: {actual:#?},\n\ndid not exactly match\n\nExpected: {expected:#?}\n",
));
}
this
}
}
#[allow(clippy::return_self_not_must_use)]
pub trait IntoIteratorAssertions<T: Debug> {
fn into_iter_contains<E>(self, expected: E) -> Self
where
E: Debug,
T: AssertrPartialEq<E>;
fn into_iter_does_not_contain<E>(self, not_expected: E) -> Self
where
E: Debug,
T: AssertrPartialEq<E>;
fn into_iter_contains_exactly<E>(self, expected: impl AsRef<[E]>) -> Self
where
E: Debug,
T: PartialEq<E>,
T: AssertrPartialEq<E> + Debug;
fn into_iter_iterator_is_empty(self) -> Self;
}
impl<T, I, M: Mode> IntoIteratorAssertions<T> for AssertThat<'_, I, M>
where
T: Debug,
for<'any> &'any I: IntoIterator<Item = &'any T>,
{
#[track_caller]
fn into_iter_contains<E>(self, expected: E) -> Self
where
E: Debug,
T: Debug + AssertrPartialEq<E>,
{
self.track_assertion();
let actual = self.actual().into_iter().collect::<Vec<_>>();
let expected = expected;
if !self
.actual()
.into_iter()
.any(|it| AssertrPartialEq::eq(it, &expected, None))
{
self.fail(format_args!(
"Actual: {actual:#?}\n\ndoes not contain expected: {expected:#?}\n",
));
}
self
}
#[track_caller]
fn into_iter_does_not_contain<E>(self, not_expected: E) -> Self
where
E: Debug,
T: Debug + AssertrPartialEq<E>,
{
self.track_assertion();
let actual = self.actual().into_iter().collect::<Vec<_>>();
if actual
.iter()
.any(|it| AssertrPartialEq::eq(*it, ¬_expected, None))
{
self.fail(format_args!(
"Actual: {actual:#?}\n\ncontains unexpected: {not_expected:#?}\n",
));
}
self
}
#[track_caller]
fn into_iter_contains_exactly<E>(self, expected: impl AsRef<[E]>) -> Self
where
E: Debug,
T: PartialEq<E>, T: AssertrPartialEq<E> + Debug,
{
self.track_assertion();
let actual = self.actual().into_iter().collect::<Vec<_>>();
let expected = expected.as_ref().iter().collect::<Vec<_>>();
let result = crate::util::slice::compare(actual.as_slice(), expected.as_slice());
if !result.strictly_equal {
if !result.not_in_b.is_empty() {
self.add_detail_message(format!("Elements not expected: {:#?}", result.not_in_b));
}
if !result.not_in_a.is_empty() {
self.add_detail_message(format!("Elements not found: {:#?}", result.not_in_a));
}
if result.only_differing_in_order() {
self.add_detail_message("The order of elements does not match!".to_owned());
}
self.fail(format_args!(
"Actual: {actual:#?},\n\ndid not exactly match\n\nExpected: {expected:#?}\n",
));
}
self
}
#[track_caller]
fn into_iter_iterator_is_empty(self) -> Self {
self.track_assertion();
if self.actual().into_iter().count() != 0 {
let actual = self.actual().into_iter().collect::<Vec<_>>();
self.fail(format_args!(
"Actual: {actual:#?}\n\nIs not empty!\n",
));
}
self
}
}
#[cfg(test)]
mod tests {
mod iterator_assertions {
mod contains {
use crate::prelude::*;
#[test]
fn succeeds_when_value_is_present() {
let values = [1, 2, 3];
let iter = values.iter();
assert_that!(iter).contains(&1);
}
#[test]
fn compiles_for_comparable_but_different_type() {
let values = vec!["foo"];
assert_that!(values).into_iter_contains("foo".to_string());
}
}
mod does_not_contain {
use crate::prelude::*;
use indoc::formatdoc;
#[test]
fn succeeds_when_value_is_absent() {
let values = [1, 2, 3];
let iter = values.iter();
assert_that!(iter).does_not_contain(&4);
}
#[test]
fn panics_when_value_is_present() {
assert_that_panic_by(|| {
let values = [1, 2, 3];
let iter = values.iter();
assert_that!(iter).with_location(false).does_not_contain(&2);
})
.has_type::<String>()
.is_equal_to(formatdoc! {"
-------- assertr --------
Actual: [
1,
2,
3,
]
contains unexpected: 2
-------- assertr --------
"});
}
}
}
mod into_iterator_assertions {
mod contains {
use crate::prelude::*;
#[test]
fn succeeds_when_value_is_present() {
let values = vec![1, 2, 3, 42];
assert_that!(values)
.into_iter_contains(1)
.into_iter_contains(42)
.into_iter_contains(3)
.into_iter_contains(2);
}
#[test]
fn compiles_for_comparable_but_different_type() {
let values = vec!["foo"];
assert_that!(values).into_iter_contains("foo".to_string());
}
}
mod does_not_contain {
use crate::prelude::*;
use indoc::formatdoc;
#[test]
fn succeeds_when_value_is_absent() {
let values = vec![1, 2, 3, 42];
assert_that!(values)
.into_iter_does_not_contain(5)
.into_iter_does_not_contain(99);
}
#[test]
fn compiles_for_comparable_but_different_type() {
let values = vec!["foo"];
assert_that!(values).into_iter_does_not_contain("bar".to_string());
}
#[test]
fn panics_when_value_is_present() {
assert_that_panic_by(|| {
let values = vec![1, 2, 3];
assert_that!(values)
.with_location(false)
.into_iter_does_not_contain(2);
})
.has_type::<String>()
.is_equal_to(formatdoc! {"
-------- assertr --------
Actual: [
1,
2,
3,
]
contains unexpected: 2
-------- assertr --------
"});
}
}
}
}