jsonpath_plus/
idx.rs

1//! Items related to shortest-path indexing of JSON objects
2
3use crate::error::{JsonTy, ResolveError};
4use core::cmp::Ordering;
5use serde_json::Value;
6use std::ops::{Deref, Index, IndexMut};
7
8/// An index on a JSON object, either an integer index on an array or a string index on an object
9#[derive(Clone, Debug, PartialEq)]
10pub enum Idx {
11    /// An array index
12    Array(usize),
13    /// An object index
14    Object(String),
15}
16
17impl Idx {
18    /// Whether this is an array index
19    #[must_use]
20    pub fn is_array(&self) -> bool {
21        matches!(self, Idx::Array(_))
22    }
23
24    /// Whether this is an object index
25    #[must_use]
26    pub fn is_object(&self) -> bool {
27        matches!(self, Idx::Object(_))
28    }
29
30    /// Get this index as an array index, or None
31    #[must_use]
32    pub fn as_array(&self) -> Option<usize> {
33        match self {
34            Idx::Array(u) => Some(*u),
35            _ => None,
36        }
37    }
38
39    /// Get this index as an object index, or None
40    #[must_use]
41    pub fn as_object(&self) -> Option<&str> {
42        match self {
43            Idx::Object(s) => Some(s),
44            _ => None,
45        }
46    }
47}
48
49impl From<Idx> for Value {
50    fn from(idx: Idx) -> Self {
51        match idx {
52            Idx::Array(i) => Value::from(i),
53            Idx::Object(str) => Value::from(str),
54        }
55    }
56}
57
58impl Index<&Idx> for Value {
59    type Output = Value;
60
61    fn index(&self, index: &Idx) -> &Self::Output {
62        match (self, index) {
63            (Value::Array(a), Idx::Array(idx)) => &a[*idx],
64            (Value::Object(o), Idx::Object(idx)) => &o[idx],
65            (val, idx) => panic!("Invalid index {:?} for value {:?}", val, idx),
66        }
67    }
68}
69
70impl IndexMut<&Idx> for Value {
71    fn index_mut(&mut self, index: &Idx) -> &mut Self::Output {
72        match (self, index) {
73            (Value::Array(a), Idx::Array(idx)) => &mut a[*idx],
74            (Value::Object(o), Idx::Object(idx)) => &mut o[idx],
75            (val, idx) => panic!("Invalid index {:?} for value {:?}", val, idx),
76        }
77    }
78}
79
80/// A shortest-path set of indices on a JSON object
81#[derive(Clone, Debug, PartialEq)]
82pub struct IdxPath(Vec<Idx>);
83
84impl IdxPath {
85    pub(crate) const fn new(indices: Vec<Idx>) -> IdxPath {
86        IdxPath(indices)
87    }
88
89    /// Reference this path as a raw slice of indices
90    #[must_use]
91    pub fn raw_path(&self) -> &[Idx] {
92        &self.0
93    }
94
95    /// Remove the last `n` items from this path
96    ///
97    /// # Panics
98    ///
99    /// - If `n` is greater than the length of this path
100    #[must_use]
101    pub fn remove(&self, n: usize) -> IdxPath {
102        assert!(
103            n <= self.len(),
104            "Cannot remove {} items from path, path is only {} items long",
105            n,
106            self.len()
107        );
108        IdxPath(self.0[..self.len() - n].to_owned())
109    }
110
111    /// Resolve this path on a value, returning a reference to the result or an error indicating
112    /// why the path couldn't be resolved
113    ///
114    /// # Errors
115    ///
116    /// - If the path cannot be resolved
117    pub fn resolve_on<'a>(&self, value: &'a Value) -> Result<&'a Value, ResolveError> {
118        let mut cur = value;
119
120        for idx in &self.0 {
121            match idx {
122                Idx::Array(i) => {
123                    cur = cur
124                        .as_array()
125                        .ok_or_else(|| ResolveError::mismatched(JsonTy::Array, cur))?
126                        .get(*i)
127                        .ok_or_else(|| ResolveError::MissingIdx(idx.clone()))?;
128                }
129                Idx::Object(i) => {
130                    cur = cur
131                        .as_object()
132                        .ok_or_else(|| ResolveError::mismatched(JsonTy::Object, cur))?
133                        .get(i)
134                        .ok_or_else(|| ResolveError::MissingIdx(idx.clone()))?;
135                }
136            }
137        }
138
139        Ok(cur)
140    }
141
142    /// Resolve this path on a value, returning a mutable reference to the result or an error
143    /// indicating why the path couldn't be resolved
144    ///
145    /// # Errors
146    ///
147    /// - If the path cannot be resolved
148    pub fn resolve_on_mut<'a>(&self, value: &'a mut Value) -> Result<&'a mut Value, ResolveError> {
149        let mut cur = value;
150
151        for idx in &self.0 {
152            match idx {
153                Idx::Array(i) => {
154                    let json_ty = JsonTy::from(&*cur);
155                    cur = cur
156                        .as_array_mut()
157                        .ok_or(ResolveError::MismatchedTy {
158                            expected: JsonTy::Array,
159                            actual: json_ty,
160                        })?
161                        .get_mut(*i)
162                        .ok_or_else(|| ResolveError::MissingIdx(idx.clone()))?;
163                }
164                Idx::Object(i) => {
165                    let json_ty = JsonTy::from(&*cur);
166                    cur = cur
167                        .as_object_mut()
168                        .ok_or(ResolveError::MismatchedTy {
169                            expected: JsonTy::Array,
170                            actual: json_ty,
171                        })?
172                        .get_mut(i)
173                        .ok_or_else(|| ResolveError::MissingIdx(idx.clone()))?;
174                }
175            }
176        }
177
178        Ok(cur)
179    }
180
181    pub(crate) fn sort_specific_last(left: &IdxPath, right: &IdxPath) -> Ordering {
182        if left.is_empty() && right.is_empty() {
183            return Ordering::Equal;
184        }
185
186        match right.len().cmp(&left.len()) {
187            Ordering::Equal => {
188                let left = &left.0[left.len() - 1];
189                let right = &right.0[right.len() - 1];
190                left.as_array()
191                    .and_then(|l| right.as_array().map(|r| (l, r)))
192                    .map_or(Ordering::Equal, |(l, r)| r.cmp(&l))
193            }
194            other => other,
195        }
196    }
197}
198
199impl Deref for IdxPath {
200    type Target = [Idx];
201
202    fn deref(&self) -> &Self::Target {
203        &self.0
204    }
205}
206
207impl From<Vec<Idx>> for IdxPath {
208    fn from(path: Vec<Idx>) -> Self {
209        IdxPath(path)
210    }
211}