1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
//! Value access and iteration.
use alloc::vec::Vec;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// A path such as `.[].a?[1:]`.
pub type Path<T> = Vec<(Part<crate::Spanned<T>>, Opt)>;
/// A part of a path, such as `[]`, `a`, and `[1:]` in `.[].a?[1:]`.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug)]
pub enum Part<I> {
/// Access arrays with integer and objects with string indices
Index(I),
/// Iterate over arrays with optional range bounds and over objects without bounds
Range(Option<I>, Option<I>),
}
/// Optionality of a path part, i.e. whether `?` is present.
///
/// For example, `[] | .a` fails with an error, while `[] | .a?` returns nothing.
/// By default, path parts are *essential*, meaning that they fail.
/// Annotating them with `?` makes them *optional*.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, Debug)]
pub enum Opt {
/// Return nothing if the input cannot be accessed with the path
Optional,
/// Fail if the input cannot be accessed with the path
Essential,
}
impl<I> Part<I> {
/// Apply a function to the contained indices.
pub fn map<J>(self, mut f: impl FnMut(I) -> J) -> Part<J> {
match self {
Self::Index(i) => Part::Index(f(i)),
Self::Range(l, h) => Part::Range(l.map(&mut f), h.map(f)),
}
}
}
impl Opt {
/// If `self` is optional, return `x`, else fail with `f(x)`.
pub fn fail<T, E>(self, x: T, f: impl FnOnce(T) -> E) -> Result<T, E> {
match self {
Self::Optional => Ok(x),
Self::Essential => Err(f(x)),
}
}
/// If `self` is optional, return all items of the iterator that are `Ok` and succeed,
/// else return all items of the iterator and fail if any is `Err`.
pub fn collect<T, E>(self, iter: impl Iterator<Item = Result<T, E>>) -> Result<Vec<T>, E> {
match self {
Self::Optional => Ok(iter.filter_map(|x| x.ok()).collect()),
Self::Essential => iter.collect(),
}
}
}