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}}