1use serde_json::Value;
5
6pub fn contains(input: &Value, compare: &Value) -> bool {
7 match (input, compare) {
8 (Value::Object(map_input), Value::Object(map_compare)) => {
9 for (key, val_compare) in map_compare {
10 match map_input.get(key) {
11 Some(val_input) => {
12 if !contains(val_input, val_compare) {
13 return false;
14 }
15 }
16 None => return false,
17 }
18 }
19 true
20 }
21 (Value::Array(arr_input), Value::Array(arr_compare)) => {
22 for val_compare in arr_compare {
23 let mut found = false;
24 for val_input in arr_input {
25 if contains(val_input, val_compare) {
26 found = true;
27 break;
28 }
29 }
30 if !found {
31 return false;
32 }
33 }
34 true
35 }
36 _ => input == compare,
37 }
38}
39
40#[cfg(test)]
41mod tests {
42 use super::*;
43 use rstest::rstest;
44 use serde_json::json;
45
46 #[rstest]
47 #[case(
48 json!({"a": 1, "b": 2}),
49 json!({"a": 1}),
50 true
51 )]
52 #[case(
53 json!({"a": 1, "b": {"c": 3, "d": 4}}),
54 json!({"b": {"c": 3}}),
55 true
56 )]
57 #[case(
58 json!({"a": [1, 2, 3], "b": 4}),
59 json!({"a": [2]}),
60 true
61 )]
62 #[case(
63 json!({"a": [1, 2, 3], "b": 4}),
64 json!({"a": [4]}),
65 false
66 )]
67 #[case(
68 json!(["apple", "banana", "cherry"]),
69 json!(["banana"]),
70 true
71 )]
72 #[case(
73 json!(["apple", "banana", "cherry"]),
74 json!(["banana", "date"]),
75 false
76 )]
77 #[case(
78 json!(null),
79 json!(null),
80 true
81 )]
82 #[case(
83 json!(null),
84 json!(1),
85 false
86 )]
87 #[case(
88 json!(1),
89 json!(1),
90 true
91 )]
92 #[case(
93 json!(1),
94 json!(2),
95 false
96 )]
97 #[case(
98 json!({"a": {"b": {"c": 1}}}),
99 json!({"a": {"b": {"c": 1}}}),
100 true
101 )]
102 #[case(
103 json!({"a": {"b": {"c": 1, "d": 2}}}),
104 json!({"a": {"b": {"c": 1}}}),
105 true
106 )]
107 #[case(
108 json!({"a": {"b": {"c": 1}}}),
109 json!({"a": {"b": {"c": 2}}}),
110 false
111 )]
112 #[case(
113 json!({"a": [ {"b": 1}, {"b": 2}, {"b": 3} ]}),
114 json!({"a": [ {"b": 2} ]}),
115 true
116 )]
117 #[case(
118 json!({"a": [ {"b": 1}, {"b": 2}, {"b": 3} ]}),
119 json!({"a": [ {"b": 4} ]}),
120 false
121 )]
122 #[case(
123 json!({"a": {"b": [1, 2, 3]}}),
124 json!({"a": {"b": [2, 3]}}),
125 true
126 )]
127 #[case(
128 json!({"a": {"b": [1, 2, 3]}}),
129 json!({"a": {"b": [4]}}),
130 false
131 )]
132 #[case(
133 json!({"a": {"b": 1}}),
134 json!({"a": {"b": 2}}),
135 false
136 )]
137 #[case(
138 json!({"a": [ { "c": "foo", "b": false} ]}),
139 json!({"a": [ { "c": "foo", "b": true} ]}),
140 false
141 )]
142 #[case(
143 json!({"a": [ { "c": "foo", "b": true} ]}),
144 json!({"a": [ { "c": "foo", "b": true} ]}),
145 true
146 )]
147 #[case(
148 json!({
149 "spec": "some_spec",
150 "status": {
151 "conditions": [
152 {
153 "lastProbeTime": null,
154 "lastTransitionTime": "2024-11-02T15:49:19Z",
155 "status": "True",
156 "type": "Ready"
157 },
158 {
159 "lastProbeTime": null,
160 "lastTransitionTime": "2024-11-02T15:49:19Z",
161 "status": "True",
162 "type": "ContainersReady"
163 },
164 {
165 "lastProbeTime": null,
166 "lastTransitionTime": "2024-11-02T15:49:14Z",
167 "status": "True",
168 "type": "PodScheduled"
169 }
170 ],
171 "containerStatuses": [
172 {
173 "containerID": "docker://1b9a81f558cc5980a152a554ba6b03866062e91958c0af72c8290b62633dd2f7",
174 "image": "eclipse-mosquitto:2",
175 "state": {
176 "running": {
177 "startedAt": "2024-11-02T15:49:19Z"
178 }
179 },
180 "volumeMounts": [
181 {
182 "mountPath": "/mosquitto/config",
183 "name": "configuration"
184 },
185 {
186 "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
187 "name": "kube-api-access-dtsbh",
188 "readOnly": true,
189 "recursiveReadOnly": "Disabled"
190 }
191 ]
192 }
193 ]
194 }
195 }),
196 json!({
197 "status": {
198 "conditions": [
199 {
200 "type": "Ready",
201 "status": "True"
202 }
203 ]
204 }
205 }),
206 true
207 )]
208 #[case(
209 json!({
210 "spec": "some_spec",
211 "status": {
212 "conditions": [
213 {
214 "lastProbeTime": null,
215 "lastTransitionTime": "2024-11-02T15:49:19Z",
216 "status": "False",
217 "type": "Ready"
218 },
219 {
220 "lastProbeTime": null,
221 "lastTransitionTime": "2024-11-02T15:49:19Z",
222 "status": "True",
223 "type": "ContainersReady"
224 },
225 {
226 "lastProbeTime": null,
227 "lastTransitionTime": "2024-11-02T15:49:14Z",
228 "status": "True",
229 "type": "PodScheduled"
230 }
231 ],
232 "containerStatuses": [
233 {
234 "containerID": "docker://1b9a81f558cc5980a152a554ba6b03866062e91958c0af72c8290b62633dd2f7",
235 "image": "eclipse-mosquitto:2",
236 "state": {
237 "running": {
238 "startedAt": "2024-11-02T15:49:19Z"
239 }
240 },
241 "volumeMounts": [
242 {
243 "mountPath": "/mosquitto/config",
244 "name": "configuration"
245 },
246 {
247 "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
248 "name": "kube-api-access-dtsbh",
249 "readOnly": true,
250 "recursiveReadOnly": "Disabled"
251 }
252 ]
253 }
254 ]
255 }
256 }),
257 json!({
258 "status": {
259 "conditions": [
260 {
261 "type": "Ready",
262 "status": "True"
263 }
264 ]
265 }
266 }),
267 false
268 )]
269 fn test_contains(#[case] input: Value, #[case] compare: Value, #[case] expected: bool) {
270 let result = contains(&input, &compare);
271 assert_eq!(result, expected);
272 }
273}