1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
//! Items related to shortest-path indexing of JSON objects

use crate::error::{JsonTy, ResolveError};
use core::cmp::Ordering;
use serde_json::Value;

/// An index on a JSON object, either an integer index on an array or a string index on an object
#[derive(Clone, Debug)]
pub enum Idx {
    /// An array index
    Array(usize),
    /// An object index
    Object(String),
}

impl Idx {
    /// Whether this is an array index
    #[must_use]
    pub fn is_array(&self) -> bool {
        matches!(self, Idx::Array(_))
    }

    /// Whether this is an object index
    #[must_use]
    pub fn is_object(&self) -> bool {
        matches!(self, Idx::Object(_))
    }

    /// Get this index as an array index, or None
    #[must_use]
    pub fn as_array(&self) -> Option<usize> {
        match self {
            Idx::Array(u) => Some(*u),
            _ => None,
        }
    }

    /// Get this index as an object index, or None
    #[must_use]
    pub fn as_object(&self) -> Option<&str> {
        match self {
            Idx::Object(s) => Some(s),
            _ => None,
        }
    }
}

impl From<Idx> for Value {
    fn from(idx: Idx) -> Self {
        match idx {
            Idx::Array(i) => Value::from(i),
            Idx::Object(str) => Value::from(str),
        }
    }
}

/// A shortest-path set of indices on a JSON object
pub struct IdxPath(Vec<Idx>);

impl IdxPath {
    pub(crate) const fn new(indices: Vec<Idx>) -> IdxPath {
        IdxPath(indices)
    }

    /// The length of this path
    #[must_use]
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Whether this path is empty
    #[must_use]
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    /// Reference this path as a raw slice of indices
    #[must_use]
    pub fn raw_path(&self) -> &[Idx] {
        &self.0
    }

    /// Remove the last `n` items from this path
    ///
    /// # Panics
    ///
    /// - If `n` is greater than the length of this path
    #[must_use]
    pub fn remove(&self, n: usize) -> IdxPath {
        assert!(
            n <= self.len(),
            "Cannot remove {} items from path, path is only {} items long",
            n,
            self.len()
        );
        IdxPath(self.0[..self.len() - n].to_owned())
    }

    /// Resolve this path on a value, returning a reference to the result or an error indicating
    /// why the path couldn't be resolved
    ///
    /// # Errors
    ///
    /// - If the path cannot be resolved
    pub fn resolve_on<'a>(&self, value: &'a Value) -> Result<&'a Value, ResolveError> {
        let mut cur = value;

        for idx in &self.0 {
            match idx {
                Idx::Array(i) => {
                    cur = cur
                        .as_array()
                        .ok_or_else(|| ResolveError::mismatched(JsonTy::Array, cur))?
                        .get(*i)
                        .ok_or_else(|| ResolveError::MissingIdx(idx.clone()))?;
                }
                Idx::Object(i) => {
                    cur = cur
                        .as_object()
                        .ok_or_else(|| ResolveError::mismatched(JsonTy::Object, cur))?
                        .get(i)
                        .ok_or_else(|| ResolveError::MissingIdx(idx.clone()))?;
                }
            }
        }

        Ok(cur)
    }

    /// Resolve this path on a value, returning a mutable reference to the result or an error
    /// indicating why the path couldn't be resolved
    ///
    /// # Errors
    ///
    /// - If the path cannot be resolved
    pub fn resolve_on_mut<'a>(&self, value: &'a mut Value) -> Result<&'a mut Value, ResolveError> {
        let mut cur = value;

        for idx in &self.0 {
            match idx {
                Idx::Array(i) => {
                    let json_ty = JsonTy::from(&*cur);
                    cur = cur
                        .as_array_mut()
                        .ok_or(ResolveError::MismatchedTy {
                            expected: JsonTy::Array,
                            actual: json_ty,
                        })?
                        .get_mut(*i)
                        .ok_or_else(|| ResolveError::MissingIdx(idx.clone()))?;
                }
                Idx::Object(i) => {
                    let json_ty = JsonTy::from(&*cur);
                    cur = cur
                        .as_object_mut()
                        .ok_or(ResolveError::MismatchedTy {
                            expected: JsonTy::Array,
                            actual: json_ty,
                        })?
                        .get_mut(i)
                        .ok_or_else(|| ResolveError::MissingIdx(idx.clone()))?;
                }
            }
        }

        Ok(cur)
    }

    pub(crate) fn sort_specific_last(left: &IdxPath, right: &IdxPath) -> Ordering {
        if left.is_empty() && right.is_empty() {
            return Ordering::Equal;
        }

        match right.len().cmp(&left.len()) {
            Ordering::Equal => {
                let left = &left.0[left.len() - 1];
                let right = &right.0[right.len() - 1];
                left.as_array()
                    .and_then(|l| right.as_array().map(|r| (l, r)))
                    .map_or(Ordering::Equal, |(l, r)| r.cmp(&l))
            }
            other => other,
        }
    }
}

impl From<Vec<Idx>> for IdxPath {
    fn from(path: Vec<Idx>) -> Self {
        IdxPath(path)
    }
}