Skip to main content

yo_doc/
path.rs

1//! Reaching one field of a document without decoding the rest of it.
2//!
3//! `$.a.b[3].c` is four steps, and each step is a header read, a binary search
4//! or an index, and a seek. A ten kilobyte document with a four level path is
5//! four cache lines and no allocation, which is the whole reason the encoding
6//! is shaped the way it is.
7//!
8//! ```
9//! use yo_doc::{Builder, Value};
10//!
11//! let mut b = Builder::new();
12//! b.begin_object().unwrap();
13//! b.key(b"user").unwrap();
14//! b.begin_object().unwrap();
15//! b.key(b"tags").unwrap();
16//! b.begin_array().unwrap();
17//! b.text("a").unwrap();
18//! b.text("b").unwrap();
19//! b.end_array().unwrap();
20//! b.end_object().unwrap();
21//! b.end_object().unwrap();
22//! let bytes = b.finish().unwrap();
23//!
24//! let v = Value::new(&bytes).unwrap();
25//! assert_eq!(v.path("$.user.tags[1]").unwrap().unwrap().as_text(), Some("b"));
26//! assert_eq!(v.path("$.user.tags[-1]").unwrap().unwrap().as_text(), Some("b"));
27//! assert!(v.path("$.user.missing").unwrap().is_none());
28//! ```
29//!
30//! # What this grammar is
31//!
32//! The part of JSONPath that names exactly one place: a root, member access by
33//! name, and element access by index counting from either end. The parts that
34//! name a set of places, `[*]` and `..` and `?(@.x > 1)`, come with the
35//! RedisJSON surface, because they answer a list and everything here answers at
36//! most one value.
37
38use yo_common::{Code, Error, Result};
39
40use crate::head::Kind;
41use crate::read::Value;
42
43/// One step of a path.
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum Step<'a> {
46    /// A member of an object, by name.
47    Key(&'a [u8]),
48    /// An element of an array. Negative counts back from the end, so -1 is the
49    /// last element.
50    Index(i64),
51}
52
53/// The steps of a path, parsed as they are walked.
54///
55/// Nothing is collected, so a path costs no allocation and a path that turns
56/// out to be nonsense costs only as much of itself as was read before the
57/// nonsense.
58#[derive(Debug, Clone)]
59pub struct Steps<'a> {
60    rest: &'a [u8],
61}
62
63impl<'a> Steps<'a> {
64    /// The steps of `path`.
65    ///
66    /// A leading `$` is the root and is optional, so both `$.a.b` and `a.b`
67    /// parse to the same two steps.
68    #[must_use]
69    pub fn new(path: &'a [u8]) -> Steps<'a> {
70        let rest = path.strip_prefix(b"$").unwrap_or(path);
71        Steps { rest }
72    }
73}
74
75impl<'a> Iterator for Steps<'a> {
76    type Item = Result<Step<'a>>;
77
78    fn next(&mut self) -> Option<Result<Step<'a>>> {
79        if self.rest.is_empty() {
80            return None;
81        }
82        Some(self.step())
83    }
84}
85
86impl<'a> Steps<'a> {
87    fn step(&mut self) -> Result<Step<'a>> {
88        match self.rest[0] {
89            b'.' => {
90                self.rest = &self.rest[1..];
91                if self.rest.first() == Some(&b'.') {
92                    return Err(bad("a descent, `..`, names more than one place"));
93                }
94                let end = self
95                    .rest
96                    .iter()
97                    .position(|&c| c == b'.' || c == b'[')
98                    .unwrap_or(self.rest.len());
99                if end == 0 {
100                    return Err(bad("a `.` with no name after it"));
101                }
102                let (name, rest) = self.rest.split_at(end);
103                self.rest = rest;
104                Ok(Step::Key(name))
105            }
106            b'[' => self.bracket(),
107            _ => {
108                // A path may start with a bare name, so that `a.b` and `$.a.b`
109                // both work. Anywhere else this is a missing separator.
110                let end = self
111                    .rest
112                    .iter()
113                    .position(|&c| c == b'.' || c == b'[')
114                    .unwrap_or(self.rest.len());
115                let (name, rest) = self.rest.split_at(end);
116                self.rest = rest;
117                Ok(Step::Key(name))
118            }
119        }
120    }
121
122    fn bracket(&mut self) -> Result<Step<'a>> {
123        let body = &self.rest[1..];
124        let Some(close) = body.iter().position(|&c| c == b']') else {
125            return Err(bad("a `[` with no `]` after it"));
126        };
127        let inner = &body[..close];
128        self.rest = &body[close + 1..];
129        if inner == b"*" {
130            return Err(bad("a wildcard, `[*]`, names more than one place"));
131        }
132        if let Some(quoted) = quoted(inner) {
133            return Ok(Step::Key(quoted));
134        }
135        let text = core::str::from_utf8(inner).map_err(|_| bad("an index that is not a number"))?;
136        let i: i64 = text
137            .parse()
138            .map_err(|_| bad("an index that is not a number"))?;
139        Ok(Step::Index(i))
140    }
141}
142
143/// The bytes inside `"..."` or `'...'`, if that is what this is.
144fn quoted(inner: &[u8]) -> Option<&[u8]> {
145    if inner.len() >= 2 {
146        let (first, last) = (inner[0], inner[inner.len() - 1]);
147        if (first == b'"' || first == b'\'') && last == first {
148            return Some(&inner[1..inner.len() - 1]);
149        }
150    }
151    None
152}
153
154impl<'a> Value<'a> {
155    /// The value at `path`, if there is one there.
156    ///
157    /// `Ok(None)` is a path that is well formed and names nothing, which is a
158    /// normal answer and not a failure. `Err` is a path that does not parse.
159    pub fn path(&self, path: &str) -> Result<Option<Value<'a>>> {
160        self.path_bytes(path.as_bytes())
161    }
162
163    /// [`Value::path`] over bytes, for the RESP side where a path arrives as a
164    /// bulk string.
165    pub fn path_bytes(&self, path: &[u8]) -> Result<Option<Value<'a>>> {
166        let mut at = *self;
167        for step in Steps::new(path) {
168            let Some(next) = at.step(step?) else {
169                return Ok(None);
170            };
171            at = next;
172        }
173        Ok(Some(at))
174    }
175
176    /// One step down from here.
177    #[must_use]
178    pub fn step(&self, step: Step<'_>) -> Option<Value<'a>> {
179        match step {
180            Step::Key(k) => self.get(k),
181            Step::Index(i) => {
182                if self.kind() != Kind::Array {
183                    return None;
184                }
185                let n = self.len();
186                let at = if i < 0 {
187                    n.checked_sub(i.unsigned_abs() as usize)?
188                } else {
189                    i as usize
190                };
191                self.at(at)
192            }
193        }
194    }
195}
196
197fn bad(what: &str) -> Error {
198    Error::new(Code::Invalid, what)
199}
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204    use crate::Builder;
205
206    /// `{"a": {"b": [10, 20, {"c": "deep"}]}, "empty": {}}`
207    fn doc() -> Vec<u8> {
208        let mut b = Builder::new();
209        b.begin_object().expect("open");
210        b.key(b"a").expect("key");
211        b.begin_object().expect("open");
212        b.key(b"b").expect("key");
213        b.begin_array().expect("open");
214        b.int(10).expect("value");
215        b.int(20).expect("value");
216        b.begin_object().expect("open");
217        b.key(b"c").expect("key");
218        b.text("deep").expect("value");
219        b.end_object().expect("close");
220        b.end_array().expect("close");
221        b.end_object().expect("close");
222        b.key(b"empty").expect("key");
223        b.begin_object().expect("open");
224        b.end_object().expect("close");
225        b.end_object().expect("close");
226        b.finish().expect("finished").to_vec()
227    }
228
229    #[test]
230    fn a_path_reaches_what_it_names() {
231        let bytes = doc();
232        let v = Value::new(&bytes).expect("readable");
233        let at = |p: &str| v.path(p).expect("the path parses");
234        assert_eq!(at("$.a.b[0]").expect("there").as_int(), Some(10));
235        assert_eq!(at("$.a.b[2].c").expect("there").as_text(), Some("deep"));
236        assert_eq!(at("$.a.b[-1].c").expect("there").as_text(), Some("deep"));
237        assert_eq!(at("$.a.b[-3]").expect("there").as_int(), Some(10));
238        assert_eq!(at("$").expect("there").len(), 2);
239        assert_eq!(at("").expect("there").len(), 2);
240        // The three ways of writing the same step.
241        assert_eq!(at("$.a.b[0]").expect("there").as_int(), Some(10));
242        assert_eq!(at("$[\"a\"][\"b\"][0]").expect("there").as_int(), Some(10));
243        assert_eq!(at("a.b[0]").expect("there").as_int(), Some(10));
244        assert_eq!(at("$['a'].b[0]").expect("there").as_int(), Some(10));
245    }
246
247    #[test]
248    fn a_path_that_names_nothing_is_an_answer_and_not_a_failure() {
249        let bytes = doc();
250        let v = Value::new(&bytes).expect("readable");
251        let at = |p: &str| v.path(p).expect("the path parses");
252        assert!(at("$.nope").is_none());
253        assert!(at("$.a.nope.deeper").is_none());
254        assert!(at("$.a.b[3]").is_none(), "past the end");
255        assert!(at("$.a.b[-4]").is_none(), "past the start");
256        assert!(at("$.empty.anything").is_none());
257        assert!(at("$.a.b.c").is_none(), "a name into an array");
258        assert!(at("$.a[0]").is_none(), "an index into an object");
259        assert!(at("$.a.b[0][0]").is_none(), "an index into a number");
260    }
261
262    #[test]
263    fn a_path_that_does_not_parse_says_so() {
264        let bytes = doc();
265        let v = Value::new(&bytes).expect("readable");
266        let why = |p: &str| v.path(p).unwrap_err().message().to_string();
267        assert!(why("$.a[").contains("no `]`"));
268        assert!(why("$.a[x]").contains("not a number"));
269        assert!(why("$..a").contains("more than one place"));
270        assert!(why("$.a[*]").contains("more than one place"));
271        assert!(why("$.a.").contains("no name after it"));
272    }
273
274    #[test]
275    fn the_steps_of_a_path_are_what_they_look_like() {
276        let steps: Vec<Step<'_>> = Steps::new(b"$.a[3].bb[-1][\"c c\"]")
277            .map(|s| s.expect("parses"))
278            .collect();
279        assert_eq!(
280            steps,
281            [
282                Step::Key(b"a"),
283                Step::Index(3),
284                Step::Key(b"bb"),
285                Step::Index(-1),
286                Step::Key(b"c c"),
287            ]
288        );
289    }
290
291    #[test]
292    fn a_path_over_bytes_reads_the_same_as_a_path_over_text() {
293        let bytes = doc();
294        let v = Value::new(&bytes).expect("readable");
295        let one = v.path("$.a.b[1]").expect("parses").expect("there");
296        let two = v.path_bytes(b"$.a.b[1]").expect("parses").expect("there");
297        assert_eq!(one.as_int(), two.as_int());
298    }
299}