use crate::{
description::Description,
matcher::{Describable, Matcher, MatcherResult},
};
use std::fmt::Debug;
pub fn char_count<E: Matcher<usize>>(expected: E) -> __internal::CharCountMatcher<E> {
__internal::CharCountMatcher { expected }
}
pub mod __internal {
use super::*;
#[doc(hidden)]
pub struct CharCountMatcher<E> {
pub(super) expected: E,
}
impl<T: Debug + ?Sized + AsRef<str>, E: Matcher<usize>> Matcher<T> for CharCountMatcher<E> {
fn matches(&self, actual: &T) -> MatcherResult {
self.expected.matches(&actual.as_ref().chars().count())
}
fn explain_match(&self, actual: &T) -> Description {
let actual_size = actual.as_ref().chars().count();
format!(
"which has character count {}, {}",
actual_size,
self.expected.explain_match(&actual_size)
)
.into()
}
}
impl<E: Matcher<usize>> Describable for CharCountMatcher<E> {
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => format!(
"has character count, which {}",
self.expected.describe(MatcherResult::Match)
)
.into(),
MatcherResult::NoMatch => format!(
"has character count, which {}",
self.expected.describe(MatcherResult::NoMatch)
)
.into(),
}
}
}
}
#[cfg(test)]
mod tests {
use super::char_count;
use crate::description::Description;
use crate::matcher::{Describable, Matcher, MatcherResult};
use crate::prelude::*;
use indoc::indoc;
use std::fmt::Debug;
use std::marker::PhantomData;
#[test]
fn char_count_matches_string_slice() -> TestResult<()> {
let value = "abcd";
verify_that!(value, char_count(eq(4)))
}
#[test]
fn char_count_matches_owned_string() -> TestResult<()> {
let value = String::from("abcd");
verify_that!(value, char_count(eq(4)))
}
#[test]
fn char_count_counts_non_ascii_characters_correctly() -> TestResult<()> {
let value = "äöüß";
verify_that!(value, char_count(eq(4)))
}
#[test]
fn char_count_explains_match() -> TestResult<()> {
struct TestMatcher<T>(PhantomData<T>);
impl<T: Debug> Matcher<T> for TestMatcher<T> {
fn matches(&self, _: &T) -> MatcherResult {
false.into()
}
fn explain_match(&self, _: &T) -> Description {
"called explain_match".into()
}
}
impl<T: Debug> Describable for TestMatcher<T> {
fn describe(&self, _: MatcherResult) -> Description {
"called described".into()
}
}
verify_that!(
char_count(TestMatcher(Default::default())).explain_match(&"A string"),
displays_as(eq("which has character count 8, called explain_match"))
)
}
#[test]
fn char_count_has_correct_failure_message() -> TestResult<()> {
let result = verify_that!("äöüß", char_count(eq(3)));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
r#"
Value of: "äöüß"
Expected: has character count, which is equal to 3
Actual: "äöüß",
which has character count 4, which isn't equal to 3"#
))))
)
}
}