googletest_json_serde/matchers/
value_matcher.rs1#[deprecated(since = "0.2.0", note = "please use `json::primitive!` instead")]
24#[macro_export]
25#[doc(hidden)]
26macro_rules! __json_value {
27 ($matcher:expr) => {
28 $crate::__json_primitive!($matcher)
29 };
30}
31
32#[macro_export]
53#[doc(hidden)]
54macro_rules! __json_primitive {
55 ($matcher:expr) => {
56 $crate::matchers::__internal_unstable_do_not_depend_on_these::JsonValueMatcher::new(
57 $matcher,
58 )
59 };
60}
61
62pub fn is_null() -> crate::matchers::__internal_unstable_do_not_depend_on_these::IsJsonNull {
63 crate::matchers::__internal_unstable_do_not_depend_on_these::IsJsonNull
64}
65
66pub fn any_value()
67-> crate::matchers::__internal_unstable_do_not_depend_on_these::JsonAnyValueMatcher {
68 crate::matchers::__internal_unstable_do_not_depend_on_these::JsonAnyValueMatcher
69}
70
71#[doc(hidden)]
72pub mod internal {
73 use googletest::description::Description;
74 use googletest::matcher::{Matcher, MatcherBase, MatcherResult};
75 use serde_json::Value;
76
77 #[doc(hidden)]
78 #[derive(MatcherBase)]
79 pub struct JsonValueMatcher<M, T> {
80 inner: M,
81 phantom: std::marker::PhantomData<T>,
82 }
83
84 impl<M, T> JsonValueMatcher<M, T> {
85 pub fn new(inner: M) -> Self {
86 Self {
87 inner,
88 phantom: std::marker::PhantomData,
89 }
90 }
91 }
92
93 impl<M> Matcher<&Value> for JsonValueMatcher<M, String>
94 where
95 M: for<'a> Matcher<&'a str>,
96 {
97 fn matches(&self, actual: &Value) -> MatcherResult {
98 match actual {
99 Value::String(s) => self.inner.matches(s),
100 _ => MatcherResult::NoMatch,
101 }
102 }
103 fn describe(&self, r: MatcherResult) -> Description {
104 self.inner.describe(r)
105 }
106 fn explain_match(&self, actual: &Value) -> Description {
107 match actual {
108 Value::String(s) => self.inner.explain_match(s),
109 _ => Description::new().text("which is not a JSON string".to_string()),
110 }
111 }
112 }
113
114 impl<M> Matcher<&Value> for JsonValueMatcher<M, i64>
115 where
116 M: Matcher<i64>,
117 {
118 fn matches(&self, actual: &Value) -> MatcherResult {
119 match actual {
120 Value::Number(n) => n
121 .as_i64()
122 .map_or(MatcherResult::NoMatch, |i| self.inner.matches(i)),
123 _ => MatcherResult::NoMatch,
124 }
125 }
126 fn describe(&self, r: MatcherResult) -> Description {
127 self.inner.describe(r)
128 }
129 fn explain_match(&self, actual: &Value) -> Description {
130 match actual {
131 Value::Number(n) => match n.as_i64() {
132 Some(i) => self.inner.explain_match(i),
133 None => Description::new().text(format!("number out of i64 range: {n}")),
134 },
135 _ => Description::new().text("which is not a JSON number"),
136 }
137 }
138 }
139
140 impl<M> Matcher<&Value> for JsonValueMatcher<M, f64>
141 where
142 M: Matcher<f64>,
143 {
144 fn matches(&self, actual: &Value) -> MatcherResult {
145 match actual {
146 Value::Number(n) => n
147 .as_f64()
148 .map_or(MatcherResult::NoMatch, |f| self.inner.matches(f)),
149 _ => MatcherResult::NoMatch,
150 }
151 }
152 fn describe(&self, r: MatcherResult) -> Description {
153 self.inner.describe(r)
154 }
155 fn explain_match(&self, actual: &Value) -> Description {
156 match actual {
157 Value::Number(n) => match n.as_f64() {
158 Some(f) => self.inner.explain_match(f),
159 None => Description::new().text(format!("number not convertible to f64: {n}")),
160 },
161 _ => Description::new().text("which is not a JSON number"),
162 }
163 }
164 }
165
166 impl<M> Matcher<&Value> for JsonValueMatcher<M, bool>
167 where
168 M: Matcher<bool>,
169 {
170 fn matches(&self, actual: &Value) -> MatcherResult {
171 match actual {
172 Value::Bool(b) => self.inner.matches(*b),
173 _ => MatcherResult::NoMatch,
174 }
175 }
176 fn describe(&self, r: MatcherResult) -> Description {
177 self.inner.describe(r)
178 }
179 fn explain_match(&self, actual: &Value) -> Description {
180 match actual {
181 Value::Bool(b) => self.inner.explain_match(*b),
182 _ => Description::new().text("which is not a JSON boolean"),
183 }
184 }
185 }
186
187 #[derive(MatcherBase)]
188 pub struct IsJsonNull;
189 impl Matcher<&Value> for IsJsonNull {
190 fn matches(&self, actual: &Value) -> MatcherResult {
191 match actual {
192 Value::Null => MatcherResult::Match,
193 _ => MatcherResult::NoMatch,
194 }
195 }
196
197 fn describe(&self, _: MatcherResult) -> Description {
198 Description::new().text("JSON null")
199 }
200
201 fn explain_match(&self, actual: &Value) -> Description {
202 match actual {
203 Value::Null => Description::new().text("which is null"),
204 _ => Description::new().text("which is not JSON null"),
205 }
206 }
207 }
208
209 #[derive(MatcherBase)]
210 pub struct JsonAnyValueMatcher;
211 impl JsonMatcher for JsonAnyValueMatcher {}
212 impl Matcher<&Value> for JsonAnyValueMatcher {
213 fn matches(&self, actual: &Value) -> MatcherResult {
214 match actual {
215 Value::Null => MatcherResult::NoMatch,
216 _ => MatcherResult::Match,
217 }
218 }
219
220 fn describe(&self, matcher_result: MatcherResult) -> Description {
221 match matcher_result {
222 MatcherResult::Match => Description::new().text("is any JSON value"),
223 MatcherResult::NoMatch => Description::new().text("never matches"),
224 }
225 }
226
227 fn explain_match(&self, actual: &Value) -> Description {
228 Description::new().text(format!("which is {actual}"))
229 }
230 }
231
232 pub trait JsonMatcher: for<'a> Matcher<&'a Value> {}
234
235 impl<M, T> JsonMatcher for JsonValueMatcher<M, T> where
236 JsonValueMatcher<M, T>: for<'a> Matcher<&'a Value>
237 {
238 }
239
240 impl JsonMatcher for IsJsonNull {}
241
242 pub trait IntoJsonMatcher<T> {
244 fn into_json_matcher(self) -> Box<dyn for<'a> Matcher<&'a Value>>;
245 }
246
247 impl<J> IntoJsonMatcher<()> for J
248 where
249 J: JsonMatcher + 'static,
250 {
251 fn into_json_matcher(self) -> Box<dyn for<'a> Matcher<&'a Value>> {
252 Box::new(self)
253 }
254 }
255
256 impl<M> IntoJsonMatcher<i64> for M
257 where
258 M: Matcher<i64> + 'static,
259 {
260 fn into_json_matcher(self) -> Box<dyn for<'a> Matcher<&'a Value>> {
261 Box::new(JsonValueMatcher::<M, i64>::new(self))
262 }
263 }
264
265 impl<M> Matcher<&Value> for JsonValueMatcher<M, u64>
266 where
267 M: Matcher<u64>,
268 {
269 fn matches(&self, actual: &Value) -> MatcherResult {
270 match actual {
271 Value::Number(n) => n
272 .as_u64()
273 .map_or(MatcherResult::NoMatch, |u| self.inner.matches(u)),
274 _ => MatcherResult::NoMatch,
275 }
276 }
277 fn describe(&self, r: MatcherResult) -> Description {
278 self.inner.describe(r)
279 }
280 fn explain_match(&self, actual: &Value) -> Description {
281 match actual {
282 Value::Number(n) => match n.as_u64() {
283 Some(u) => self.inner.explain_match(u),
284 None => Description::new().text(format!("number out of u64 range: {n}")),
285 },
286 _ => Description::new().text("which is not a JSON number"),
287 }
288 }
289 }
290
291 impl<M> IntoJsonMatcher<u64> for M
292 where
293 M: Matcher<u64> + 'static,
294 {
295 fn into_json_matcher(self) -> Box<dyn for<'a> Matcher<&'a Value>> {
296 Box::new(JsonValueMatcher::<M, u64>::new(self))
297 }
298 }
299
300 impl<M> IntoJsonMatcher<f64> for M
301 where
302 M: Matcher<f64> + 'static,
303 {
304 fn into_json_matcher(self) -> Box<dyn for<'a> Matcher<&'a Value>> {
305 Box::new(JsonValueMatcher::<M, f64>::new(self))
306 }
307 }
308
309 impl<M> IntoJsonMatcher<String> for M
310 where
311 M: for<'a> Matcher<&'a str> + 'static,
312 {
313 fn into_json_matcher(self) -> Box<dyn for<'a> Matcher<&'a Value>> {
314 Box::new(JsonValueMatcher::<M, String>::new(self))
315 }
316 }
317
318 impl<M> IntoJsonMatcher<bool> for M
319 where
320 M: Matcher<bool> + 'static,
321 {
322 fn into_json_matcher(self) -> Box<dyn for<'a> Matcher<&'a Value>> {
323 Box::new(JsonValueMatcher::<M, bool>::new(self))
324 }
325 }
326
327 impl<M> Matcher<&Value> for JsonValueMatcher<M, i32>
328 where
329 M: Matcher<i32>,
330 {
331 fn matches(&self, actual: &Value) -> MatcherResult {
332 match actual {
333 Value::Number(n) => match n.as_i64() {
334 Some(i) => match i32::try_from(i) {
335 Ok(i32_val) => self.inner.matches(i32_val),
336 Err(_) => MatcherResult::NoMatch,
337 },
338 None => MatcherResult::NoMatch,
339 },
340 _ => MatcherResult::NoMatch,
341 }
342 }
343 fn describe(&self, r: MatcherResult) -> Description {
344 self.inner.describe(r)
345 }
346 fn explain_match(&self, actual: &Value) -> Description {
347 match actual {
348 Value::Number(n) => match n.as_i64() {
349 Some(i) => match i32::try_from(i) {
350 Ok(i32_val) => self.inner.explain_match(i32_val),
351 Err(_) => Description::new().text(format!("number out of i32 range: {n}")),
352 },
353 None => Description::new().text(format!("number out of i64 range: {n}")),
354 },
355 _ => Description::new().text("which is not a JSON number"),
356 }
357 }
358 }
359
360 impl<M> IntoJsonMatcher<i32> for M
361 where
362 M: Matcher<i32> + 'static,
363 {
364 fn into_json_matcher(self) -> Box<dyn for<'a> Matcher<&'a Value>> {
365 Box::new(JsonValueMatcher::<M, i32>::new(self))
366 }
367 }
368}