jsonpath_plus/
lib.rs

1//! Implementation of the `JSONPath` spec, Proposal A with extensions.
2
3#![forbid(unsafe_code)]
4#![warn(
5    missing_docs,
6    elided_lifetimes_in_paths,
7    explicit_outlives_requirements,
8    missing_abi,
9    noop_method_call,
10    pointer_structural_match,
11    semicolon_in_expressions_from_macros,
12    unused_import_braces,
13    unused_lifetimes,
14    clippy::cargo,
15    clippy::missing_panics_doc,
16    clippy::doc_markdown,
17    clippy::ptr_as_ptr,
18    clippy::cloned_instead_of_copied,
19    clippy::unreadable_literal,
20    clippy::must_use_candidate
21)]
22#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
23
24use serde_json::Value;
25
26use ast::Span;
27use error::{ParseError, ParseOrJsonError};
28use eval::EvalCtx;
29use idx::{Idx, IdxPath};
30use utils::{delete_paths, replace_paths, try_replace_paths};
31
32pub mod ast;
33pub mod error;
34mod eval;
35pub mod idx;
36mod utils;
37
38#[doc(inline)]
39pub use ast::Path as JsonPath;
40
41/// Find a pattern in the provided JSON value. Recompiles the pattern every call, if the same
42/// pattern is used a lot should instead try using [`JsonPath::compile`].
43///
44/// # Errors
45///
46/// - If the provided pattern fails to parse as a valid JSON path
47pub fn find<'a>(pattern: &str, value: &'a Value) -> Result<Vec<&'a Value>, ParseError> {
48    Ok(JsonPath::compile(pattern)?.find(value))
49}
50
51/// Find a pattern in the provided JSON string. Recompiles the pattern every call, if the same
52/// pattern is used a lot should instead try using [`JsonPath::compile`].
53///
54/// # Errors
55///
56/// - If the provided pattern fails to parse as a valid JSON path
57/// - If the provided value fails to deserialize
58pub fn find_str(pattern: &str, value: &str) -> Result<Vec<Value>, ParseOrJsonError> {
59    Ok(JsonPath::compile(pattern)?.find_str(value)?)
60}
61
62impl JsonPath {
63    /// Compile a JSON path, which can be used to match items multiple times.
64    ///
65    /// # Errors
66    ///
67    /// - If the provided pattern fails to parse as a valid JSON path
68    pub fn compile(pattern: &str) -> Result<JsonPath, ParseError> {
69        use chumsky::{Parser, Stream};
70
71        let len = pattern.chars().count();
72        let stream = Stream::from_iter(
73            Span::from(len..len),
74            Box::new(
75                pattern
76                    .chars()
77                    .enumerate()
78                    .map(|(i, c)| (c, Span::from(i..i + 1))),
79            ),
80        );
81
82        Self::parser()
83            .parse(stream)
84            .map_err(|e| ParseError::new(pattern, e))
85    }
86
87    /// Find this pattern in the provided JSON value
88    #[must_use = "this does not modify the path or provided value"]
89    pub fn find<'a>(&self, value: &'a Value) -> Vec<&'a Value> {
90        let mut ctx = EvalCtx::new(value);
91        if self.has_parent() {
92            ctx.prepopulate_parents();
93        }
94        self.eval(&mut ctx);
95        ctx.into_matched()
96    }
97
98    /// Find this pattern in the provided JSON value, and return the shortest paths to all found
99    /// values as a chain of indices
100    #[must_use = "this does not modify the path or provided value"]
101    pub fn find_paths(&self, value: &Value) -> Vec<IdxPath> {
102        let mut ctx = EvalCtx::new(value);
103        ctx.prepopulate_parents();
104        self.eval(&mut ctx);
105        ctx.paths_matched()
106    }
107
108    /// Delete all items matched by this pattern on the provided JSON value, and return the
109    /// resulting object
110    #[must_use = "this returns the new value, without modifying the original. To work in-place, \
111                  use `delete_on`"]
112    pub fn delete(&self, value: &Value) -> Value {
113        let paths = self.find_paths(value);
114        let mut out = value.clone();
115        delete_paths(paths, &mut out);
116        out
117    }
118
119    /// Delete all items matched by this pattern on the provided JSON value, operating in-place
120    pub fn delete_on(&self, value: &mut Value) {
121        let paths = self.find_paths(value);
122        delete_paths(paths, value);
123    }
124
125    /// Replace items matched by this pattern on the provided JSON value, filling them with the
126    /// value returned by the provided function, then return the resulting object
127    #[must_use = "this returns the new value, without modifying the original. To work in-place, \
128                  use `replace_on`"]
129    pub fn replace(&self, value: &Value, f: impl FnMut(&Value) -> Value) -> Value {
130        let paths = self.find_paths(value);
131        let mut out = value.clone();
132        replace_paths(paths, &mut out, f);
133        out
134    }
135
136    /// Replace items matched by this pattern on the provided JSON value, filling them the value
137    /// returned by the provided function, operating in-place
138    pub fn replace_on(&self, value: &mut Value, f: impl FnMut(&Value) -> Value) {
139        let paths = self.find_paths(value);
140        replace_paths(paths, value, f);
141    }
142
143    /// Replace or delete items matched by this pattern on the provided JSON value. Replaces if the
144    /// provided method returns `Some`, deletes if the provided method returns `None`. This method
145    /// then returns the resulting object
146    #[must_use = "this returns the new value, without modifying the original. To work in-place, \
147                  use `try_replace_on`"]
148    pub fn try_replace(&self, value: &Value, f: impl FnMut(&Value) -> Option<Value>) -> Value {
149        let paths = self.find_paths(value);
150        let mut out = value.clone();
151        try_replace_paths(paths, &mut out, f);
152        out
153    }
154
155    /// Replace or delete items matched by this pattern on the provided JSON value. Replaces if the
156    /// provided method returns `Some`, deletes if the provided method returns `None`. This method
157    /// operates in-place on the provided value
158    pub fn try_replace_on(&self, value: &mut Value, f: impl FnMut(&Value) -> Option<Value>) {
159        let paths = self.find_paths(value);
160        try_replace_paths(paths, value, f);
161    }
162
163    /// Find this pattern in the provided JSON string
164    ///
165    /// # Errors
166    ///
167    /// - If the provided value fails to deserialize
168    pub fn find_str(&self, str: &str) -> Result<Vec<Value>, serde_json::Error> {
169        let val = serde_json::from_str(str)?;
170        Ok(self.find(&val).into_iter().cloned().collect())
171    }
172
173    /// Delete items matching this pattern in the provided JSON string
174    ///
175    /// # Errors
176    ///
177    /// - If the provided value fails to deserialize
178    pub fn delete_str(&self, str: &str) -> Result<Value, serde_json::Error> {
179        let val = serde_json::from_str(str)?;
180        Ok(self.delete(&val))
181    }
182
183    /// Replace items matching this pattern in the provided JSON string
184    ///
185    /// # Errors
186    ///
187    /// - If the provided value fails to deserialize
188    pub fn replace_str(
189        &self,
190        str: &str,
191        f: impl FnMut(&Value) -> Value,
192    ) -> Result<Value, serde_json::Error> {
193        let val = serde_json::from_str(str)?;
194        Ok(self.replace(&val, f))
195    }
196
197    /// Replace or delete items matching this pattern in the provided JSON string
198    ///
199    /// # Errors
200    ///
201    /// - If the provided value fails to deserialize
202    pub fn try_replace_str(
203        &self,
204        str: &str,
205        f: impl FnMut(&Value) -> Option<Value>,
206    ) -> Result<Value, serde_json::Error> {
207        let val = serde_json::from_str(str)?;
208        Ok(self.try_replace(&val, f))
209    }
210}
211
212#[cfg(test)]
213mod tests;