googletest_json_serde/matchers/
json_matcher.rs

1//! Utility matchers and macros for concise JSON assertions using googletest.
2
3use crate::matchers::__internal_unstable_do_not_depend_on_these::JsonPredicateMatcher;
4
5/// Matches JSON null values.
6pub fn is_null() -> JsonPredicateMatcher {
7    JsonPredicateMatcher::new(|v| v.is_null(), "JSON null", "which is not JSON null")
8}
9
10/// Matches any JSON value except null.
11pub fn is_not_null() -> JsonPredicateMatcher {
12    JsonPredicateMatcher::new(|v| !v.is_null(), "not JSON null", "which is JSON null")
13}
14
15/// Matches any JSON value except null.
16#[deprecated(since = "0.2.2", note = "Use `is_not_null` instead")]
17pub fn any_value() -> JsonPredicateMatcher {
18    JsonPredicateMatcher::new(
19        |v| !v.is_null(),
20        "any JSON value",
21        "which is not any JSON value",
22    )
23}
24
25/// Matches JSON string values.
26pub fn is_string() -> JsonPredicateMatcher {
27    JsonPredicateMatcher::new(
28        |v| v.is_string(),
29        "a JSON string",
30        "which is not a JSON string",
31    )
32}
33
34/// Matches JSON number values.
35pub fn is_number() -> JsonPredicateMatcher {
36    JsonPredicateMatcher::new(
37        |v| v.is_number(),
38        "a JSON number",
39        "which is not a JSON number",
40    )
41}
42
43/// Matches JSON boolean values.
44pub fn is_boolean() -> JsonPredicateMatcher {
45    JsonPredicateMatcher::new(
46        |v| v.is_boolean(),
47        "a JSON boolean",
48        "which is not a JSON boolean",
49    )
50}
51
52/// Matches JSON array values.
53pub fn is_array() -> JsonPredicateMatcher {
54    JsonPredicateMatcher::new(
55        |v| v.is_array(),
56        "a JSON array",
57        "which is not a JSON array",
58    )
59}
60
61/// Matches JSON object values.
62pub fn is_object() -> JsonPredicateMatcher {
63    JsonPredicateMatcher::new(
64        |v| v.is_object(),
65        "a JSON object",
66        "which is not a JSON object",
67    )
68}
69
70#[doc(hidden)]
71pub mod internal {
72    use googletest::description::Description;
73    use googletest::matcher::MatcherResult::{Match, NoMatch};
74    use googletest::matcher::{Matcher, MatcherBase, MatcherResult};
75    use serde_json::Value;
76
77    #[derive(MatcherBase)]
78    pub struct JsonPredicateMatcher {
79        predicate: fn(&Value) -> bool,
80        positive_description: &'static str,
81        negative_description: &'static str,
82    }
83    impl JsonMatcher for JsonPredicateMatcher {}
84
85    impl JsonPredicateMatcher {
86        pub fn new(
87            predicate: fn(&Value) -> bool,
88            positive_description: &'static str,
89            negative_description: &'static str,
90        ) -> Self {
91            Self {
92                predicate,
93                positive_description,
94                negative_description,
95            }
96        }
97    }
98    impl Matcher<&Value> for JsonPredicateMatcher {
99        fn matches(&self, actual: &Value) -> MatcherResult {
100            match (self.predicate)(actual) {
101                true => Match,
102                false => NoMatch,
103            }
104        }
105
106        fn describe(&self, matcher_result: MatcherResult) -> Description {
107            match matcher_result {
108                Match => self.positive_description.into(),
109                NoMatch => self.negative_description.into(),
110            }
111        }
112        fn explain_match(&self, actual: &Value) -> Description {
113            let kind = match actual {
114                Value::String(_) => "a JSON string",
115                Value::Number(_) => "a JSON number",
116                Value::Bool(_) => "a JSON boolean",
117                Value::Null => "a JSON null",
118                Value::Array(_) => "a JSON array",
119                Value::Object(_) => "a JSON object",
120            };
121            Description::new().text(format!("which is {kind}"))
122        }
123    }
124
125    /// Marker trait for JSON-aware matchers.
126    pub trait JsonMatcher: for<'a> Matcher<&'a Value> {}
127
128    /// Trait for converting into a boxed JSON matcher.
129    pub trait IntoJsonMatcher<T> {
130        fn into_json_matcher(self) -> Box<dyn for<'a> Matcher<&'a Value>>;
131    }
132
133    impl<J> IntoJsonMatcher<()> for J
134    where
135        J: JsonMatcher + 'static,
136    {
137        fn into_json_matcher(self) -> Box<dyn for<'a> Matcher<&'a Value>> {
138            Box::new(self)
139        }
140    }
141}