googletest_json_serde/matchers/
json_matcher.rs

1//! JSON predicate matchers for googletest assertions.
2//!
3//! # Examples
4//! ```rust
5//! # use googletest::prelude::*;
6//! # use googletest_json_serde::json;
7//! # use serde_json::json as j;
8//! assert_that!(j!("hi"), json::is_string());
9//! ```
10
11use crate::matchers::__internal_unstable_do_not_depend_on_these;
12use crate::matchers::__internal_unstable_do_not_depend_on_these::JsonPredicateMatcher;
13use googletest::description::Description;
14use serde_json::Value;
15
16/// Builds a JSON matcher from an arbitrary predicate function.
17///
18/// # Examples
19///
20/// ```rust
21/// # use googletest::prelude::*;
22/// # use googletest_json_serde::json;
23/// # use serde_json::json as j;
24/// let positive = json::predicate(|v| v.as_i64().is_some_and(|n| n > 0));
25/// verify_that!(j!(42), &positive);
26/// assert_that!(j!(-1), not(&positive));
27/// ```
28pub fn predicate<P>(
29    predicate: P,
30) -> JsonPredicateMatcher<
31    P,
32    __internal_unstable_do_not_depend_on_these::NoDescription,
33    __internal_unstable_do_not_depend_on_these::NoDescription,
34>
35where
36    P: Fn(&Value) -> bool,
37{
38    JsonPredicateMatcher::new(
39        predicate,
40        __internal_unstable_do_not_depend_on_these::NoDescription,
41        __internal_unstable_do_not_depend_on_these::NoDescription,
42    )
43}
44/// Matches JSON null values.
45///
46/// # Examples
47///
48/// ```rust
49/// # use googletest::prelude::*;
50/// # use googletest_json_serde::json;
51/// # use serde_json::json as j;
52/// assert_that!(j!(null), json::is_null());
53/// assert_that!(j!("value"), not(json::is_null()));
54/// ```
55pub fn is_null() -> JsonPredicateMatcher<impl Fn(&Value) -> bool, &'static str, &'static str> {
56    JsonPredicateMatcher::new(|v| v.is_null(), "JSON null", "which is not JSON null")
57        .with_explain_fn(__internal_unstable_do_not_depend_on_these::describe_json_type)
58}
59/// Matches JSON values that are not null.
60///
61/// # Examples
62///
63/// ```rust
64/// # use googletest::prelude::*;
65/// # use googletest_json_serde::json;
66/// # use serde_json::json as j;
67/// assert_that!(j!("text"), json::is_not_null());
68/// assert_that!(j!(null), not(json::is_not_null()));
69/// ```
70pub fn is_not_null() -> JsonPredicateMatcher<impl Fn(&Value) -> bool, &'static str, &'static str> {
71    JsonPredicateMatcher::new(|v| !v.is_null(), "not JSON null", "which is JSON null")
72        .with_explain_fn(__internal_unstable_do_not_depend_on_these::describe_json_type)
73}
74
75/// Matches JSON values that are not null.
76///
77/// # Examples
78///
79/// ```rust
80/// # use googletest::prelude::*;
81/// # use googletest_json_serde::json;
82/// # use serde_json::json as j;
83/// assert_that!(j!("ok"), json::any_value());
84/// assert_that!(j!(null), not(json::any_value()));
85/// ```
86#[deprecated(since = "0.2.2", note = "Use `is_not_null` instead")]
87pub fn any_value() -> JsonPredicateMatcher<impl Fn(&Value) -> bool, &'static str, &'static str> {
88    JsonPredicateMatcher::new(|v| !v.is_null(), "any JSON value", "is not any JSON value")
89        .with_explain_fn(__internal_unstable_do_not_depend_on_these::describe_json_type)
90}
91
92/// Matches JSON string values.
93///
94/// # Examples
95///
96/// ```rust
97/// # use googletest::prelude::*;
98/// # use googletest_json_serde::json;
99/// # use serde_json::json as j;
100/// assert_that!(j!("hi"), json::is_string());
101/// assert_that!(j!(true), not(json::is_string()));
102/// ```
103pub fn is_string() -> JsonPredicateMatcher<impl Fn(&Value) -> bool, &'static str, &'static str> {
104    JsonPredicateMatcher::new(
105        |v| v.is_string(),
106        "a JSON string",
107        "which is not a JSON string",
108    )
109    .with_explain_fn(__internal_unstable_do_not_depend_on_these::describe_json_type)
110}
111
112/// Matches an empty JSON string (`""`).
113///
114/// # Examples
115///
116/// ```rust
117/// # use googletest::prelude::*;
118/// # use googletest_json_serde::json;
119/// # use serde_json::json as j;
120/// assert_that!(j!(""), json::is_empty_string());
121/// assert_that!(j!("hi"), not(json::is_empty_string()));
122/// ```
123pub fn is_empty_string() -> JsonPredicateMatcher<impl Fn(&Value) -> bool, &'static str, &'static str>
124{
125    JsonPredicateMatcher::new(
126        |v| v.as_str().is_some_and(|s| s.is_empty()),
127        "an empty JSON string",
128        "which is not an empty JSON string",
129    )
130    .with_explain_fn(|v| {
131        if v.is_string() {
132            Description::new().text("which is a non-empty JSON string")
133        } else {
134            __internal_unstable_do_not_depend_on_these::describe_json_type(v)
135        }
136    })
137}
138
139/// Matches a non-empty JSON string.
140///
141/// # Examples
142///
143/// ```rust
144/// # use googletest::prelude::*;
145/// # use googletest_json_serde::json;
146/// # use serde_json::json as j;
147/// assert_that!(j!("hi"), json::is_non_empty_string());
148/// assert_that!(j!(""), not(json::is_non_empty_string()));
149/// ```
150pub fn is_non_empty_string()
151-> JsonPredicateMatcher<impl Fn(&Value) -> bool, &'static str, &'static str> {
152    JsonPredicateMatcher::new(
153        |v| v.as_str().is_some_and(|s| !s.is_empty()),
154        "a non-empty JSON string",
155        "which is not a non-empty JSON string",
156    )
157    .with_explain_fn(|v| {
158        if v.is_string() {
159            Description::new().text("which is an empty JSON string")
160        } else {
161            __internal_unstable_do_not_depend_on_these::describe_json_type(v)
162        }
163    })
164}
165
166/// Matches JSON number values.
167///
168/// # Examples
169///
170/// ```rust
171/// # use googletest::prelude::*;
172/// # use googletest_json_serde::json;
173/// # use serde_json::json as j;
174/// assert_that!(j!(3.14), json::is_number());
175/// assert_that!(j!("three"), not(json::is_number()));
176/// ```
177pub fn is_number() -> JsonPredicateMatcher<impl Fn(&Value) -> bool, &'static str, &'static str> {
178    JsonPredicateMatcher::new(
179        |v| v.is_number(),
180        "a JSON number",
181        "which is not a JSON number",
182    )
183    .with_explain_fn(__internal_unstable_do_not_depend_on_these::describe_json_type)
184}
185
186/// Matches JSON numbers that are integers.
187///
188/// # Examples
189///
190/// ```rust
191/// # use googletest::prelude::*;
192/// # use googletest_json_serde::json;
193/// # use serde_json::json as j;
194/// assert_that!(j!(42), json::is_integer());
195/// assert_that!(j!(3.14), not(json::is_integer()));
196/// ```
197pub fn is_integer() -> JsonPredicateMatcher<impl Fn(&Value) -> bool, &'static str, &'static str> {
198    JsonPredicateMatcher::new(
199        |v| matches!(v, Value::Number(n) if n.is_i64() || n.is_u64()),
200        "an integer JSON number",
201        "which is not an integer JSON number",
202    )
203    .with_explain_fn(|v| {
204        if matches!(v, Value::Number(_)) {
205            Description::new().text("which is a non-integer JSON number")
206        } else {
207            __internal_unstable_do_not_depend_on_these::describe_json_type(v)
208        }
209    })
210}
211
212/// Matches JSON numbers whose fractional part is zero (e.g., `2` or `2.0`).
213///
214/// # Examples
215///
216/// ```rust
217/// # use googletest::prelude::*;
218/// # use googletest_json_serde::json;
219/// # use serde_json::json as j;
220/// assert_that!(j!(2.0), json::is_whole_number());
221/// assert_that!(j!(2.5), not(json::is_whole_number()));
222/// ```
223pub fn is_whole_number() -> JsonPredicateMatcher<impl Fn(&Value) -> bool, &'static str, &'static str>
224{
225    JsonPredicateMatcher::new(
226        |v| match v {
227            Value::Number(n) => {
228                if n.is_i64() || n.is_u64() {
229                    true
230                } else {
231                    n.as_f64()
232                        .is_some_and(|f| f.is_finite() && f.fract() == 0.0)
233                }
234            }
235            _ => false,
236        },
237        "a JSON number with no fractional part",
238        "which is not a JSON number with no fractional part",
239    )
240    .with_explain_fn(|v| {
241        if matches!(v, Value::Number(_)) {
242            Description::new().text("which is a JSON number with a fractional part")
243        } else {
244            __internal_unstable_do_not_depend_on_these::describe_json_type(v)
245        }
246    })
247}
248
249/// Matches JSON numbers that have a non-zero fractional part.
250///
251/// # Examples
252///
253/// ```rust
254/// # use googletest::prelude::*;
255/// # use googletest_json_serde::json;
256/// # use serde_json::json as j;
257/// assert_that!(j!(3.5), json::is_fractional_number());
258/// assert_that!(j!(3), not(json::is_fractional_number()));
259/// assert_that!(j!(3.0), not(json::is_fractional_number()));
260/// ```
261pub fn is_fractional_number()
262-> JsonPredicateMatcher<impl Fn(&Value) -> bool, &'static str, &'static str> {
263    JsonPredicateMatcher::new(
264        |v| match v {
265            Value::Number(n) => {
266                if n.is_i64() || n.is_u64() {
267                    return false;
268                }
269                n.as_f64()
270                    .is_some_and(|f| f.is_finite() && f.fract() != 0.0)
271            }
272            _ => false,
273        },
274        "a JSON number with a fractional part",
275        "which is not a JSON number with a fractional part",
276    )
277    .with_explain_fn(|v| {
278        if matches!(v, Value::Number(_)) {
279            Description::new().text("which is a JSON number without a fractional part")
280        } else {
281            __internal_unstable_do_not_depend_on_these::describe_json_type(v)
282        }
283    })
284}
285
286/// Matches JSON boolean values.
287///
288/// # Examples
289///
290/// ```rust
291/// # use googletest::prelude::*;
292/// # use googletest_json_serde::json;
293/// # use serde_json::json as j;
294/// assert_that!(j!(true), json::is_boolean());
295/// assert_that!(j!(0), not(json::is_boolean()));
296/// ```
297pub fn is_boolean() -> JsonPredicateMatcher<impl Fn(&Value) -> bool, &'static str, &'static str> {
298    JsonPredicateMatcher::new(
299        |v| v.is_boolean(),
300        "a JSON boolean",
301        "which is not a JSON boolean",
302    )
303    .with_explain_fn(__internal_unstable_do_not_depend_on_these::describe_json_type)
304}
305
306/// Matches the JSON boolean `true` value.
307///
308/// # Examples
309///
310/// ```rust
311/// # use googletest::prelude::*;
312/// # use googletest_json_serde::json;
313/// # use serde_json::json as j;
314/// assert_that!(j!(true), json::is_true());
315/// assert_that!(j!(false), not(json::is_true()));
316/// ```
317pub fn is_true() -> JsonPredicateMatcher<impl Fn(&Value) -> bool, &'static str, &'static str> {
318    JsonPredicateMatcher::new(
319        |v| matches!(v, Value::Bool(true)),
320        "JSON true",
321        "which is not JSON true",
322    )
323    .with_explain_fn(|v| match v {
324        Value::Bool(false) => Description::new().text("which is JSON false"),
325        _ => __internal_unstable_do_not_depend_on_these::describe_json_type(v),
326    })
327}
328
329/// Matches the JSON boolean `false` value.
330///
331/// # Examples
332///
333/// ```rust
334/// # use googletest::prelude::*;
335/// # use googletest_json_serde::json;
336/// # use serde_json::json as j;
337/// assert_that!(j!(false), json::is_false());
338/// assert_that!(j!(true), not(json::is_false()));
339/// ```
340pub fn is_false() -> JsonPredicateMatcher<impl Fn(&Value) -> bool, &'static str, &'static str> {
341    JsonPredicateMatcher::new(
342        |v| matches!(v, Value::Bool(false)),
343        "JSON false",
344        "which is not JSON false",
345    )
346    .with_explain_fn(|v| match v {
347        Value::Bool(true) => Description::new().text("which is JSON true"),
348        _ => __internal_unstable_do_not_depend_on_these::describe_json_type(v),
349    })
350}
351
352/// Matches JSON array values.
353///
354/// # Examples
355///
356/// ```rust
357/// # use googletest::prelude::*;
358/// # use googletest_json_serde::json;
359/// # use serde_json::json as j;
360/// assert_that!(j!([1, 2]), json::is_array());
361/// assert_that!(j!({"a":1}), not(json::is_array()));
362/// ```
363pub fn is_array() -> JsonPredicateMatcher<impl Fn(&Value) -> bool, &'static str, &'static str> {
364    JsonPredicateMatcher::new(
365        |v| v.is_array(),
366        "a JSON array",
367        "which is not a JSON array",
368    )
369    .with_explain_fn(__internal_unstable_do_not_depend_on_these::describe_json_type)
370}
371
372/// Matches an empty JSON array (`[]`).
373///
374/// # Examples
375///
376/// ```rust
377/// # use googletest::prelude::*;
378/// # use googletest_json_serde::json;
379/// # use serde_json::json as j;
380/// assert_that!(j!([]), json::is_empty_array());
381/// assert_that!(j!([1]), not(json::is_empty_array()));
382/// ```
383pub fn is_empty_array() -> JsonPredicateMatcher<impl Fn(&Value) -> bool, &'static str, &'static str>
384{
385    JsonPredicateMatcher::new(
386        |v| v.as_array().is_some_and(|a| a.is_empty()),
387        "an empty JSON array",
388        "which is not an empty JSON array",
389    )
390    .with_explain_fn(|v| {
391        if v.is_array() {
392            Description::new().text("which is a non-empty JSON array")
393        } else {
394            __internal_unstable_do_not_depend_on_these::describe_json_type(v)
395        }
396    })
397}
398
399/// Matches a non-empty JSON array.
400///
401/// # Examples
402///
403/// ```rust
404/// # use googletest::prelude::*;
405/// # use googletest_json_serde::json;
406/// # use serde_json::json as j;
407/// assert_that!(j!([1]), json::is_non_empty_array());
408/// assert_that!(j!([]), not(json::is_non_empty_array()));
409/// ```
410pub fn is_non_empty_array()
411-> JsonPredicateMatcher<impl Fn(&Value) -> bool, &'static str, &'static str> {
412    JsonPredicateMatcher::new(
413        |v| v.as_array().is_some_and(|a| !a.is_empty()),
414        "a non-empty JSON array",
415        "which is not a non-empty JSON array",
416    )
417    .with_explain_fn(|v| {
418        if v.is_array() {
419            Description::new().text("which is an empty JSON array")
420        } else {
421            __internal_unstable_do_not_depend_on_these::describe_json_type(v)
422        }
423    })
424}
425
426/// Matches JSON object values.
427///
428/// # Examples
429///
430/// ```rust
431/// # use googletest::prelude::*;
432/// # use googletest_json_serde::json;
433/// # use serde_json::json as j;
434/// assert_that!(j!({"a": 1}), json::is_object());
435/// assert_that!(j!(null), not(json::is_object()));
436/// ```
437pub fn is_object() -> JsonPredicateMatcher<impl Fn(&Value) -> bool, &'static str, &'static str> {
438    JsonPredicateMatcher::new(
439        |v| v.is_object(),
440        "a JSON object",
441        "which is not a JSON object",
442    )
443    .with_explain_fn(__internal_unstable_do_not_depend_on_these::describe_json_type)
444}
445
446/// Matches an empty JSON object (`{}`).
447///
448/// # Examples
449///
450/// ```rust
451/// # use googletest::prelude::*;
452/// # use googletest_json_serde::json;
453/// # use serde_json::json as j;
454/// assert_that!(j!({}), json::is_empty_object());
455/// assert_that!(j!({"a":1}), not(json::is_empty_object()));
456/// ```
457pub fn is_empty_object() -> JsonPredicateMatcher<impl Fn(&Value) -> bool, &'static str, &'static str>
458{
459    JsonPredicateMatcher::new(
460        |v| v.as_object().is_some_and(|o| o.is_empty()),
461        "an empty JSON object",
462        "which is not an empty JSON object",
463    )
464    .with_explain_fn(|v| {
465        if v.is_object() {
466            Description::new().text("which is a non-empty JSON object")
467        } else {
468            __internal_unstable_do_not_depend_on_these::describe_json_type(v)
469        }
470    })
471}
472
473/// Matches a non-empty JSON object.
474///
475/// # Examples
476///
477/// ```rust
478/// # use googletest::prelude::*;
479/// # use googletest_json_serde::json;
480/// # use serde_json::json as j;
481/// assert_that!(j!({"a": 1}), json::is_non_empty_object());
482/// assert_that!(j!({}), not(json::is_non_empty_object()));
483/// ```
484pub fn is_non_empty_object()
485-> JsonPredicateMatcher<impl Fn(&Value) -> bool, &'static str, &'static str> {
486    JsonPredicateMatcher::new(
487        |v| v.as_object().is_some_and(|o| !o.is_empty()),
488        "a non-empty JSON object",
489        "which is not a non-empty JSON object",
490    )
491    .with_explain_fn(|v| {
492        if v.is_object() {
493            Description::new().text("which is an empty JSON object")
494        } else {
495            __internal_unstable_do_not_depend_on_these::describe_json_type(v)
496        }
497    })
498}
499
500// Path-based matchers live in `path_matcher.rs`.
501
502#[doc(hidden)]
503pub mod internal {
504    use googletest::description::Description;
505    use googletest::matcher::MatcherResult::{Match, NoMatch};
506    use googletest::matcher::{Matcher, MatcherBase, MatcherResult};
507    use serde_json::Value;
508
509    /// Trait for types that can provide a description string.
510    pub trait PredicateDescription {
511        fn to_description(self) -> String;
512    }
513
514    impl PredicateDescription for &'static str {
515        fn to_description(self) -> String {
516            self.to_string()
517        }
518    }
519
520    impl PredicateDescription for String {
521        fn to_description(self) -> String {
522            self
523        }
524    }
525
526    impl<F> PredicateDescription for F
527    where
528        F: Fn() -> String,
529    {
530        fn to_description(self) -> String {
531            self()
532        }
533    }
534    /// Sentinel type for missing descriptions.
535    #[derive(Clone, Copy, Debug)]
536    pub struct NoDescription;
537    impl PredicateDescription for NoDescription {
538        fn to_description(self) -> String {
539            String::new()
540        }
541    }
542
543    /// Type alias for the explain function to reduce type complexity.
544    type ExplainFn = Box<dyn Fn(&Value) -> Description>;
545
546    #[derive(MatcherBase)]
547    pub struct JsonPredicateMatcher<P, D1 = NoDescription, D2 = NoDescription>
548    where
549        P: Fn(&Value) -> bool,
550        D1: PredicateDescription,
551        D2: PredicateDescription,
552    {
553        predicate: P,
554        positive_description: D1,
555        negative_description: D2,
556        explain_fn: Option<ExplainFn>,
557    }
558
559    impl<P, D1, D2> JsonPredicateMatcher<P, D1, D2>
560    where
561        P: Fn(&Value) -> bool,
562        D1: PredicateDescription,
563        D2: PredicateDescription,
564    {
565        pub fn new(predicate: P, positive_description: D1, negative_description: D2) -> Self {
566            Self {
567                predicate,
568                positive_description,
569                negative_description,
570                explain_fn: None,
571            }
572        }
573
574        pub fn with_description<D1b, D2b>(
575            self,
576            positive_description: D1b,
577            negative_description: D2b,
578        ) -> JsonPredicateMatcher<P, D1b, D2b>
579        where
580            D1b: PredicateDescription,
581            D2b: PredicateDescription,
582        {
583            JsonPredicateMatcher {
584                predicate: self.predicate,
585                positive_description,
586                negative_description,
587                explain_fn: self.explain_fn,
588            }
589        }
590
591        pub fn with_explain_fn<F>(mut self, f: F) -> Self
592        where
593            F: Fn(&Value) -> Description + 'static,
594        {
595            self.explain_fn = Some(Box::new(f));
596            self
597        }
598    }
599
600    impl<P, D1, D2> Matcher<&Value> for JsonPredicateMatcher<P, D1, D2>
601    where
602        P: Fn(&Value) -> bool,
603        D1: PredicateDescription + Clone,
604        D2: PredicateDescription + Clone,
605    {
606        fn matches(&self, actual: &Value) -> MatcherResult {
607            if (self.predicate)(actual) {
608                Match
609            } else {
610                NoMatch
611            }
612        }
613
614        fn describe(&self, result: MatcherResult) -> Description {
615            let pos = self.positive_description.clone().to_description();
616            let neg = self.negative_description.clone().to_description();
617
618            match result {
619                Match if pos.is_empty() => "matches predicate".into(),
620                NoMatch if neg.is_empty() => "does not match predicate".into(),
621                Match => pos.into(),
622                NoMatch => neg.into(),
623            }
624        }
625
626        fn explain_match(&self, actual: &Value) -> Description {
627            if let Some(ref f) = self.explain_fn {
628                return f(actual);
629            }
630            Description::new().text("which does not match the predicate")
631        }
632    }
633    /// Marker trait for JSON-aware matchers.
634    pub trait JsonMatcher: for<'a> Matcher<&'a Value> {
635        /// Returns true if this matcher allows the field to be missing in an object.
636        fn allows_missing(&self) -> bool {
637            false
638        }
639    }
640
641    /// Trait for converting into a boxed JSON matcher.
642    pub trait IntoJsonMatcher<T> {
643        fn into_json_matcher(self) -> Box<dyn JsonMatcher>;
644    }
645
646    impl<J> IntoJsonMatcher<()> for J
647    where
648        J: JsonMatcher + 'static,
649    {
650        fn into_json_matcher(self) -> Box<dyn JsonMatcher> {
651            Box::new(self)
652        }
653    }
654
655    // A concrete matcher that checks equality with an owned serde_json::Value.
656    // This avoids lifetime issues of using googletest::eq on &Value and gives
657    // us control over descriptions.
658    #[derive(googletest::matcher::MatcherBase)]
659    struct JsonEqMatcher {
660        expected: Value,
661    }
662
663    impl Matcher<&Value> for JsonEqMatcher {
664        fn matches(&self, actual: &Value) -> MatcherResult {
665            if *actual == self.expected {
666                Match
667            } else {
668                NoMatch
669            }
670        }
671
672        fn describe(&self, result: MatcherResult) -> Description {
673            match result {
674                Match => format!("is equal to {:?}", self.expected).into(),
675                NoMatch => format!("isn't equal to {:?}", self.expected).into(),
676            }
677        }
678
679        fn explain_match(&self, _actual: &Value) -> Description {
680            // Framework prints the actual value already. Provide the expected.
681            format!("which isn't equal to {:?}", self.expected).into()
682        }
683    }
684
685    impl JsonMatcher for JsonEqMatcher {}
686
687    // Allow &serde_json::Value to be used seamlessly with JSON macros
688    impl IntoJsonMatcher<Value> for &Value {
689        fn into_json_matcher(self) -> Box<dyn JsonMatcher> {
690            Box::new(JsonEqMatcher {
691                expected: self.clone(),
692            })
693        }
694    }
695
696    impl IntoJsonMatcher<Value> for Value {
697        fn into_json_matcher(self) -> Box<dyn JsonMatcher> {
698            Box::new(JsonEqMatcher { expected: self })
699        }
700    }
701
702    // Literal support marker type
703    pub struct Literal;
704
705    impl IntoJsonMatcher<Literal> for &str {
706        fn into_json_matcher(self) -> Box<dyn JsonMatcher> {
707            Box::new(JsonEqMatcher {
708                expected: Value::from(self),
709            })
710        }
711    }
712
713    impl IntoJsonMatcher<Literal> for String {
714        fn into_json_matcher(self) -> Box<dyn JsonMatcher> {
715            Box::new(JsonEqMatcher {
716                expected: Value::from(self),
717            })
718        }
719    }
720
721    impl IntoJsonMatcher<Literal> for bool {
722        fn into_json_matcher(self) -> Box<dyn JsonMatcher> {
723            Box::new(JsonEqMatcher {
724                expected: Value::from(self),
725            })
726        }
727    }
728
729    impl IntoJsonMatcher<Literal> for i64 {
730        fn into_json_matcher(self) -> Box<dyn JsonMatcher> {
731            Box::new(JsonEqMatcher {
732                expected: Value::from(self),
733            })
734        }
735    }
736    impl IntoJsonMatcher<Literal> for i32 {
737        fn into_json_matcher(self) -> Box<dyn JsonMatcher> {
738            Box::new(JsonEqMatcher {
739                expected: Value::from(self),
740            })
741        }
742    }
743
744    impl IntoJsonMatcher<Literal> for u64 {
745        fn into_json_matcher(self) -> Box<dyn JsonMatcher> {
746            Box::new(JsonEqMatcher {
747                expected: Value::from(self),
748            })
749        }
750    }
751
752    impl IntoJsonMatcher<Literal> for f64 {
753        fn into_json_matcher(self) -> Box<dyn JsonMatcher> {
754            Box::new(JsonEqMatcher {
755                expected: Value::from(self),
756            })
757        }
758    }
759
760    impl<P, D1, D2> JsonMatcher for JsonPredicateMatcher<P, D1, D2>
761    where
762        P: Fn(&Value) -> bool + 'static,
763        D1: PredicateDescription + Clone + 'static,
764        D2: PredicateDescription + Clone + 'static,
765    {
766    }
767
768    pub fn describe_json_type(v: &Value) -> Description {
769        match v {
770            Value::Null => "which is a JSON null",
771            Value::String(_) => "which is a JSON string",
772            Value::Number(_) => "which is a JSON number",
773            Value::Bool(_) => "which is a JSON boolean",
774            Value::Array(_) => "which is a JSON array",
775            Value::Object(_) => "which is a JSON object",
776        }
777        .into()
778    }
779}