googletest_json_serde/matchers/
elements_are_matcher.rs1#[macro_export]
46#[doc(hidden)]
47macro_rules! __json_elements_are {
48 ([$($matcher:expr),* $(,)?]) => {{
50 $crate::matchers::__internal_unstable_do_not_depend_on_these::JsonElementsAre::new(vec![
51 $(
52 $crate::matchers::__internal_unstable_do_not_depend_on_these::IntoJsonMatcher::into_json_matcher($matcher)
53 ),*
54 ])
55 }};
56 ($($matcher:expr),* $(,)?) => {{
58 $crate::__json_elements_are!([$($matcher),*])
59 }};
60}
61
62#[doc(hidden)]
63pub mod internal {
64 use crate::matchers::json_matcher::internal::JsonMatcher;
65 use googletest::description::Description;
66 use googletest::matcher::{Matcher, MatcherBase, MatcherResult};
67 use serde_json::Value;
68
69 #[doc(hidden)]
70 #[derive(MatcherBase)]
71 pub struct JsonElementsAre {
72 elements: Vec<Box<dyn JsonMatcher>>,
73 }
74
75 impl JsonMatcher for JsonElementsAre {}
76
77 impl JsonElementsAre {
78 pub fn new(elements: Vec<Box<dyn JsonMatcher>>) -> Self {
79 Self { elements }
80 }
81 }
82
83 impl Matcher<&Value> for JsonElementsAre {
84 fn matches(&self, actual: &Value) -> MatcherResult {
85 match actual {
86 Value::Array(arr) => {
87 if arr.len() != self.elements.len() {
88 return MatcherResult::NoMatch;
89 }
90 for (item, matcher) in arr.iter().zip(&self.elements) {
91 if matcher.matches(item).is_no_match() {
92 return MatcherResult::NoMatch;
93 }
94 }
95 MatcherResult::Match
96 }
97 _ => MatcherResult::NoMatch,
98 }
99 }
100
101 fn describe(&self, result: MatcherResult) -> Description {
102 let verb = if result.into() { "has" } else { "doesn't have" };
103 let inner = self
104 .elements
105 .iter()
106 .map(|m| m.describe(MatcherResult::Match))
107 .collect::<Description>()
108 .enumerate()
109 .indent();
110
111 format!("{verb} JSON array elements:\n{inner}").into()
112 }
113
114 fn explain_match(&self, actual: &Value) -> Description {
115 match actual {
116 Value::Array(arr) => {
117 let mut mismatches = Vec::new();
118 let actual_len = arr.len();
119 let expected_len = self.elements.len();
120
121 for (index, (item, matcher)) in arr.iter().zip(&self.elements).enumerate() {
122 if matcher.matches(item).is_no_match() {
123 mismatches.push(format!(
124 "element #{index} is {item:?}, {}",
125 matcher.explain_match(item)
126 ));
127 }
128 }
129
130 if mismatches.is_empty() {
131 if actual_len == expected_len {
132 "whose elements all match".into()
133 } else {
134 format!("whose size is {}", actual_len).into()
135 }
136 } else if mismatches.len() == 1 {
137 let description = mismatches.into_iter().collect::<Description>();
138 format!("where {description}").into()
139 } else {
140 let description = mismatches.into_iter().collect::<Description>();
141 format!("where:\n{}", description.bullet_list().indent()).into()
142 }
143 }
144 _ => Description::new().text("where the type is not array".to_string()),
145 }
146 }
147 }
148}