jmap_tools/pointer/
eval.rs

1/*
2 * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
3 *
4 * SPDX-License-Identifier: Apache-2.0 OR MIT
5 */
6
7use super::{JsonPointerHandler, JsonPointerItem};
8use crate::json::key::Key;
9use crate::pointer::JsonPointerIter;
10use crate::{Element, Property, Value};
11use std::borrow::Cow;
12use std::collections::HashMap;
13use std::hash::BuildHasher;
14
15impl<'x, P: Property, E: Element> JsonPointerHandler<'x, P, E> for Value<'x, P, E> {
16    fn eval_jptr<'y>(
17        &'y self,
18        mut pointer: JsonPointerIter<'_, P>,
19        results: &mut Vec<Cow<'y, Value<'x, P, E>>>,
20    ) {
21        match pointer.next() {
22            Some(JsonPointerItem::Key(key)) => {
23                if let Value::Object(map) = self
24                    && let Some(v) = map.get(key)
25                {
26                    v.eval_jptr(pointer, results);
27                }
28            }
29            Some(JsonPointerItem::Number(n)) => match self {
30                Value::Array(values) => {
31                    if let Some(v) = values.get(*n as usize) {
32                        v.eval_jptr(pointer, results);
33                    }
34                }
35                Value::Object(map) => {
36                    let n = Key::Owned(n.to_string());
37                    if let Some(v) = map.get(&n) {
38                        v.eval_jptr(pointer, results);
39                    }
40                }
41                _ => {}
42            },
43            Some(JsonPointerItem::Wildcard) => match self {
44                Value::Array(values) => {
45                    for v in values {
46                        v.eval_jptr(pointer.clone(), results);
47                    }
48                }
49                Value::Object(map) => {
50                    for v in map.values() {
51                        v.eval_jptr(pointer.clone(), results);
52                    }
53                }
54                _ => {}
55            },
56            Some(JsonPointerItem::Root) | None => {
57                results.push(Cow::Borrowed(self));
58            }
59        }
60    }
61
62    fn patch_jptr<'y: 'x>(
63        &mut self,
64        mut pointer: JsonPointerIter<'_, P>,
65        value: Value<'y, P, E>,
66    ) -> bool {
67        match pointer.next() {
68            Some(JsonPointerItem::Key(key)) => {
69                if let Value::Object(map) = self {
70                    if let Some(pos) = map.0.iter().position(|(k, _)| k == key) {
71                        return if pointer.peek().is_some() {
72                            map.0[pos].1.patch_jptr(pointer, value)
73                        } else {
74                            map.0[pos].1 = value;
75                            true
76                        };
77                    } else if pointer.next().is_none() {
78                        map.insert_unchecked(key.clone(), value);
79                        return true;
80                    }
81                }
82            }
83            Some(JsonPointerItem::Number(n)) => match self {
84                Value::Array(values) => {
85                    if let Some(item) = values.get_mut(*n as usize) {
86                        return if pointer.peek().is_some() {
87                            item.patch_jptr(pointer, value)
88                        } else {
89                            *item = value;
90                            true
91                        };
92                    }
93                }
94                Value::Object(map) => {
95                    let n = Key::Owned(n.to_string());
96                    if let Some(item) = map.get_mut(&n) {
97                        return if pointer.peek().is_some() {
98                            item.patch_jptr(pointer, value)
99                        } else {
100                            *item = value;
101                            true
102                        };
103                    }
104                }
105                _ => {}
106            },
107            Some(JsonPointerItem::Wildcard) | Some(JsonPointerItem::Root) | None => (),
108        }
109
110        false
111    }
112
113    fn to_value<'y>(&'y self) -> Cow<'y, Value<'x, P, E>> {
114        Cow::Borrowed(self)
115    }
116}
117
118impl<'x, P: Property, E: Element, T> JsonPointerHandler<'x, P, E> for Vec<T>
119where
120    T: JsonPointerHandler<'x, P, E> + for<'y> TryFrom<Value<'y, P, E>> + 'static,
121{
122    fn eval_jptr<'y>(
123        &'y self,
124        mut pointer: JsonPointerIter<'_, P>,
125        results: &mut Vec<Cow<'y, Value<'x, P, E>>>,
126    ) {
127        match pointer.next() {
128            Some(JsonPointerItem::Number(n)) => {
129                if let Some(v) = self.get(*n as usize) {
130                    v.eval_jptr(pointer, results);
131                }
132            }
133            Some(JsonPointerItem::Wildcard) => {
134                for v in self {
135                    v.eval_jptr(pointer.clone(), results);
136                }
137            }
138            Some(JsonPointerItem::Root) | None => {
139                results.push(self.to_value());
140            }
141            _ => {}
142        }
143    }
144
145    fn patch_jptr<'y: 'x>(
146        &mut self,
147        mut pointer: JsonPointerIter<'_, P>,
148        value: Value<'y, P, E>,
149    ) -> bool {
150        if let Some(JsonPointerItem::Number(n)) = pointer.next()
151            && let Some(item) = self.get_mut(*n as usize)
152        {
153            if pointer.peek().is_some() {
154                return item.patch_jptr(pointer, value);
155            } else if let Ok(value) = T::try_from(value) {
156                *item = value;
157                return true;
158            }
159        }
160        false
161    }
162
163    fn to_value<'y>(&'y self) -> Cow<'y, Value<'x, P, E>> {
164        Cow::Owned(Value::Array(
165            self.iter().map(|v| v.to_value().into_owned()).collect(),
166        ))
167    }
168}
169
170impl<'x, P: Property, E: Element, T> TryFrom<Value<'x, P, E>> for Vec<T>
171where
172    T: JsonPointerHandler<'x, P, E> + for<'y> TryFrom<Value<'y, P, E>> + 'static,
173{
174    type Error = ();
175
176    fn try_from(value: Value<'x, P, E>) -> Result<Self, Self::Error> {
177        if let Value::Array(arr) = value {
178            arr.into_iter()
179                .map(T::try_from)
180                .collect::<Result<Vec<_>, _>>()
181                .map_err(|_| ())
182        } else {
183            Err(())
184        }
185    }
186}
187
188impl<'x, P: Property, E: Element, T, S: BuildHasher + Default> JsonPointerHandler<'x, P, E>
189    for HashMap<String, T, S>
190where
191    T: JsonPointerHandler<'x, P, E> + for<'y> TryFrom<Value<'y, P, E>> + 'static,
192{
193    fn eval_jptr<'y>(
194        &'y self,
195        mut pointer: JsonPointerIter<'_, P>,
196        results: &mut Vec<Cow<'y, Value<'x, P, E>>>,
197    ) {
198        match pointer.next() {
199            Some(JsonPointerItem::Key(key)) => {
200                if let Some(v) = self.get(key.to_string().as_ref()) {
201                    v.eval_jptr(pointer, results);
202                }
203            }
204            Some(JsonPointerItem::Number(n)) => {
205                let n = n.to_string();
206                if let Some(v) = self.get(&n) {
207                    v.eval_jptr(pointer, results);
208                }
209            }
210            Some(JsonPointerItem::Wildcard) => {
211                for v in self.values() {
212                    v.eval_jptr(pointer.clone(), results);
213                }
214            }
215            Some(JsonPointerItem::Root) | None => {
216                results.push(self.to_value());
217            }
218        }
219    }
220
221    fn patch_jptr<'y: 'x>(
222        &mut self,
223        mut pointer: JsonPointerIter<'_, P>,
224        value: Value<'y, P, E>,
225    ) -> bool {
226        match pointer.next() {
227            Some(JsonPointerItem::Key(key)) => {
228                let key = key.to_string();
229                if let Some(item) = self.get_mut(key.as_ref()) {
230                    if pointer.peek().is_some() {
231                        return item.patch_jptr(pointer, value);
232                    } else if let Ok(value) = T::try_from(value) {
233                        *item = value;
234                        return true;
235                    }
236                } else if pointer.next().is_none()
237                    && let Ok(v) = T::try_from(value)
238                {
239                    self.insert(key.into_owned(), v);
240                    return true;
241                }
242            }
243            Some(JsonPointerItem::Number(n)) => {
244                if let Some(v) = self.get_mut(&n.to_string()) {
245                    return v.patch_jptr(pointer, value);
246                }
247            }
248            Some(JsonPointerItem::Wildcard) | Some(JsonPointerItem::Root) | None => (),
249        }
250
251        false
252    }
253
254    fn to_value<'y>(&'y self) -> Cow<'y, Value<'x, P, E>> {
255        Cow::Owned(Value::Object(
256            self.iter()
257                .map(|(k, v)| (Key::Owned(k.to_string()), v.to_value().into_owned()))
258                .collect(),
259        ))
260    }
261}
262
263impl<'x, P: Property, E: Element, T> TryFrom<Value<'x, P, E>> for HashMap<String, T>
264where
265    T: JsonPointerHandler<'x, P, E> + for<'y> TryFrom<Value<'y, P, E>> + 'static,
266{
267    type Error = ();
268
269    fn try_from(value: Value<'x, P, E>) -> Result<Self, Self::Error> {
270        if let Value::Object(map) = value {
271            map.into_vec()
272                .into_iter()
273                .map(|(k, v)| T::try_from(v).map(|v| (k.to_string().into_owned(), v)))
274                .collect::<Result<HashMap<_, _>, _>>()
275                .map_err(|_| ())
276        } else {
277            Err(())
278        }
279    }
280}
281
282#[cfg(test)]
283mod tests {
284    use crate::{
285        Element, JsonPointer, JsonPointerHandler, JsonPointerItem, Key, Null, ObjectAsVec,
286        Property, Value, pointer::JsonPointerIter,
287    };
288    use serde::{Deserialize, Serialize, Serializer};
289    use std::{borrow::Cow, collections::HashMap};
290
291    #[derive(Debug, Clone, Serialize, Deserialize)]
292    struct SubObject {
293        text: String,
294        number: u64,
295        boolean: bool,
296    }
297
298    #[derive(Debug, Clone, Serialize, Deserialize)]
299    struct Object {
300        #[serde(serialize_with = "serialize_ordered_map")]
301        map: HashMap<String, SubObject>,
302        array: Vec<SubObject>,
303        value: SubObject,
304    }
305
306    #[test]
307    fn json_pointer() {
308        const TEST: &str = r#"
309        {
310            "map": {
311                "key1": {"text": "value1", "number": 1, "boolean": true},
312                "key2": {"text": "value2", "number": 2, "boolean": false}
313            },
314            "array": [
315                {"text": "item1", "number": 10, "boolean": true},
316                {"text": "item2", "number": 20, "boolean": false}
317            ],
318            "value": {"text": "single", "number": 100, "boolean": true}
319        }
320        "#;
321
322        let obj = serde_json::from_str::<Object>(TEST).unwrap();
323        let value = serde_json::from_str::<Value<'static, Null, Null>>(TEST).unwrap();
324
325        test_json_pointer(&obj, "object");
326        test_json_pointer(&value, "value");
327
328        test_json_patch(obj, "object");
329        test_json_patch(value, "value");
330    }
331
332    fn test_json_pointer<T, P, E>(obj: &T, test: &str)
333    where
334        T: JsonPointerHandler<'static, P, E>,
335        P: Property,
336        E: Element,
337    {
338        for (pointer, expected) in [
339            ("value/text", r#"["single"]"#),
340            ("value/number", r#"[100]"#),
341            ("value/boolean", r#"[true]"#),
342            (
343                "value",
344                r#"[{"text":"single","number":100,"boolean":true}]"#,
345            ),
346            ("map/key1/text", r#"["value1"]"#),
347            ("map/key1/number", r#"[1]"#),
348            ("map/key1/boolean", r#"[true]"#),
349            ("map/key2/text", r#"["value2"]"#),
350            ("map/key2/number", r#"[2]"#),
351            ("map/key2/boolean", r#"[false]"#),
352            ("array/0/text", r#"["item1"]"#),
353            ("array/0/number", r#"[10]"#),
354            ("array/0/boolean", r#"[true]"#),
355            ("array/1/text", r#"["item2"]"#),
356            ("array/1/number", r#"[20]"#),
357            ("array/1/boolean", r#"[false]"#),
358            ("map/*/text", r#"["value1","value2"]"#),
359            ("map/*/number", r#"[1,2]"#),
360            ("map/*/boolean", r#"[false,true]"#),
361            ("map/*/*", r#"["value1","value2",1,2,false,true]"#),
362            ("array/*/text", r#"["item1","item2"]"#),
363            ("array/*/number", r#"[10,20]"#),
364            ("array/*/boolean", r#"[false,true]"#),
365            ("array/*/*", r#"["item1","item2",10,20,false,true]"#),
366            ("/*/text", r#"["single"]"#),
367            ("/*/*/text", r#"["item1","item2","value1","value2"]"#),
368            ("/*/*/number", r#"[1,10,2,20]"#),
369            ("/*/*/boolean", r#"[false,false,true,true]"#),
370        ] {
371            let ptr = JsonPointer::parse(pointer);
372            let mut results = Vec::new();
373            obj.eval_jptr(ptr.iter(), &mut results);
374            results.sort_unstable_by_key(|a| a.to_string());
375            let results = serde_json::to_string(&results).unwrap();
376            if results != expected {
377                panic!(
378                    "Pointer: {}\nTest: {}\nExpected: {}\nResults: {}",
379                    pointer, test, expected, results
380                );
381            }
382        }
383    }
384
385    fn test_json_patch<T>(obj: T, test: &str)
386    where
387        T: JsonPointerHandler<'static, Null, Null> + Clone + Serialize,
388    {
389        for (pointer, patch, expected) in [
390            (
391                "value/text",
392                r#""hello""#,
393                r#""value":{"text":"hello","number":100,"boolean":true}"#,
394            ),
395            (
396                "value/number",
397                "123",
398                r#""value":{"text":"single","number":123,"boolean":true}"#,
399            ),
400            (
401                "value/boolean",
402                "false",
403                r#""value":{"text":"single","number":100,"boolean":false}"#,
404            ),
405            (
406                "value",
407                r#"{"text":"blah","number":999,"boolean":true}"#,
408                r#""value":{"text":"blah","number":999,"boolean":true}"#,
409            ),
410            (
411                "map/key1/text",
412                r#""hola""#,
413                r#"{"key1":{"text":"hola","number":1,"boolean":true},"key2":{"text":"value2","number":2,"boolean":false}}"#,
414            ),
415            (
416                "map/key1",
417                r#"{"text":"adios","number":123,"boolean":false}"#,
418                r#"{"key1":{"text":"adios","number":123,"boolean":false},"key2":{"text":"value2","number":2,"boolean":false}}"#,
419            ),
420            (
421                "array/1/text",
422                r#""nihao""#,
423                r#":[{"text":"item1","number":10,"boolean":true},{"text":"nihao","number":20,"boolean":false}]"#,
424            ),
425            (
426                "array/0",
427                r#"{"text":"bonjour","number":42,"boolean":true}"#,
428                r#"[{"text":"bonjour","number":42,"boolean":true},{"text":"item2","number":20,"boolean":false}]"#,
429            ),
430        ] {
431            let mut obj = obj.clone();
432            obj.patch_jptr(
433                JsonPointer::parse(pointer).iter(),
434                serde_json::from_str::<Value<'_, Null, Null>>(patch).unwrap(),
435            );
436
437            let results = serde_json::to_string(&obj).unwrap();
438            if !results.contains(expected) {
439                panic!(
440                    "Pointer: {}\nTest: {}\nExpected: {}\nResults: {}",
441                    pointer, test, expected, results
442                );
443            }
444        }
445    }
446
447    impl JsonPointerHandler<'static, Null, Null> for Object {
448        fn eval_jptr<'y>(
449            &'y self,
450            mut pointer: JsonPointerIter<'_, Null>,
451            results: &mut Vec<Cow<'y, Value<'static, Null, Null>>>,
452        ) {
453            match pointer.next() {
454                Some(JsonPointerItem::Key(key)) => {
455                    let key = key.to_string();
456                    if key == "map" {
457                        self.map.eval_jptr(pointer, results);
458                    } else if key == "array" {
459                        self.array.eval_jptr(pointer, results);
460                    } else if key == "value" {
461                        if pointer.peek().is_none() {
462                            results.push(self.value.to_value());
463                        } else {
464                            self.value.eval_jptr(pointer, results);
465                        }
466                    }
467                }
468                Some(JsonPointerItem::Wildcard) => {
469                    self.map.eval_jptr(pointer.clone(), results);
470                    self.array.eval_jptr(pointer.clone(), results);
471                    self.value.eval_jptr(pointer.clone(), results);
472                }
473                Some(JsonPointerItem::Root) | None => {
474                    results.push(self.to_value());
475                }
476                _ => {}
477            }
478        }
479
480        fn patch_jptr<'y: 'static>(
481            &mut self,
482            mut pointer: JsonPointerIter<'_, Null>,
483            value: Value<'y, Null, Null>,
484        ) -> bool {
485            if let Some(JsonPointerItem::Key(key)) = pointer.next() {
486                let key = key.to_string();
487                if pointer.peek().is_some() {
488                    if key == "map" {
489                        return self.map.patch_jptr(pointer, value);
490                    } else if key == "array" {
491                        return self.array.patch_jptr(pointer, value);
492                    } else if key == "value" {
493                        return self.value.patch_jptr(pointer, value);
494                    }
495                } else if key == "map" {
496                    if let Ok(v) = HashMap::<String, SubObject>::try_from(value) {
497                        self.map = v;
498                        return true;
499                    }
500                } else if key == "array" {
501                    if let Ok(v) = Vec::<SubObject>::try_from(value) {
502                        self.array = v;
503                        return true;
504                    }
505                } else if key == "value"
506                    && let Ok(v) = SubObject::try_from(value)
507                {
508                    self.value = v;
509                    return true;
510                }
511            }
512
513            false
514        }
515
516        fn to_value<'y>(&'y self) -> Cow<'y, Value<'static, Null, Null>> {
517            Cow::Owned(Value::Object(ObjectAsVec::from(vec![
518                (Key::Borrowed("map"), self.map.to_value().into_owned()),
519                (Key::Borrowed("array"), self.array.to_value().into_owned()),
520                (Key::Borrowed("value"), self.value.to_value().into_owned()),
521            ])))
522        }
523    }
524
525    impl JsonPointerHandler<'static, Null, Null> for SubObject {
526        fn eval_jptr<'y>(
527            &'y self,
528            mut pointer: JsonPointerIter<'_, Null>,
529            results: &mut Vec<Cow<'y, Value<'_, Null, Null>>>,
530        ) {
531            match pointer.next() {
532                Some(JsonPointerItem::Key(s)) => match s.to_string().as_ref() {
533                    "text" => results.push(Cow::Owned(Value::Str(self.text.clone().into()))),
534                    "number" => results.push(Cow::Owned(Value::Number(self.number.into()))),
535                    "boolean" => results.push(Cow::Owned(Value::Bool(self.boolean))),
536                    _ => {}
537                },
538                Some(JsonPointerItem::Wildcard) if pointer.peek().is_none() => {
539                    results.push(Cow::Owned(Value::Str(self.text.clone().into())));
540                    results.push(Cow::Owned(Value::Number(self.number.into())));
541                    results.push(Cow::Owned(Value::Bool(self.boolean)));
542                }
543                _ => {}
544            }
545        }
546
547        fn patch_jptr<'y: 'static>(
548            &mut self,
549            mut pointer: JsonPointerIter<'_, Null>,
550            value: Value<'y, Null, Null>,
551        ) -> bool {
552            if let Some(JsonPointerItem::Key(s)) = pointer.next() {
553                let has_next = pointer.next().is_some();
554                match s.to_string().as_ref() {
555                    "text" if !has_next => {
556                        if let Some(text) = value.into_string() {
557                            self.text = text.into_owned();
558                            return true;
559                        }
560                    }
561                    "number" if !has_next => {
562                        if let Some(number) = value.as_u64() {
563                            self.number = number;
564                            return true;
565                        }
566                    }
567                    "boolean" if !has_next => {
568                        if let Some(boolean) = value.as_bool() {
569                            self.boolean = boolean;
570                            return true;
571                        }
572                    }
573                    _ => {}
574                }
575            }
576
577            false
578        }
579
580        fn to_value<'y>(&'y self) -> Cow<'y, Value<'static, Null, Null>> {
581            Cow::Owned(Value::Object(ObjectAsVec::from(vec![
582                (Key::Borrowed("text"), Value::Str(self.text.clone().into())),
583                (Key::Borrowed("number"), Value::Number(self.number.into())),
584                (Key::Borrowed("boolean"), Value::Bool(self.boolean)),
585            ])))
586        }
587    }
588
589    impl TryFrom<Value<'_, Null, Null>> for SubObject {
590        type Error = ();
591
592        fn try_from(value: Value<'_, Null, Null>) -> Result<Self, Self::Error> {
593            if let Value::Object(map) = value {
594                let text = map
595                    .get(&Key::Borrowed("text"))
596                    .and_then(|v| v.as_str())
597                    .unwrap_or_default()
598                    .into_owned();
599                let number = map
600                    .get(&Key::Borrowed("number"))
601                    .and_then(Value::as_u64)
602                    .unwrap_or(0);
603                let boolean = map
604                    .get(&Key::Borrowed("boolean"))
605                    .and_then(Value::as_bool)
606                    .unwrap_or(false);
607                Ok(SubObject {
608                    text,
609                    number,
610                    boolean,
611                })
612            } else {
613                Err(())
614            }
615        }
616    }
617
618    fn serialize_ordered_map<S>(
619        map: &HashMap<String, SubObject>,
620        serializer: S,
621    ) -> Result<S::Ok, S::Error>
622    where
623        S: Serializer,
624    {
625        let mut sorted_pairs: Vec<_> = map.iter().collect();
626        sorted_pairs.sort_by_key(|(k, _)| *k);
627
628        use serde::ser::SerializeMap;
629        let mut map_serializer = serializer.serialize_map(Some(sorted_pairs.len()))?;
630        for (k, v) in sorted_pairs {
631            map_serializer.serialize_entry(k, v)?;
632        }
633        map_serializer.end()
634    }
635}