1use serde_json::Value;
2
3use crate::{JsonMatcher, JsonMatcherError, JsonPath, JsonPathElement};
4
5pub struct ArrayMatcherRefs<'a> {
6 elements: Vec<&'a dyn JsonMatcher>,
7}
8
9impl<'a> ArrayMatcherRefs<'a> {
10 pub fn new(elements: Vec<&'a dyn JsonMatcher>) -> Self {
11 Self { elements }
12 }
13}
14
15impl JsonMatcher for ArrayMatcherRefs<'_> {
16 fn json_matches(&self, value: &Value) -> Vec<JsonMatcherError> {
17 let mut errors: Vec<JsonMatcherError> = vec![];
18 match value {
19 Value::Array(array) => {
20 let actual_length = array.len();
21 let expected_length = self.elements.len();
22 let expected_but_missing_indexes = actual_length..expected_length;
23 if !expected_but_missing_indexes.is_empty() {
24 let min = expected_but_missing_indexes
25 .clone()
26 .min()
27 .expect("Expected array length is greater than 0");
28 let max = expected_but_missing_indexes
29 .max()
30 .expect("Expected array length is greater than 0");
31 let error = if min == max {
32 format!("Array is missing index {}", min)
33 } else {
34 format!("Array is missing indexes: {}..{}", min, max)
35 };
36 errors.push(JsonMatcherError::at_root(error));
37 }
38 let unexpected_indexes = expected_length..actual_length;
39 if !unexpected_indexes.is_empty() {
40 let min = unexpected_indexes
41 .clone()
42 .min()
43 .expect("Unexpected array length is greater than 0");
44 let max = unexpected_indexes
45 .max()
46 .expect("Unexpected array length is greater than 0");
47 let error = if min == max {
48 format!("Array has unexpected index {}", min)
49 } else {
50 format!("Array has unexpected indexes: {}..{}", min, max)
51 };
52 errors.push(JsonMatcherError::at_root(error));
53 }
54 let expected_and_present_indexes = 0..([actual_length, expected_length]
55 .into_iter()
56 .min()
57 .expect("Array is inlined to have length greater than 0"));
58 for index in expected_and_present_indexes {
59 let matcher = &self.elements[index];
60 let value = array.get(index).expect("Index in array checked.");
61 let sub_errors = matcher.json_matches(value);
62 for sub_error in sub_errors {
63 let this_path = JsonPath::from(vec![
64 JsonPathElement::Root,
65 JsonPathElement::Index(index),
66 ]);
67 let JsonMatcherError { path, message } = sub_error;
68 let new_path = this_path.extend(path);
69 errors.push(JsonMatcherError {
70 path: new_path,
71 message,
72 });
73 }
74 }
75 }
76 _ => errors.push(JsonMatcherError::at_root("Value is not an array")),
77 }
78 errors
79 }
80}
81
82pub struct ArrayMatcher {
83 elements: Vec<Box<dyn JsonMatcher>>,
84}
85
86impl Default for ArrayMatcher {
87 fn default() -> Self {
88 Self::new()
89 }
90}
91
92impl ArrayMatcher {
93 pub fn new() -> Self {
94 Self { elements: vec![] }
95 }
96
97 pub fn of(elements: Vec<Box<dyn JsonMatcher>>) -> Self {
98 Self { elements }
99 }
100
101 pub fn element(mut self, value: impl JsonMatcher + 'static) -> Self {
102 self.elements.push(Box::new(value));
103 self
104 }
105}
106
107impl JsonMatcher for ArrayMatcher {
108 fn json_matches(&self, value: &Value) -> Vec<JsonMatcherError> {
109 ArrayMatcherRefs::new(
110 self.elements
111 .iter()
112 .map(|x| x.as_ref() as &dyn JsonMatcher)
113 .collect(),
114 )
115 .json_matches(value)
116 }
117}
118
119impl JsonMatcher for Vec<Box<dyn JsonMatcher>> {
120 fn json_matches(&self, value: &Value) -> Vec<JsonMatcherError> {
121 ArrayMatcherRefs::new(
122 self.iter()
123 .map(|x| x.as_ref() as &dyn JsonMatcher)
124 .collect(),
125 )
126 .json_matches(value)
127 }
128}
129
130impl JsonMatcher for [Box<dyn JsonMatcher>] {
131 fn json_matches(&self, value: &Value) -> Vec<JsonMatcherError> {
132 ArrayMatcherRefs::new(
133 self.iter()
134 .map(|x| x.as_ref() as &dyn JsonMatcher)
135 .collect(),
136 )
137 .json_matches(value)
138 }
139}
140
141impl JsonMatcher for Vec<&dyn JsonMatcher> {
142 fn json_matches(&self, value: &Value) -> Vec<JsonMatcherError> {
143 ArrayMatcherRefs::new(self.to_vec()).json_matches(value)
144 }
145}
146
147impl JsonMatcher for [&dyn JsonMatcher] {
148 fn json_matches(&self, value: &Value) -> Vec<JsonMatcherError> {
149 ArrayMatcherRefs::new(self.to_vec()).json_matches(value)
150 }
151}
152
153impl<T: JsonMatcher> JsonMatcher for Vec<T> {
154 fn json_matches(&self, value: &Value) -> Vec<JsonMatcherError> {
155 ArrayMatcherRefs::new(self.iter().map(|x| x as &dyn JsonMatcher).collect())
156 .json_matches(value)
157 }
158}
159
160impl<T: JsonMatcher> JsonMatcher for [T] {
161 fn json_matches(&self, value: &Value) -> Vec<JsonMatcherError> {
162 ArrayMatcherRefs::new(self.iter().map(|x| x as &dyn JsonMatcher).collect())
163 .json_matches(value)
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use serde_json::json;
170
171 use crate::test::catch_string_panic;
172 use crate::{assert_jm, StringMatcher};
173
174 use super::*;
175
176 #[test]
177 fn test_array_matcher() {
178 let get_matcher = || {
179 ArrayMatcher::new()
180 .element(
181 ArrayMatcher::new()
182 .element(StringMatcher::new("one"))
183 .element(StringMatcher::new("two")),
184 )
185 .element(StringMatcher::new("three"))
186 };
187 assert_jm!(json!([["one", "two"], "three"]), get_matcher());
189 assert_eq!(
191 catch_string_panic(|| assert_jm!(json!([["one", "two"], "four"]), get_matcher())),
192 r#"
193Json matcher failed:
194 - $.1: Expected string "three" but got "four"
195
196Actual:
197[
198 [
199 "one",
200 "two"
201 ],
202 "four"
203]"#
204 );
205 assert_eq!(
207 catch_string_panic(|| assert_jm!(json!([["one", "four"], "three"]), get_matcher())),
208 r#"
209Json matcher failed:
210 - $.0.1: Expected string "two" but got "four"
211
212Actual:
213[
214 [
215 "one",
216 "four"
217 ],
218 "three"
219]"#
220 );
221 assert_eq!(
223 catch_string_panic(|| assert_jm!(
224 json!([["one", "two"], "three", "four"]),
225 get_matcher()
226 )),
227 r#"
228Json matcher failed:
229 - $: Array has unexpected index 2
230
231Actual:
232[
233 [
234 "one",
235 "two"
236 ],
237 "three",
238 "four"
239]"#
240 );
241 assert_eq!(
243 catch_string_panic(|| assert_jm!(
244 json!([["one", "two", "four"], "three"]),
245 get_matcher()
246 )),
247 r#"
248Json matcher failed:
249 - $.0: Array has unexpected index 2
250
251Actual:
252[
253 [
254 "one",
255 "two",
256 "four"
257 ],
258 "three"
259]"#
260 );
261 assert_eq!(
263 catch_string_panic(|| assert_jm!(json!([[2], "three", "four", "five"]), get_matcher())),
264 r#"
265Json matcher failed:
266 - $: Array has unexpected indexes: 2..3
267 - $.0: Array is missing index 1
268 - $.0.0: Value is not a string
269
270Actual:
271[
272 [
273 2
274 ],
275 "three",
276 "four",
277 "five"
278]"#
279 );
280 }
281
282 #[test]
283 fn test_raw_implementations() {
284 let matcher: Vec<Box<dyn JsonMatcher>> = vec![Box::new(1), Box::new(2)];
285 assert_eq!(matcher.json_matches(&json!([1, 2])), vec![]);
286 assert_eq!(
287 matcher
288 .json_matches(&json!([1, 2, 3]))
289 .into_iter()
290 .map(|x| x.to_string())
291 .collect::<String>(),
292 "$: Array has unexpected index 2"
293 );
294 let matcher: [Box<dyn JsonMatcher>; 2] = [Box::new(1), Box::new(2)];
295 assert_eq!(matcher.json_matches(&json!([1, 2])), vec![]);
296 assert_eq!(
297 matcher
298 .json_matches(&json!([1, 2, 3]))
299 .into_iter()
300 .map(|x| x.to_string())
301 .collect::<String>(),
302 "$: Array has unexpected index 2"
303 );
304 let matcher: Vec<&dyn JsonMatcher> = vec![&1, &2];
305 assert_eq!(matcher.json_matches(&json!([1, 2])), vec![]);
306 assert_eq!(
307 matcher
308 .json_matches(&json!([1, 2, 3]))
309 .into_iter()
310 .map(|x| x.to_string())
311 .collect::<String>(),
312 "$: Array has unexpected index 2"
313 );
314 let matcher: [&dyn JsonMatcher; 2] = [&1, &2];
315 assert_eq!(matcher.json_matches(&json!([1, 2])), vec![]);
316 assert_eq!(
317 matcher
318 .json_matches(&json!([1, 2, 3]))
319 .into_iter()
320 .map(|x| x.to_string())
321 .collect::<String>(),
322 "$: Array has unexpected index 2"
323 );
324 let matcher: Vec<i8> = vec![1, 2];
325 assert_eq!(matcher.json_matches(&json!([1, 2])), vec![]);
326 assert_eq!(
327 matcher
328 .json_matches(&json!([1, 2, 3]))
329 .into_iter()
330 .map(|x| x.to_string())
331 .collect::<String>(),
332 "$: Array has unexpected index 2"
333 );
334 let matcher: [i8; 2] = [1, 2];
335 assert_eq!(matcher.json_matches(&json!([1, 2])), vec![]);
336 assert_eq!(
337 matcher
338 .json_matches(&json!([1, 2, 3]))
339 .into_iter()
340 .map(|x| x.to_string())
341 .collect::<String>(),
342 "$: Array has unexpected index 2"
343 );
344 }
345}