jmap_tools/pointer/
mod.rs

1/*
2 * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
3 *
4 * SPDX-License-Identifier: Apache-2.0 OR MIT
5 */
6
7pub(crate) mod eval;
8pub(crate) mod parser;
9
10use std::{
11    borrow::Cow,
12    fmt::{Debug, Display, Formatter},
13    iter::Peekable,
14    slice::Iter,
15};
16
17use crate::{Element, Key, Property, Value};
18
19pub trait JsonPointerHandler<'x, P: Property, E: Element>: Debug {
20    fn eval_jptr<'y>(
21        &'y self,
22        pointer: JsonPointerIter<'_, P>,
23        results: &mut Vec<Cow<'y, Value<'x, P, E>>>,
24    );
25    fn patch_jptr<'y: 'x>(
26        &mut self,
27        pointer: JsonPointerIter<'_, P>,
28        value: Value<'y, P, E>,
29    ) -> bool;
30    fn to_value<'y>(&'y self) -> Cow<'y, Value<'x, P, E>>;
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
34pub struct JsonPointer<P: Property>(pub(crate) Vec<JsonPointerItem<P>>);
35
36pub type JsonPointerIter<'x, P> = Peekable<Iter<'x, JsonPointerItem<P>>>;
37
38#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
39pub enum JsonPointerItem<P: Property> {
40    Root,
41    Wildcard,
42    Key(Key<'static, P>),
43    Number(u64),
44}
45
46impl<P: Property> JsonPointer<P> {
47    pub fn iter(&self) -> JsonPointerIter<'_, P> {
48        self.0.iter().peekable()
49    }
50
51    #[allow(clippy::should_implement_trait)]
52    pub fn into_iter(self) -> impl Iterator<Item = JsonPointerItem<P>> {
53        self.0.into_iter()
54    }
55
56    pub fn into_inner(self) -> Vec<JsonPointerItem<P>> {
57        self.0
58    }
59
60    pub fn encode<I, T>(items: I) -> String
61    where
62        I: IntoIterator<Item = T>,
63        T: AsRef<str>,
64    {
65        let mut encoded = String::with_capacity(8);
66        for (pos, item) in items.into_iter().enumerate() {
67            if pos > 0 {
68                encoded.push('/');
69            }
70            let item = item.as_ref();
71            for c in item.chars() {
72                match c {
73                    '~' => encoded.push_str("~0"),
74                    '/' => encoded.push_str("~1"),
75                    _ => encoded.push(c),
76                }
77            }
78        }
79        encoded
80    }
81
82    pub fn first(&self) -> Option<&JsonPointerItem<P>> {
83        self.0.first()
84    }
85
86    pub fn last(&self) -> Option<&JsonPointerItem<P>> {
87        self.0.last()
88    }
89
90    pub fn len(&self) -> usize {
91        self.0.len()
92    }
93
94    pub fn is_empty(&self) -> bool {
95        self.0.is_empty()
96    }
97}
98
99impl<P: Property> JsonPointerItem<P> {
100    pub fn as_key(&self) -> Option<&Key<'static, P>> {
101        match self {
102            JsonPointerItem::Key(key) => Some(key),
103            _ => None,
104        }
105    }
106
107    pub fn as_property_key(&self) -> Option<&P> {
108        match self {
109            JsonPointerItem::Key(Key::Property(key)) => Some(key),
110            _ => None,
111        }
112    }
113
114    pub fn as_string_key(&self) -> Option<&str> {
115        match self {
116            JsonPointerItem::Key(Key::Borrowed(key)) => Some(key),
117            JsonPointerItem::Key(Key::Owned(key)) => Some(key),
118            _ => None,
119        }
120    }
121
122    pub fn to_cow(&self) -> Option<Cow<'_, str>> {
123        match self {
124            JsonPointerItem::Key(Key::Property(key)) => Some(key.to_cow()),
125            JsonPointerItem::Key(Key::Borrowed(key)) => Some(Cow::Borrowed(key)),
126            JsonPointerItem::Key(Key::Owned(key)) => Some(Cow::Borrowed(key)),
127            _ => None,
128        }
129    }
130}
131
132impl<P: Property> Display for JsonPointer<P> {
133    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
134        for (i, ptr) in self.0.iter().enumerate() {
135            if i > 0 {
136                write!(f, "/")?;
137            }
138
139            match ptr {
140                JsonPointerItem::Root => {}
141                JsonPointerItem::Wildcard => write!(f, "*")?,
142                JsonPointerItem::Key(k) => {
143                    for c in k.to_string().chars() {
144                        match c {
145                            '~' => write!(f, "~0")?,
146                            '/' => write!(f, "~1")?,
147                            _ => write!(f, "{}", c)?,
148                        }
149                    }
150                }
151                JsonPointerItem::Number(n) => write!(f, "{}", n)?,
152            }
153        }
154        Ok(())
155    }
156}