valq/
lib.rs

1//! `valq` provides macros for querying semi-structured ("JSON-ish") data **with the JavaScript-like syntax**.
2//!
3//! The principal macro provided by this crate is `query_value!`. Read [the `query_value` doc] for detailed usage.
4//! There is also a `Result`-returning variant of `query_value!`, called [`query_value_result!`].
5//!
6//! [the `query_value` doc]: crate::query_value
7//! [`query_value_result!`]: crate::query_value_result
8//!
9//! ## Example
10//!
11//! ```rust
12//! use serde::Deserialize;
13//! use serde_json::{json, Value};
14//! use valq::{query_value, query_value_result};
15//!
16//! let data = json!({
17//!     "package": {
18//!         "name": "valq",
19//!         "authors": ["jiftechnify"],
20//!         "keywords": ["macro", "query", "json"]
21//!     },
22//!     "dependencies": {
23//!         "paste": {
24//!             "version": "1.0.15"
25//!         }
26//!     },
27//!     "dev-dependencies": {
28//!         "serde": {
29//!             "version": "1.0.228",
30//!             "features": ["derive"]
31//!         }
32//!     }
33//! });
34//!
35//! // Simple query
36//! assert_eq!(
37//!     query_value!(data.package.name -> str).unwrap(),
38//!     "valq"
39//! );
40//!
41//! // Combining dot-notations & bracket-notations
42//! assert_eq!(
43//!     query_value!(data.package.authors[0] -> str).unwrap(),
44//!     "jiftechnify"
45//! );
46//!
47//! // Deserializing a JSON array into a Vec
48//! // Make sure that you put the line: `use serde::Deserialize`!
49//! assert_eq!(
50//!     query_value!(data.package.keywords >> (Vec<String>)).unwrap(),
51//!     ["macro", "query", "json"],
52//! );
53//!
54//! // Result-returning variant for useful error
55//! let res: valq::Result<&str> = query_value_result!(data.package.readme -> str);
56//! if let Err(valq::Error::ValueNotFoundAtPath(path)) = res {
57//!     assert_eq!(path, ".package.readme");
58//! } else {
59//!     panic!("should be error");
60//! }
61//!
62//! // Unwrapping with default value
63//! assert_eq!(
64//!     query_value!(data.package.readme -> str ?? "README.md"),
65//!     "README.md",
66//! );
67//!
68//! // "Dynamic" query with bracket-notation
69//! let dep_name = "paste";
70//! assert_eq!(
71//!     query_value!(data.dependencies[dep_name].version -> str).unwrap(),
72//!     "1.0.15",
73//! );
74//!
75//! // Put it all together!
76//! assert_eq!(
77//!     query_value!(data["dev-dependencies"].serde.features[0] >> String ?? "none".into()),
78//!     "derive".to_string(),
79//! );
80//! ```
81
82mod error;
83pub use error::{Error, Result};
84
85mod util;
86
87#[doc(hidden)]
88pub use paste::paste as __paste;
89
90macro_rules! doc_query_value {
91    ($query_value:item) => {
92        /// A macro for querying an inner value of a structured ("JSON-ish") data.
93        ///
94        /// ```
95        /// use valq::query_value;
96        /// # use serde_json::{json, Value};
97        /// #
98        /// # let obj = json!({"foo":{"bar":"bar!"},"arr":[1,2,3],"path":{"to":{"matrix":[[{},{"abyss":"I gaze into you."}],[{},{}]]}}});
99        /// # let arr = json!([1,2,3]);
100        ///
101        /// // let obj = json!({ ... });
102        /// // let arr = json!([ ... ]);
103        ///
104        /// // get the field `foo` from the JSON-ish object `obj`
105        /// let foo: Option<&Value> = query_value!(obj.foo);
106        ///
107        /// // get the first item of the nested JSON array `arr` in `obj`
108        /// let head = query_value!(obj.arr[0]);
109        ///
110        /// // more complex query, just works!
111        /// let abyss = query_value!(obj.path.to.matrix[0][1].abyss);
112        ///
113        /// ```
114        ///
115        /// ## Query Notations
116        ///
117        /// You can traverse through semi-structured ("JSON-ish") data by chaining JavaScript-like accessor notaions:
118        ///
119        /// - **Dot notation** (`.field`): Access a property of an "object" (key-value structure) by name
120        /// - **Bracket notation** (`["field"]`): Access a property of an "object", or an element of an "array"-like value by index
121        ///     - With string index, you get access to object properties, similar to dot notation.
122        ///       This is especially useful for keys that are not valid Rust identifiers (e.g. `"1st"`, `"foo-bar"`).
123        ///     - With integer index, you get access to array elements.
124        ///     - Dynamic query: you can place a Rust expression that evaluate to string or integer in the brackets.
125        ///
126        /// ## Query Result
127        ///
128        /// `query_value!` returns an `Option` as the result of the query (except when unwrapping operator `??` is used; see below for details).
129        ///
130        /// Queries can fail for the following reasons. In that case, `query_value!` returns `None`:
131        ///
132        /// - The specified key or index does not exist in the target value
133        /// - Indexing an object (key-value structure) with an integer
134        /// - Indexing an array with a string key
135        ///
136        /// Otherwise, i.e. if your query succeeds, `query_value!` returns the queried value wrapped in `Some`.
137        ///
138        /// With basic queries, `query_value!` extracts a shared reference (`&`) to the inner value by default. Think of it as a function that has following signature:
139        ///
140        /// ```txt
141        /// query_value!(query...) -> Option(&Value)
142        /// ```
143        ///
144        /// ## `mut`: Extracting Mutable Reference to Inner Value
145        ///
146        /// Queries start with `mut` extract the mutable reference (`&mut`) to the inner value instead:
147        ///
148        /// ```txt
149        /// query_value!(mut query...) -> Option(&mut Value)
150        /// ```
151        ///
152        /// Example:
153        ///
154        /// ```
155        /// use serde_json::{json, Value};
156        /// use valq::query_value;
157        ///
158        /// let mut obj = json!({"foo": { "bar": { "x": 1, "y": 2 }}});
159        /// {
160        ///     let bar: &mut Value = query_value!(mut obj.foo.bar).unwrap();
161        ///     *bar = json!({"x": 100, "y": 200});
162        /// }
163        /// // see below for `->` syntax
164        /// assert_eq!(query_value!(obj.foo.bar.x -> u64), Some(100));
165        /// assert_eq!(query_value!(obj.foo.bar.y -> u64), Some(200));
166        /// ```
167        ///
168        /// ## `->`: Cast Value with `as_***()`
169        ///
170        /// Queries end with `-> ***` try to cast the extracted value with `as_***()` method.
171        /// In the `mut` context, `as_***_mut()` method is used instead.
172        ///
173        /// ```txt
174        /// // assuming your value has the method `as_str(&self) -> Option(&str)`
175        /// query_value!(query... -> str) -> Option(&str)
176        ///
177        /// // assuming your value has the method `as_array_mut(&mut self) -> Option(&mut Vec<Value>)`
178        /// query_value!(mut query... -> array) -> Option(&mut Vec<Value>)
179        /// ```
180        ///
181        /// ```
182        /// use serde_json::{json, Value};
183        /// use valq::query_value;
184        ///
185        /// let mut obj = json!({"foo": "hello", "arr": [1, 2]});
186        ///
187        /// // try to cast extracted value with `as_u64` method on that value
188        /// // results in `None` in case of type mismatch
189        /// let foo_str: Option<&str> = query_value!(obj.foo -> str);
190        /// assert_eq!(foo_str, Some("hello"));
191        ///
192        /// // `mut` example
193        /// let arr_vec: Option<&mut Vec<Value>> = query_value!(mut obj.arr -> array);
194        /// assert_eq!(arr_vec, Some(&mut vec![json!(1), json!(2)]));
195        /// ```
196        ///
197        /// ## `>>`: Deserializing Value into Any Types Implement `serde::Deserialize` trait
198        ///
199        /// Queries end with `>> (Type)` try to deserialize the extracted value using `deserialize()` method on the `Type`;
200        /// i.e. you can get a value of your `Type` out of the queried value, assuming the `Type` implements `serde::Deserialize`.
201        ///
202        /// ```txt
203        /// // assuming `Type` implements `serde::Deserialize`
204        /// query_value!(query... >> (Type)) -> Option(Type)
205        /// ```
206        ///
207        /// ```
208        /// use serde::Deserialize;
209        /// use serde_json::json;
210        /// use valq::query_value;
211        ///
212        /// #[derive(Debug, PartialEq, Deserialize)]
213        /// struct Person {
214        ///     name: String,
215        ///     age: u8,
216        /// }
217        ///
218        /// let j = json!({"author": {"name": "jiftechnify", "age": 31}});
219        /// assert_eq!(
220        ///     query_value!(j.author >> (Person)),
221        ///     Some(Person {
222        ///         name: "jiftechnify".into(),
223        ///         age: 31u8,
224        ///     }),
225        /// );
226        /// ```
227        ///
228        /// A few notes on the `>>` operator:
229        ///
230        /// - Basically, the type name after `>>` must be wrapped with parentheses. As a special case, you can omit that parens only if your type name consists of *a single identifier*, for simplicity.
231        ///     + For example, the query above can be simplified to `j.author >> Person`.
232        /// - Deserialization with `>>` involves cloning of the queried value. You may want to use `->` type casting if possible.
233        ///
234        /// ## `??`: Unwarp Query Result with Default Value
235        ///
236        /// You put `?? ...` at the very end of the query to unwrap the query result with providing a default value in case of query failure.
237        ///
238        /// - `?? <expr>`: Use the value of`<expr>` as the default.
239        /// - `?? default`: Use `Default::default()` as the default.
240        ///
241        /// This is especilly useful together with `->` or `>>` conversions:
242        ///
243        /// ```
244        /// use serde_json::{json, Value};
245        /// use valq::query_value;
246        ///
247        /// let obj = json!({"foo": {"bar": "not a number"}});
248        /// assert_eq!(query_value!(obj.foo.bar -> str ?? "failed!"), "not a number");
249        /// assert_eq!(query_value!(obj.foo.bar -> u64 ?? 42), 42);
250        /// assert_eq!(query_value!(obj.foo.bar -> u64 ?? default), 0u64); // u64::default()
251        /// ```
252        ///
253        /// ## Query Syntax Specification
254        ///
255        /// ```txt
256        /// query_value!(
257        ///     ("mut")?
258        ///     <value> ("." <key> | "[" <idx> "]")*
259        ///     ("->" <as_dest> | ">>" "(" <deser_dest> ")")?
260        ///     ("??" ("default" | <default_expr>))?
261        /// )
262        /// ```
263        ///
264        /// where:
265        ///
266        /// - `<value>`: An expression evaluates to a structured data to be queried
267        /// - `<key>`: A property/field key to extract value from a key-value structure
268        /// - `<idx>`: An index to extract value from structure
269        ///     + For an array-like structure, any expressions evaluates to an integer can be used
270        ///     + For a key-value structure, any expressions evaluates to a string can be used
271        /// - `<as_dest>`: A destination type of type casting with `as_***()` / `as_***_mut()` methods
272        /// - `<deser_dest>`: A type name into which the queried value is deserialized
273        ///     + The specified type *MUST* implement the `serde::Deserialize` trait
274        ///     + If the type name contains only a single identifier, you can omit parentheses around it
275        /// - `<default_expr>`: An expression for a default value in case of query failure
276        ///     + Instead, you can put `default` keyword in this place to use `Default::default()` as the default value
277        ///
278        /// ## Compatibility
279        ///
280        /// `query_value!` can be used with arbitrary data structure(to call, `Value`) that supports `get(&self, idx) -> Option<&Value>` method that retrieves a value at `idx`.
281        ///
282        /// Extracting mutable reference is also supported if your `Value` supports `get_mut(&mut self, idx) -> Option<&Value>`.
283        ///
284        /// Instances of compatible data structures:
285        ///
286        /// - [`serde_json::Value`](https://docs.rs/serde_json/latest/serde_json/enum.Value.html)
287        /// - [`serde_yaml::Value`](https://docs.rs/serde_yaml/latest/serde_yaml/enum.Value.html)
288        /// - [`toml::Value`](https://docs.rs/toml/latest/toml/value/enum.Value.html)
289        /// - and more...
290        #[macro_export]
291        $query_value
292    };
293}
294
295// fake implementation illustrates the macro syntax for docs
296#[cfg(doc)]
297doc_query_value! {macro_rules! query_value {
298    ($(mut)? $value:tt $(query:tt)* $(?? $default:expr)?) => {};
299    ($(mut)? $value:tt $(query:tt)* -> $as:ident $(?? $default:expr)?) => {};
300    ($(mut)? $value:tt $(query:tt)* >> ($deser_to:ty) $(?? $default:expr)?) => {};
301}}
302
303// actual implementation
304#[cfg(not(doc))]
305doc_query_value! {macro_rules! query_value {
306    /* non-mut traversal */
307    // traversal step
308    (@trv { $vopt:expr } . $key:ident $($rest:tt)*) => {
309        $crate::query_value!(@trv { $vopt.and_then(|v| v.get(stringify!($key))) } $($rest)*)
310    };
311    (@trv { $vopt:expr } [ $idx:expr ] $($rest:tt)*) => {
312        $crate::query_value!(@trv { $vopt.and_then(|v| v.get($idx)) } $($rest)*)
313    };
314    // conversion step -> convert then jump to finalization step
315    (@trv { $vopt:expr } -> $dest:ident $($rest:tt)*) => {
316        $crate::__paste! {
317            $crate::query_value!(@fin { $vopt.and_then(|v| v.[<as_ $dest>]()) } $($rest)*)
318        }
319    };
320    (@trv { $vopt:expr } >> $dest:ident $($rest:tt)*) => {
321        $crate::query_value!(@fin { $vopt.and_then(|v| <$dest>::deserialize(v.clone()).ok()) } $($rest)*)
322    };
323    (@trv { $vopt:expr } >> ($dest:ty) $($rest:tt)*) => {
324        $crate::query_value!(@fin { $vopt.and_then(|v| <$dest>::deserialize(v.clone()).ok()) } $($rest)*)
325    };
326    // no conversion -> just jump to finalization step
327    (@trv { $vopt:expr } $($rest:tt)*) => {
328        $crate::query_value!(@fin { $vopt } $($rest)*)
329    };
330
331    /* mut traversal */
332    // traversal step
333    (@trv_mut { $vopt:expr } . $key:ident $($rest:tt)*) => {
334        $crate::query_value!(@trv_mut { $vopt.and_then(|v| v.get_mut(stringify!($key))) } $($rest)*)
335    };
336    (@trv_mut { $vopt:expr } [ $idx:expr ] $($rest:tt)*) => {
337        $crate::query_value!(@trv_mut { $vopt.and_then(|v| v.get_mut($idx)) } $($rest)*)
338    };
339    // conversion step -> convert then jump to finalization step
340    (@trv_mut { $vopt:expr } -> $dest:ident $($rest:tt)*) => {
341        $crate::__paste! {
342            $crate::query_value!(@fin { $vopt.and_then(|v| v.[<as_ $dest _mut>]()) } $($rest)*)
343        }
344    };
345    (@trv_mut { $vopt:expr } >> $dest:ident $($rest:tt)*) => {
346        $crate::query_value!(@fin { $vopt.and_then(|v| <$dest>::deserialize(v.clone()).ok()) } $($rest)*)
347    };
348    (@trv_mut { $vopt:expr } >> ($dest:ty) $($rest:tt)*) => {
349        $crate::query_value!(@fin { $vopt.and_then(|v| <$dest>::deserialize(v.clone()).ok()) } $($rest)*)
350    };
351    // no conversion -> just jump to finalization step
352    (@trv_mut { $vopt:expr } $($rest:tt)*) => {
353        $crate::query_value!(@fin { $vopt } $($rest)*)
354    };
355
356    /* finalize: handle unwrapping operator */
357    (@fin { $vopt:expr } ?? default) => {
358        $vopt.unwrap_or_default()
359    };
360    (@fin { $vopt:expr } ?? $default:expr) => {
361        $vopt.unwrap_or_else(|| $default)
362    };
363    // no unwrapping operator
364    (@fin { $vopt:expr }) => {
365        $vopt
366    };
367    // unreachable branch -> report syntax error
368    (@fin $($_:tt)*) => {
369        compile_error!("invalid query syntax for query_value!()")
370    };
371
372    /* entry points */
373    (mut $v:tt $($rest:tt)*) => {
374        $crate::query_value!(@trv_mut { Some(&mut $v) } $($rest)*)
375    };
376    ($v:tt $($rest:tt)*) => {
377        $crate::query_value!(@trv { Some(&$v) } $($rest)*)
378    };
379}}
380
381macro_rules! doc_query_value_result {
382    ($query_value_result:item) => {
383        /// A `Result`-returning variant of [`query_value!`].
384        ///
385        /// See the documentation of [`query_value!`] macro for detailed usage.
386        ///
387        /// If your query fails, this macro returns a [`valq::Error`] describing the failure reason.
388        ///
389        /// ```
390        /// use serde::Deserialize;
391        /// use serde_json::json;
392        /// use valq::{query_value_result, Error};
393        ///
394        /// let obj = json!({"foo": {"bar": 42}});
395        ///
396        /// // Error::ValueNotFoundAtPath: querying non-existent path
397        /// let result = query_value_result!(obj.foo.baz);
398        /// assert!(matches!(result, Err(Error::ValueNotFoundAtPath(_))));
399        ///
400        /// // Error::AsCastFailed: type casting failure
401        /// let result = query_value_result!(obj.foo.bar -> str);
402        /// assert!(matches!(result, Err(Error::AsCastFailed(_))));
403        ///
404        /// // Error::DeserializationFailed: deserialization failure
405        /// let result = query_value_result!(obj.foo >> (Vec<u8>));
406        /// assert!(matches!(result, Err(Error::DeserializationFailed(_))));
407        /// ```
408        ///
409        /// [`query_value!`]: crate::query_value
410        /// [`valq::Error`]: crate::Error
411        #[macro_export]
412        $query_value_result
413    };
414}
415
416// fake implementation illustrates the macro syntax for docs
417#[cfg(doc)]
418doc_query_value_result! {macro_rules! query_value_result {
419    ($(mut)? $value:tt $(query:tt)* $(?? $default:expr)?) => {};
420    ($(mut)? $value:tt $(query:tt)* -> $as:ident $(?? $default:expr)?) => {};
421    ($(mut)? $value:tt $(query:tt)* >> ($deser_to:ty) $(?? $default:expr)?) => {};
422}}
423
424// actual implementation
425#[cfg(not(doc))]
426doc_query_value_result! {macro_rules! query_value_result {
427    /* non-mut traversal */
428    // traversal step
429    (@trv [$trace:ident] { $vopt:expr } . $key:ident $($rest:tt)*) => {
430        $crate::query_value_result!(@trv [$trace] {
431            $vopt.and_then(|v| {
432                $trace.push_str(stringify!(.$key));
433                v.get(stringify!($key)).ok_or_else(|| $crate::Error::ValueNotFoundAtPath($trace.clone()))
434            })
435        } $($rest)*)
436    };
437    (@trv [$trace:ident] { $vopt:expr } [ $idx:expr ] $($rest:tt)*) => {
438        $crate::query_value_result!(@trv [$trace] {
439            $vopt.and_then(|v| {
440                $trace.push_str(format!("[{}]", stringify!($idx)).as_str());
441                v.get($idx).ok_or_else(|| $crate::Error::ValueNotFoundAtPath($trace.clone()))
442            })
443        } $($rest)*)
444    };
445    // conversion step -> convert then jump to finalization step
446    (@trv [$trace:ident] { $vopt:expr } -> $dest:ident $($rest:tt)*) => {
447        $crate::__paste! {
448            $crate::query_value_result!(@fin [$trace] {
449                $vopt.and_then(|v| {
450                    let conv_name = format!("as_{}", stringify!($dest));
451                    v.[<as_ $dest>]() .ok_or_else(|| $crate::Error::AsCastFailed(conv_name))
452                })
453            } $($rest)*)
454        }
455    };
456    (@trv [$trace:ident] { $vopt:expr } >> $dest:ident $($rest:tt)*) => {
457        $crate::query_value_result!(@fin [$trace] {
458            $vopt.and_then(|v| {
459                <$dest>::deserialize(v.clone()).map_err(|e| $crate::Error::DeserializationFailed(Box::new(e)))
460            })
461        } $($rest)*)
462    };
463    (@trv [$trace:ident] { $vopt:expr } >> ($dest:ty) $($rest:tt)*) => {
464        $crate::query_value_result!(@fin [$trace] {
465            $vopt.and_then(|v| {
466                <$dest>::deserialize(v.clone()).map_err(|e| $crate::Error::DeserializationFailed(Box::new(e)))
467            })
468        } $($rest)*)
469    };
470    // no conversion -> just jump to finalization step
471    (@trv [$trace:ident] { $vopt:expr } $($rest:tt)*) => {
472        $crate::query_value_result!(@fin [$trace] { $vopt } $($rest)*)
473    };
474
475    /* mut traversal */
476    // traversal step
477    (@trv_mut [$trace:ident] { $vopt:expr } . $key:ident $($rest:tt)*) => {
478        $crate::query_value_result!(@trv_mut [$trace] {
479            $vopt.and_then(|v| {
480                $trace.push_str(stringify!(.$key));
481                v.get_mut(stringify!($key)).ok_or_else(|| $crate::Error::ValueNotFoundAtPath($trace.clone()))
482            })
483        } $($rest)*)
484    };
485    (@trv_mut [$trace:ident] { $vopt:expr } [ $idx:expr ] $($rest:tt)*) => {
486        $crate::query_value_result!(@trv_mut [$trace] {
487            $vopt.and_then(|v| {
488                $trace.push_str(format!("[{}]", stringify!($idx)).as_str());
489                v.get_mut($idx).ok_or_else(|| $crate::Error::ValueNotFoundAtPath($trace.clone()))
490            })
491        } $($rest)*)
492    };
493    // conversion step -> convert then jump to finalization step
494    (@trv_mut [$trace:ident] { $vopt:expr } -> $dest:ident $($rest:tt)*) => {
495        $crate::__paste! {
496            $crate::query_value_result!(@fin [$trace] {
497                $vopt.and_then(|v| {
498                    let conv_name = format!("as_{}_mut", stringify!($dest));
499                    v.[<as_ $dest _mut>]().ok_or_else(|| $crate::Error::AsCastFailed(conv_name))
500                })
501            } $($rest)*)
502        }
503    };
504    (@trv_mut [$trace:ident] { $vopt:expr } >> $dest:ident $($rest:tt)*) => {
505        $crate::query_value_result!(@fin [$trace] {
506            $vopt.and_then(|v| {
507                <$dest>::deserialize(v.clone()).map_err(|e| $crate::Error::DeserializationFailed(Box::new(e)))
508            })
509        } $($rest)*)
510    };
511    (@trv_mut [$trace:ident] { $vopt:expr } >> ($dest:ty) $($rest:tt)*) => {
512        $crate::query_value_result!(@fin [$trace] {
513            $vopt.and_then(|v| {
514                <$dest>::deserialize(v.clone()).map_err(|e| $crate::Error::DeserializationFailed(Box::new(e)))
515            })
516        } $($rest)*)
517    };
518    // no conversion -> just jump to finalization step
519    (@trv_mut [$trace:ident] { $vopt:expr } $($rest:tt)*) => {
520        $crate::query_value_result!(@fin [$trace] { $vopt } $($rest)*)
521    };
522
523    /* finalize: handle unwrapping operator */
524    (@fin [$trace:ident] { $vopt:expr } ?? default) => {
525        {
526            use $crate::Error;
527            let mut $trace = String::new();
528            $vopt.unwrap_or_default()
529        }
530    };
531    (@fin [$trace:ident] { $vopt:expr } ?? $default:expr) => {
532        {
533            use $crate::Error;
534            let mut $trace = String::new();
535            $vopt.unwrap_or_else(|_| $default)
536        }
537    };
538    // no unwrapping operator
539    (@fin [$trace:ident] { $vopt:expr }) => {
540        {
541            use $crate::Error;
542            let mut $trace = String::new();
543            $vopt
544        }
545    };
546    // unreachable branch -> report syntax error
547    (@fin $($_:tt)*) => {
548        compile_error!("invalid query syntax for query_value_result!()")
549    };
550
551    /* entry points */
552    (mut $v:tt $($rest:tt)*) => {
553        $crate::query_value_result!(@trv_mut [trace] { Ok::<_, $crate::Error>(&mut $v) } $($rest)*)
554    };
555    ($v:tt $($rest:tt)*) => {
556        $crate::query_value_result!(@trv [trace] { Ok::<_, $crate::Error>(&$v) } $($rest)*)
557    };
558}}