googletest_json_serde/matchers/
len_matcher.rs

1/// JSON length matcher.
2///
3/// This macro creates a matcher that asserts the length of a JSON array.
4///
5/// Two forms are supported:
6///
7/// * `json::len!(N)` — literal form, equivalent to `len(eq(N))`
8/// * `json::len!(matcher)` — matcher form, where any `Matcher<usize>` is accepted
9///
10/// Examples:
11///
12/// ```rust
13/// # use googletest::prelude::*;
14/// # use googletest_json_serde::json;
15/// # use serde_json::json as j;
16///
17/// verify_that!(j!(["a", "b", "c"]), json::len!(3));
18/// verify_that!(j!(["a", "b", "c"]), json::len!(ge(2)));
19/// verify_that!(j!(["a"]), not(json::len!(2)));
20/// ```
21///
22/// This matcher only applies to JSON arrays. For non-array values, it produces a
23/// descriptive “which is not a JSON array” failure message.
24#[macro_export]
25macro_rules! __json_len {
26    ($lit:literal) => {{
27        $crate::matchers::__internal_unstable_do_not_depend_on_these::JsonLenMatcher::new(
28            $crate::matchers::__internal_unstable_do_not_depend_on_these::IntoJsonMatcher::into_json_matcher(
29                googletest::matchers::eq($lit)
30            ),
31        )
32    }};
33    ($inner:expr) => {{
34        $crate::matchers::__internal_unstable_do_not_depend_on_these::JsonLenMatcher::new(
35            $crate::matchers::__internal_unstable_do_not_depend_on_these::IntoJsonMatcher::into_json_matcher($inner),
36        )
37    }};
38}
39
40pub mod internal {
41    use crate::matchers::__internal_unstable_do_not_depend_on_these::JsonMatcher;
42    use googletest::description::Description;
43    use googletest::matcher::{Matcher, MatcherBase, MatcherResult};
44    use serde_json::Value;
45
46    /// A JSON-aware length matcher that works for arrays and strings,
47    /// without requiring the type to implement IntoIterator.
48    #[derive(MatcherBase)]
49    pub struct JsonLenMatcher {
50        inner: Box<dyn JsonMatcher>,
51    }
52
53    impl JsonLenMatcher {
54        pub fn new(inner: Box<dyn JsonMatcher>) -> Self {
55            Self { inner }
56        }
57    }
58
59    impl Matcher<&Value> for JsonLenMatcher {
60        fn matches(&self, value: &Value) -> MatcherResult {
61            let len = match value {
62                Value::Array(arr) => arr.len(),
63                _ => return MatcherResult::NoMatch,
64            };
65            let as_value = Value::from(len);
66            self.inner.matches(&as_value)
67        }
68
69        fn describe(&self, result: MatcherResult) -> Description {
70            format!("has length, which {}", self.inner.describe(result)).into()
71        }
72
73        fn explain_match(&self, value: &Value) -> Description {
74            match value {
75                Value::Array(arr) => {
76                    let len = arr.len();
77                    let as_value = Value::from(len);
78                    format!(
79                        "which has length {}, {}",
80                        len,
81                        self.inner.explain_match(&as_value)
82                    )
83                    .into()
84                }
85                _ => Description::new().text("which is not a JSON array"),
86            }
87        }
88    }
89
90    impl JsonMatcher for JsonLenMatcher {}
91}