googletest_json_serde/matchers/
len_matcher.rs

1/// Matches the length of a JSON array against a literal or 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/// verify_that!(j!(["a", "b", "c"]), json::len!(3));
10/// verify_that!(j!(["a", "b", "c"]), json::len!(ge(2)));
11/// verify_that!(j!(["a", "b", "c"]), json::len!(j!(3)));
12/// assert_that!(j!(["a"]), not(json::len!(2)));
13/// ```
14///
15/// # Errors
16///
17/// Fails when the value is not a JSON array.
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_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}