jsonpath_rust/query/queryable.rs
1use crate::parser::errors::JsonPathError;
2use crate::parser::model::{JpQuery, Segment, Selector};
3use crate::parser::{parse_json_path, Parsed};
4use crate::query::QueryPath;
5use serde_json::Value;
6use std::borrow::Cow;
7use std::fmt::Debug;
8
9/// A trait that abstracts JSON-like data structures for JSONPath queries
10///
11/// This trait provides the essential operations needed to traverse and query
12/// hierarchical data structures in a JSONPath-compatible way. Implementors of
13/// this trait can be used with the JSONPath query engine.
14///
15/// The trait requires several standard type conversions to be implemented to
16/// ensure that query operations can properly handle various data types.
17///
18/// # Type Requirements
19///
20/// Implementing types must satisfy these trait bounds:
21/// - `Default`: Provides a default value for the type
22/// - `Clone`: Allows creation of copies of values
23/// - `Debug`: Enables debug formatting
24/// - `From<&str>`: Conversion from string slices
25/// - `From<bool>`: Conversion from boolean values
26/// - `From<i64>`: Conversion from 64-bit integers
27/// - `From<f64>`: Conversion from 64-bit floating point values
28/// - `From<Vec<Self>>`: Conversion from vectors of the same type
29/// - `From<String>`: Conversion from owned strings
30/// - `PartialEq`: Allows equality comparisons
31///
32/// # Examples
33///
34/// The trait is primarily implemented for `serde_json::Value` to enable
35/// JSONPath queries on JSON data structures:
36///
37/// ```
38/// use serde_json::json;
39/// use jsonpath_rust::JsonPath;
40///
41/// let data = json!({
42/// "store": {
43/// "books": [
44/// {"title": "Book 1", "price": 10},
45/// {"title": "Book 2", "price": 15}
46/// ]
47/// }
48/// });
49///
50/// // Access data using the Queryable trait
51/// let books = data.query("$.store.books[*].title").expect("no errors");
52/// ```
53pub trait Queryable
54where
55 Self: Default
56 + Clone
57 + Debug
58 + for<'a> From<&'a str>
59 + From<bool>
60 + From<i64>
61 + From<f64>
62 + From<Vec<Self>>
63 + From<String>
64 + PartialEq,
65{
66 /// Retrieves a reference to the value associated with the given key.
67 /// It is the responsibility of the implementation to handle enclosing single and double quotes.
68 /// The key will be normalized (quotes trimmed, whitespace handled, the escape symbols handled) before lookup.
69 fn get(&self, key: &str) -> Option<&Self>;
70
71 fn as_array(&self) -> Option<&Vec<Self>>;
72
73 fn as_object(&self) -> Option<Vec<(&String, &Self)>>;
74
75 fn as_str(&self) -> Option<&str>;
76
77 fn as_i64(&self) -> Option<i64>;
78 fn as_f64(&self) -> Option<f64>;
79 fn as_bool(&self) -> Option<bool>;
80
81 /// Returns a null value.
82 fn null() -> Self;
83
84 fn extension_custom(_name: &str, _args: Vec<Cow<Self>>) -> Self {
85 Self::null()
86 }
87
88 /// Retrieves a reference to the element at the specified path.
89 /// The path is specified as a string and can be obtained from the query.
90 ///
91 /// # Arguments
92 /// * `path` - A json path to the element specified as a string (root, field, index only).
93 fn reference<T>(&self, _path: T) -> Option<&Self>
94 where
95 T: Into<QueryPath>,
96 {
97 None
98 }
99
100 /// Retrieves a mutable reference to the element at the specified path.
101 ///
102 /// # Arguments
103 /// * `path` - A json path to the element specified as a string (root, field, index only).
104 ///
105 /// # Examples
106 ///
107 /// ```
108 /// use serde_json::json;
109 /// use jsonpath_rust::JsonPath;
110 /// use jsonpath_rust::query::queryable::Queryable;
111 /// let mut json = json!({
112 /// "a": {
113 /// "b": {
114 /// "c": 42
115 /// }
116 /// }
117 /// });
118 /// if let Some(path) = json.query_only_path("$.a.b.c").unwrap().first() {
119 /// if let Some(v) = json.reference_mut("$.a.b.c") {
120 /// *v = json!(43);
121 /// }
122 ///
123 /// assert_eq!(
124 /// json,
125 /// json!({
126 /// "a": {
127 /// "b": {
128 /// "c": 43
129 /// }
130 /// }
131 /// })
132 /// );
133 /// }
134 //// ```
135 fn reference_mut<T>(&mut self, _path: T) -> Option<&mut Self>
136 where
137 T: Into<QueryPath>,
138 {
139 None
140 }
141}
142
143impl Queryable for Value {
144 fn get(&self, key: &str) -> Option<&Self> {
145 let key = if key.starts_with("'") && key.ends_with("'") {
146 key.trim_matches(|c| c == '\'')
147 } else if key.starts_with('"') && key.ends_with('"') {
148 key.trim_matches(|c| c == '"')
149 } else {
150 key
151 };
152 self.get(key)
153 }
154
155 fn as_array(&self) -> Option<&Vec<Self>> {
156 self.as_array()
157 }
158
159 fn as_object(&self) -> Option<Vec<(&String, &Self)>> {
160 self.as_object()
161 .map(|v| v.into_iter().map(|(k, v)| (k, v)).collect())
162 }
163
164 fn as_str(&self) -> Option<&str> {
165 self.as_str()
166 }
167
168 fn as_i64(&self) -> Option<i64> {
169 self.as_i64()
170 }
171
172 fn as_f64(&self) -> Option<f64> {
173 self.as_f64()
174 }
175
176 fn as_bool(&self) -> Option<bool> {
177 self.as_bool()
178 }
179
180 fn null() -> Self {
181 Value::Null
182 }
183
184 /// Custom extension function for JSONPath queries.
185 ///
186 /// This function allows for custom operations to be performed on JSON data
187 /// based on the provided `name` and `args`.
188 ///
189 /// # Arguments
190 ///
191 /// * `name` - A string slice that holds the name of the custom function.
192 /// * `args` - A vector of `Cow<Self>` that holds the arguments for the custom function.
193 ///
194 /// # Returns
195 ///
196 /// Returns a `Self` value which is the result of the custom function. If the function
197 /// name is not recognized, it returns `Self::null()`.
198 ///
199 /// # Custom Functions
200 ///
201 /// * `"in"` - Checks if the first argument is in the array provided as the second argument.
202 /// Example: `$.elems[?in(@, $.list)]` - Returns elements from $.elems that are present in $.list
203 ///
204 /// * `"nin"` - Checks if the first argument is not in the array provided as the second argument.
205 /// Example: `$.elems[?nin(@, $.list)]` - Returns elements from $.elems that are not present in $.list
206 ///
207 /// * `"none_of"` - Checks if none of the elements in the first array are in the second array.
208 /// Example: `$.elems[?none_of(@, $.list)]` - Returns arrays from $.elems that have no elements in common with $.list
209 ///
210 /// * `"any_of"` - Checks if any of the elements in the first array are in the second array.
211 /// Example: `$.elems[?any_of(@, $.list)]` - Returns arrays from $.elems that have at least one element in common with $.list
212 ///
213 /// * `"subset_of"` - Checks if all elements in the first array are in the second array.
214 /// Example: `$.elems[?subset_of(@, $.list)]` - Returns arrays from $.elems where all elements are present in $.list
215 fn extension_custom(name: &str, args: Vec<Cow<Self>>) -> Self {
216 match name {
217 "in" => match args.as_slice() {
218 [lhs, rhs] => match rhs.as_array() {
219 Some(elements) => elements.iter().any(|item| item == lhs.as_ref()).into(),
220 None => Self::null(),
221 },
222 _ => Self::null(),
223 },
224 "nin" => match args.as_slice() {
225 [lhs, rhs] => match rhs.as_array() {
226 Some(elements) => (!elements.iter().any(|item| item == lhs.as_ref())).into(),
227 None => Self::null(),
228 },
229 _ => Self::null(),
230 },
231 "none_of" => match args.as_slice() {
232 [lhs, rhs] => match (lhs.as_array(), rhs.as_array()) {
233 (Some(lhs_arr), Some(rhs_arr)) => lhs_arr
234 .iter()
235 .all(|lhs| !rhs_arr.iter().any(|rhs| lhs == rhs))
236 .into(),
237 _ => Self::null(),
238 },
239 _ => Self::null(),
240 },
241 "any_of" => match args.as_slice() {
242 [lhs, rhs] => match (lhs.as_array(), rhs.as_array()) {
243 (Some(lhs_arr), Some(rhs_arr)) => lhs_arr
244 .iter()
245 .any(|lhs| rhs_arr.iter().any(|rhs| lhs == rhs))
246 .into(),
247 _ => Self::null(),
248 },
249 _ => Self::null(),
250 },
251 "subset_of" => match args.as_slice() {
252 [lhs, rhs] => match (lhs.as_array(), rhs.as_array()) {
253 (Some(lhs_arr), Some(rhs_arr)) => lhs_arr
254 .iter()
255 .all(|lhs| rhs_arr.iter().any(|rhs| lhs == rhs))
256 .into(),
257 _ => Self::null(),
258 },
259 _ => Self::null(),
260 },
261 _ => Self::null(),
262 }
263 }
264
265 fn reference<T>(&self, path: T) -> Option<&Self>
266 where
267 T: Into<QueryPath>,
268 {
269 convert_js_path(&path.into())
270 .ok()
271 .and_then(|p| self.pointer(p.as_str()))
272 }
273
274 fn reference_mut<T>(&mut self, path: T) -> Option<&mut Self>
275 where
276 T: Into<QueryPath>,
277 {
278 convert_js_path(&path.into())
279 .ok()
280 .and_then(|p| self.pointer_mut(p.as_str()))
281 }
282}
283
284fn convert_js_path(path: &str) -> Parsed<String> {
285 let JpQuery { segments } = parse_json_path(path)?;
286
287 let mut path = String::new();
288 for segment in segments {
289 match segment {
290 Segment::Selector(Selector::Name(name)) => {
291 path.push_str(&format!("/{}", name.trim_matches(|c| c == '\'')));
292 }
293 Segment::Selector(Selector::Index(index)) => {
294 path.push_str(&format!("/{}", index));
295 }
296 s => {
297 return Err(JsonPathError::InvalidJsonPath(format!(
298 "Invalid segment: {:?}",
299 s
300 )));
301 }
302 }
303 }
304 Ok(path)
305}
306
307#[cfg(test)]
308mod tests {
309 use crate::parser::Parsed;
310 use crate::query::queryable::{convert_js_path, Queryable};
311 use crate::query::Queried;
312 use crate::JsonPath;
313 use serde_json::json;
314
315 #[test]
316 fn in_smoke() -> Queried<()> {
317 let json = json!({
318 "elems": ["test", "t1", "t2"],
319 "list": ["test", "test2", "test3"],
320 });
321
322 let res = json.query("$.elems[?in(@, $.list)]")?;
323
324 assert_eq!(res, [&json!("test")]);
325
326 Ok(())
327 }
328 #[test]
329 fn nin_smoke() -> Queried<()> {
330 let json = json!({
331 "elems": ["test", "t1", "t2"],
332 "list": ["test", "test2", "test3"],
333 });
334
335 let res = json.query("$.elems[?nin(@, $.list)]")?;
336
337 assert_eq!(res, [&json!("t1"), &json!("t2")]);
338
339 Ok(())
340 }
341 #[test]
342 fn none_of_smoke() -> Queried<()> {
343 let json = json!({
344 "elems": [ ["t1", "_"], ["t2", "t5"], ["t4"]],
345 "list": ["t1","t2", "t3"],
346 });
347
348 let res = json.query("$.elems[?none_of(@, $.list)]")?;
349
350 assert_eq!(res, [&json!(["t4"])]);
351
352 Ok(())
353 }
354 #[test]
355 fn any_of_smoke() -> Queried<()> {
356 let json = json!({
357 "elems": [ ["t1", "_"], ["t4", "t5"], ["t4"]],
358 "list": ["t1","t2", "t3"],
359 });
360
361 let res = json.query("$.elems[?any_of(@, $.list)]")?;
362
363 assert_eq!(res, [&json!(["t1", "_"])]);
364
365 Ok(())
366 }
367 #[test]
368 fn subset_of_smoke() -> Queried<()> {
369 let json = json!({
370 "elems": [ ["t1", "t2"], ["t4", "t5"], ["t6"]],
371 "list": ["t1","t2", "t3"],
372 });
373
374 let res = json.query("$.elems[?subset_of(@, $.list)]")?;
375
376 assert_eq!(res, [&json!(["t1", "t2"])]);
377
378 Ok(())
379 }
380
381 #[test]
382 fn convert_paths() -> Parsed<()> {
383 let r = convert_js_path("$.a.b[2]")?;
384 assert_eq!(r, "/a/b/2");
385
386 Ok(())
387 }
388
389 #[test]
390 fn test_references() -> Parsed<()> {
391 let mut json = json!({
392 "a": {
393 "b": {
394 "c": 42
395 }
396 }
397 });
398
399 let r = convert_js_path("$.a.b.c")?;
400
401 if let Some(v) = json.pointer_mut(r.as_str()) {
402 *v = json!(43);
403 }
404
405 assert_eq!(
406 json,
407 json!({
408 "a": {
409 "b": {
410 "c": 43
411 }
412 }
413 })
414 );
415
416 Ok(())
417 }
418 #[test]
419 fn test_js_reference() -> Parsed<()> {
420 let mut json = json!({
421 "a": {
422 "b": {
423 "c": 42
424 }
425 }
426 });
427
428 if let Some(path) = json.query_only_path("$.a.b.c")?.first() {
429 if let Some(v) = json.reference_mut(path) {
430 *v = json!(43);
431 }
432
433 assert_eq!(
434 json,
435 json!({
436 "a": {
437 "b": {
438 "c": 43
439 }
440 }
441 })
442 );
443 } else {
444 panic!("no path found");
445 }
446
447 Ok(())
448 }
449}