Skip to main content

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