googletest_json_serde/matchers/
each_matcher.rs

1/// Matches every element of a JSON array against the same matcher.
2///
3/// # Examples
4///
5/// ```rust
6/// # use googletest::prelude::*;
7/// # use googletest_json_serde::json;
8/// # use serde_json::json as j;
9/// assert_that!(j!([1, 2, 3]), json::each!(gt(0)));
10/// assert_that!(j!(["ab", "ax"]), json::each!(starts_with("a")));
11/// assert_that!(j!([1, 1]), json::each!(1));
12/// assert_that!(j!([j!(2), j!(2)]), json::each!(j!(2)));
13/// ```
14///
15/// # Errors
16///
17/// Fails when the value is not a JSON array or when any element fails the matcher.
18///
19/// # Supported Inputs
20/// - Literal JSON-compatible values
21/// - Direct `serde_json::Value`
22/// - Native googletest matchers
23#[macro_export]
24#[doc(hidden)]
25macro_rules! __json_each {
26    ($inner:expr) => {
27        $crate::matchers::__internal_unstable_do_not_depend_on_these::JsonEachMatcher::new(
28            $crate::matchers::__internal_unstable_do_not_depend_on_these::IntoJsonMatcher::into_json_matcher($inner)
29        )
30    };
31}
32
33pub mod internal {
34    use crate::matchers::__internal_unstable_do_not_depend_on_these::JsonMatcher;
35    use googletest::description::Description;
36    use googletest::matcher::{Matcher, MatcherBase, MatcherResult};
37    use serde_json::Value;
38
39    #[derive(MatcherBase)]
40    pub struct JsonEachMatcher {
41        inner: Box<dyn JsonMatcher>,
42    }
43
44    impl JsonEachMatcher {
45        pub fn new(inner: Box<dyn JsonMatcher>) -> Self {
46            Self { inner }
47        }
48    }
49
50    impl JsonMatcher for JsonEachMatcher {}
51    impl Matcher<&Value> for JsonEachMatcher {
52        fn matches(&self, actual: &Value) -> MatcherResult {
53            let arr = match actual {
54                Value::Array(a) => a,
55                _ => return MatcherResult::NoMatch,
56            };
57            for v in arr {
58                if self.inner.matches(v) == MatcherResult::NoMatch {
59                    return MatcherResult::NoMatch;
60                }
61            }
62            MatcherResult::Match
63        }
64
65        fn describe(&self, result: MatcherResult) -> Description {
66            match result {
67                MatcherResult::Match => format!(
68                    "JSON array where each element {}",
69                    self.inner.describe(MatcherResult::Match)
70                )
71                .into(),
72                MatcherResult::NoMatch => format!(
73                    "JSON array where each element {}",
74                    self.inner.describe(MatcherResult::NoMatch)
75                )
76                .into(),
77            }
78        }
79
80        fn explain_match(&self, actual: &Value) -> Description {
81            let arr = match actual {
82                Value::Array(a) => a,
83                _ => return Description::new().text("which is not a JSON array"),
84            };
85            for (i, v) in arr.iter().enumerate() {
86                if self.inner.matches(v) == MatcherResult::NoMatch {
87                    return format!(
88                        "element #{} ({}) did not match: {}",
89                        i,
90                        v,
91                        self.inner.explain_match(v)
92                    )
93                    .into();
94                }
95            }
96            format!(
97                "all {} elements matched: {}",
98                arr.len(),
99                self.inner.describe(MatcherResult::Match)
100            )
101            .into()
102        }
103    }
104}