object_query/
lib.rs

1//! A representation of querying an object by either a key or an index. Normally an type implements
2//! `AccessNext` and `AccessNextMut`. The `json` feature will implement this for the
3//! `serde_json::Value` type
4#![cfg_attr(not(feature = "std"), no_std)]
5
6#[cfg(feature = "std")]
7extern crate std as core;
8
9extern crate alloc;
10
11use alloc::borrow::Cow;
12use alloc::string::{String, ToString};
13use core::iter;
14
15/// Either a key or an index query
16#[derive(Debug, PartialEq, Eq)]
17pub enum Query<'a> {
18    /// The index query. Represents the index (starting at 0) from either the front or the back
19    Index { index: usize, from_last: bool },
20    /// The key query. Represents string key to query by
21    Key(Cow<'a, str>),
22}
23
24/// The result of doing a set operation
25#[derive(Clone, Debug, PartialEq, Eq)]
26pub enum SetResult<T> {
27    /// Could not set term
28    NotSet,
29    /// Term was set and there was no value there before
30    Set,
31    /// Term replaced the value
32    Replaced(T),
33}
34
35impl Query<'static> {
36    /// Create an index query from the front
37    pub fn index(index: usize) -> Self {
38        Query::Index {
39            index,
40            from_last: false,
41        }
42    }
43
44    /// Create an index query from the back
45    pub fn index_from_last(index: usize) -> Self {
46        Query::Index {
47            index,
48            from_last: true,
49        }
50    }
51
52    /// Create a owned key query
53    pub fn key_owned(key: String) -> Self {
54        Query::Key(Cow::Owned(key))
55    }
56}
57
58impl<'a> Query<'a> {
59    /// Create a borrowed key query
60    pub fn key(key: &'a str) -> Self {
61        Query::Key(Cow::Borrowed(key))
62    }
63
64    /// Is an index query from the back
65    pub fn is_from_last(&self) -> bool {
66        match self {
67            Query::Index { from_last, .. } => *from_last,
68            _ => false,
69        }
70    }
71
72    /// Is a key query
73    pub fn is_key(&self) -> bool {
74        match self {
75            Query::Key(_) => true,
76            _ => false,
77        }
78    }
79
80    /// Is an index query
81    pub fn is_index(&self) -> bool {
82        match self {
83            Query::Index { .. } => true,
84            _ => false,
85        }
86    }
87
88    /// Return the string reference if it is a key query
89    pub fn as_key(&self) -> Option<&str> {
90        match self {
91            Query::Key(s) => Some(s.as_ref()),
92            _ => None,
93        }
94    }
95
96    /// Returns an i64 representation of the index if it is a index query. This is either the index
97    /// or `0 - index - 1` for an index from the back
98    pub fn as_index(&self) -> Option<i64> {
99        match self {
100            Query::Index { index, from_last } => Some(if *from_last {
101                -1 - (*index as i64)
102            } else {
103                *index as i64
104            }),
105            _ => None,
106        }
107    }
108
109    /// An alternative to the `std::borrow::ToOwned` method
110    pub fn to_owned(&self) -> Query<'static> {
111        match self {
112            Query::Index { index, from_last } => Query::Index {
113                index: *index,
114                from_last: *from_last,
115            },
116            Query::Key(key) => Query::Key(Cow::Owned(key.to_string())),
117        }
118    }
119}
120
121impl<'a> Clone for Query<'a> {
122    fn clone(&self) -> Query<'static> {
123        self.to_owned()
124    }
125}
126
127/// Describes how to access query
128pub trait AccessNext<T = Self> {
129    fn access_next<'a>(&self, query: &Query<'a>) -> Option<&T>;
130}
131
132/// Describes how to access query on a mutable item
133pub trait AccessNextMut<T = Self> {
134    fn access_next_mut<'a>(&mut self, query: &Query<'a>) -> Option<&mut T>;
135}
136
137/// Describes how to access query on an owned item
138pub trait AccessNextOwned<T = Self>: Sized {
139    fn access_next_owned<'a>(self, query: &Query<'a>) -> Option<T>;
140}
141
142/// An easily implementable trait to acess a list of queries
143pub trait Access: AccessNext + Sized {
144    fn access<'a, I: IntoIterator<Item = &'a Query<'a>>>(&self, queries: I) -> Option<&Self> {
145        queries.into_iter().fold(Some(self), |res, query| {
146            res.and_then(|res| res.access_next(query))
147        })
148    }
149}
150
151/// An easily implementable trait to acess a list of queries on a mutable item
152pub trait AccessMut: AccessNextMut + Sized {
153    fn access_mut<'a, I: IntoIterator<Item = &'a Query<'a>>>(
154        &mut self,
155        queries: I,
156    ) -> Option<&mut Self> {
157        queries.into_iter().fold(Some(self), |res, query| {
158            res.and_then(|res| res.access_next_mut(query))
159        })
160    }
161}
162
163/// An easily implementable trait to acess a list of queries on an owned item
164pub trait AccessOwned: AccessNextOwned {
165    fn access_owned<'a, I: IntoIterator<Item = &'a Query<'a>>>(self, queries: I) -> Option<Self> {
166        queries.into_iter().fold(Some(self), |res, query| {
167            res.and_then(|res| res.access_next_owned(query))
168        })
169    }
170}
171
172/// Describe how to set a value from a query
173pub trait QuerySetItem: Sized {
174    fn query_set_item<'a>(&mut self, query: &Query<'a>, val: Self) -> SetResult<Self>;
175}
176
177struct SkipLastIter<I, T>
178where
179    I: Iterator<Item = T>,
180{
181    iter: iter::Peekable<I>,
182    last: Option<T>,
183}
184
185impl<I, T> SkipLastIter<I, T>
186where
187    I: Iterator<Item = T>,
188{
189    fn new(iter: I) -> Self {
190        Self {
191            iter: iter.peekable(),
192            last: None,
193        }
194    }
195}
196
197impl<I, T> Iterator for SkipLastIter<I, T>
198where
199    I: Iterator<Item = T>,
200{
201    type Item = T;
202    fn next(&mut self) -> Option<Self::Item> {
203        let next = self.iter.next();
204        if next.is_none() {
205            return None;
206        }
207        if self.iter.peek().is_none() {
208            self.last = next;
209            return None;
210        }
211        next
212    }
213}
214
215/// An easily implementable trait to set a value form a list of queries on a mutable item
216pub trait QuerySet: QuerySetItem + AccessMut {
217    fn query_set<'a, I: IntoIterator<Item = &'a Query<'a>>>(
218        &mut self,
219        queries: I,
220        val: Self,
221    ) -> SetResult<Self> {
222        let mut iter = SkipLastIter::new(queries.into_iter());
223        let item = self.access_mut(&mut iter);
224        if let Some(item) = item {
225            if let Some(last) = iter.last {
226                item.query_set_item(last, val)
227            } else {
228                // implies empty query
229                SetResult::NotSet
230            }
231        } else {
232            // implies query not found
233            SetResult::NotSet
234        }
235    }
236}
237
238impl From<usize> for Query<'static> {
239    fn from(index: usize) -> Self {
240        Self::Index {
241            index,
242            from_last: false,
243        }
244    }
245}
246
247impl From<isize> for Query<'static> {
248    fn from(index: isize) -> Self {
249        if index.is_negative() {
250            Self::Index {
251                index: index.abs() as usize - 1,
252                from_last: true,
253            }
254        } else {
255            Self::Index {
256                index: index as usize,
257                from_last: false,
258            }
259        }
260    }
261}
262
263impl From<i8> for Query<'static> {
264    fn from(index: i8) -> Self {
265        (index as isize).into()
266    }
267}
268
269impl From<i16> for Query<'static> {
270    fn from(index: i16) -> Self {
271        (index as isize).into()
272    }
273}
274
275impl From<i32> for Query<'static> {
276    fn from(index: i32) -> Self {
277        (index as isize).into()
278    }
279}
280
281impl From<i64> for Query<'static> {
282    fn from(index: i64) -> Self {
283        (index as isize).into()
284    }
285}
286
287impl From<u8> for Query<'static> {
288    fn from(index: u8) -> Self {
289        (index as usize).into()
290    }
291}
292
293impl From<u16> for Query<'static> {
294    fn from(index: u16) -> Self {
295        (index as usize).into()
296    }
297}
298
299impl From<u32> for Query<'static> {
300    fn from(index: u32) -> Self {
301        (index as usize).into()
302    }
303}
304
305impl From<u64> for Query<'static> {
306    fn from(index: u64) -> Self {
307        (index as usize).into()
308    }
309}
310
311impl From<String> for Query<'static> {
312    fn from(key: String) -> Self {
313        Self::Key(Cow::Owned(key))
314    }
315}
316
317impl<'a> From<&'a str> for Query<'a> {
318    fn from(key: &'a str) -> Self {
319        Self::Key(Cow::Borrowed(key))
320    }
321}
322
323#[cfg(feature = "json")]
324impl AccessNext for serde_json::Value {
325    fn access_next<'a>(&self, query: &Query<'a>) -> Option<&Self> {
326        match self {
327            serde_json::Value::Null => None,
328            serde_json::Value::Bool(_) => None,
329            serde_json::Value::Number(_) => None,
330            serde_json::Value::String(_) => None,
331            serde_json::Value::Array(array) => match query {
332                Query::Index { index, from_last } => {
333                    if *from_last {
334                        if *index >= array.len() {
335                            return None;
336                        }
337                        array.get(array.len() - 1 - index)
338                    } else {
339                        array.get(*index)
340                    }
341                }
342                Query::Key(_) => None,
343            },
344            serde_json::Value::Object(map) => match query {
345                Query::Index { .. } => None,
346                Query::Key(key) => map.get(&key.to_string()),
347            },
348        }
349    }
350}
351
352#[cfg(feature = "json")]
353impl Access for serde_json::Value {}
354
355#[cfg(feature = "json")]
356impl AccessNextMut for serde_json::Value {
357    fn access_next_mut<'a>(&mut self, query: &Query<'a>) -> Option<&mut Self> {
358        match self {
359            serde_json::Value::Null => None,
360            serde_json::Value::Bool(_) => None,
361            serde_json::Value::Number(_) => None,
362            serde_json::Value::String(_) => None,
363            serde_json::Value::Array(array) => match query {
364                Query::Index { index, from_last } => {
365                    if *from_last {
366                        if *index >= array.len() {
367                            return None;
368                        }
369                        let index = array.len() - 1 - index;
370                        array.get_mut(index)
371                    } else {
372                        array.get_mut(*index)
373                    }
374                }
375                Query::Key(_) => None,
376            },
377            serde_json::Value::Object(map) => match query {
378                Query::Index { .. } => None,
379                Query::Key(key) => map.get_mut(&key.to_string()),
380            },
381        }
382    }
383}
384
385#[cfg(feature = "json")]
386impl AccessMut for serde_json::Value {}
387
388#[cfg(feature = "json")]
389impl AccessNextOwned for serde_json::Value {
390    fn access_next_owned<'a>(self, query: &Query<'a>) -> Option<Self> {
391        match self {
392            serde_json::Value::Null => None,
393            serde_json::Value::Bool(_) => None,
394            serde_json::Value::Number(_) => None,
395            serde_json::Value::String(_) => None,
396            serde_json::Value::Array(array) => match query {
397                Query::Index { index, from_last } => {
398                    if *from_last {
399                        if *index >= array.len() {
400                            return None;
401                        }
402                        array.get(array.len() - 1 - index).cloned()
403                    } else {
404                        array.get(*index).cloned()
405                    }
406                }
407                Query::Key(_) => None,
408            },
409            serde_json::Value::Object(map) => match query {
410                Query::Index { .. } => None,
411                Query::Key(key) => map.get(&key.to_string()).cloned(),
412            },
413        }
414    }
415}
416
417#[cfg(feature = "json")]
418impl AccessOwned for serde_json::Value {}
419
420#[cfg(feature = "json")]
421impl QuerySetItem for serde_json::Value {
422    fn query_set_item<'a>(&mut self, query: &Query<'a>, val: Self) -> SetResult<Self> {
423        match self {
424            serde_json::Value::Null => SetResult::NotSet,
425            serde_json::Value::Bool(_) => SetResult::NotSet,
426            serde_json::Value::Number(_) => SetResult::NotSet,
427            serde_json::Value::String(_) => SetResult::NotSet,
428            serde_json::Value::Array(array) => match query {
429                Query::Index { index, from_last } => {
430                    if *from_last {
431                        if *index >= array.len() {
432                            return SetResult::NotSet;
433                        }
434                        let index = array.len() - 1 - index;
435                        array.push(val);
436                        SetResult::Replaced(array.swap_remove(index))
437                    } else {
438                        if *index == array.len() {
439                            array.push(val);
440                            SetResult::Set
441                        } else if *index > array.len() {
442                            array.resize(index + 1, serde_json::Value::Null);
443                            array[*index] = val;
444                            SetResult::Set
445                        } else {
446                            array.push(val);
447                            SetResult::Replaced(array.swap_remove(*index))
448                        }
449                    }
450                }
451                Query::Key(_) => SetResult::NotSet,
452            },
453            serde_json::Value::Object(map) => match query {
454                Query::Index { .. } => SetResult::NotSet,
455                Query::Key(key) => {
456                    if let Some(res) = map.insert(key.to_string(), val) {
457                        SetResult::Replaced(res)
458                    } else {
459                        SetResult::Set
460                    }
461                }
462            },
463        }
464    }
465}
466
467/// Convenience macro for query arguments
468///
469/// ```
470/// # use object_query::{query, Query};
471/// assert_eq!(query!["a", 1, "b"], &[Query::key("a"), Query::index(1), Query::key("b")])
472/// ```
473#[macro_export]
474macro_rules! query {
475    ($($item:expr),*) => {
476        &[$($crate::Query::from(($item))),*]
477    }
478}
479
480#[cfg(feature = "json")]
481impl QuerySet for serde_json::Value {}
482
483#[cfg(test)]
484mod tests {
485    use super::*;
486    #[cfg(feature = "json")]
487    use alloc::vec;
488    #[cfg(feature = "json")]
489    use serde_json::json;
490
491    #[cfg(feature = "json")]
492    #[test]
493    fn access_json_object() {
494        let mut value = json!({"a": 1, "b": 2});
495        let query = vec!["a".into()];
496        assert_eq!(value.access(&query), Some(&json!(1)));
497        assert_eq!(value.access_mut(&query), Some(&mut json!(1)));
498        assert_eq!(value.clone().access_owned(&query), Some(json!(1)));
499        let query = vec!["b".into()];
500        assert_eq!(value.access(&query), Some(&json!(2)));
501        let query = vec!["c".into()];
502        assert_eq!(value.access(&query), None);
503    }
504
505    #[cfg(feature = "json")]
506    #[test]
507    fn access_json_array() {
508        let mut value = json!([1, 2, 3]);
509        let query = vec![0.into()];
510        assert_eq!(value.access(&query), Some(&json!(1)));
511        assert_eq!(value.access_mut(&query), Some(&mut json!(1)));
512        assert_eq!(value.clone().access_owned(&query), Some(json!(1)));
513        let query = vec![1.into()];
514        assert_eq!(value.access(&query), Some(&json!(2)));
515        let query = vec![(-1).into()];
516        assert_eq!(value.access(&query), Some(&json!(3)));
517        let query = vec![(-2).into()];
518        assert_eq!(value.access(&query), Some(&json!(2)));
519        let query = vec![4.into()];
520        assert_eq!(value.access(&query), None);
521    }
522
523    #[cfg(feature = "json")]
524    #[test]
525    fn access_json_nested() {
526        let mut value = json!([{"a": 1}, [2, 3], {"c": 4}]);
527        let query = vec![0.into(), "a".into()];
528        assert_eq!(value.access(&query), Some(&json!(1)));
529        assert_eq!(value.access_mut(&query), Some(&mut json!(1)));
530        assert_eq!(value.clone().access_owned(&query), Some(json!(1)));
531        let query = vec![(-1).into(), "c".into()];
532        assert_eq!(value.access(&query), Some(&json!(4)));
533        let query = vec![1.into(), (-1).into()];
534        assert_eq!(value.access(&query), Some(&json!(3)));
535        let query = vec![0.into(), "b".into()];
536        assert_eq!(value.access(&query), None);
537        let query = vec![1.into(), 2.into()];
538        assert_eq!(value.access(&query), None);
539    }
540
541    #[cfg(feature = "json")]
542    #[test]
543    fn query_set_json_array() {
544        let mut value = json!([1, 2, 3]);
545
546        let query = vec![0.into()];
547        assert_eq!(
548            value.query_set(&query, json!(4)),
549            SetResult::Replaced(json!(1))
550        );
551        assert_eq!(value, json!([4, 2, 3]));
552
553        let query = vec![3.into()];
554        assert_eq!(value.query_set(&query, json!(5)), SetResult::Set);
555        assert_eq!(value, json!([4, 2, 3, 5]));
556
557        let query = vec![5.into()];
558        assert_eq!(value.query_set(&query, json!(6)), SetResult::Set);
559        assert_eq!(value, json!([4, 2, 3, 5, null, 6]));
560
561        let query = vec![(-2).into()];
562        assert_eq!(
563            value.query_set(&query, json!(7)),
564            SetResult::Replaced(json!(null))
565        );
566        assert_eq!(value, json!([4, 2, 3, 5, 7, 6]));
567
568        let query = vec![(-7).into()];
569        assert_eq!(value.query_set(&query, json!(9)), SetResult::NotSet);
570        assert_eq!(value, json!([4, 2, 3, 5, 7, 6]));
571    }
572
573    #[cfg(feature = "json")]
574    #[test]
575    fn query_set_json_object() {
576        let mut value = json!({"a": 1, "b": 2});
577
578        assert_eq!(
579            value.query_set(query!["a"], json!(3)),
580            SetResult::Replaced(json!(1))
581        );
582        assert_eq!(value, json!({"a": 3, "b": 2}));
583
584        assert_eq!(value.query_set(query!["c"], json!(4)), SetResult::Set);
585        assert_eq!(value, json!({"a": 3, "b": 2, "c": 4}));
586    }
587
588    #[cfg(feature = "json")]
589    #[test]
590    fn query_set_json_nested() {
591        let mut value = json!([{"a": 1}, [2, 3], {"c": 4}]);
592
593        assert_eq!(
594            value.query_set(query![0, "a"], json!(2)),
595            SetResult::Replaced(json!(1))
596        );
597        assert_eq!(value, json!([{"a": 2}, [2, 3], {"c": 4}]));
598
599        assert_eq!(value.query_set(query![2, "b"], json!(5)), SetResult::Set);
600        assert_eq!(value, json!([{"a": 2}, [2, 3], {"c": 4, "b": 5}]));
601
602        assert_eq!(value.query_set(query![1, 2], json!(6)), SetResult::Set);
603        assert_eq!(value, json!([{"a": 2}, [2, 3, 6], {"c": 4, "b": 5}]));
604
605        assert_eq!(
606            value.query_set(query![-2, -3], json!(7)),
607            SetResult::Replaced(json!(2))
608        );
609        assert_eq!(value, json!([{"a": 2}, [7, 3, 6], {"c": 4, "b": 5}]));
610
611        assert_eq!(
612            value.query_set(query![3, "key"], json!(8)),
613            SetResult::NotSet
614        );
615        assert_eq!(value, json!([{"a": 2}, [7, 3, 6], {"c": 4, "b": 5}]));
616    }
617}