jsonpath-plus 0.1.9

A JSONPath implementation in Rust, compliant with the Proposal A specification as well as including several extensions such as parent and subpath selectors.
Documentation
use core::hash::{Hash, Hasher};
use std::borrow::Cow;
use std::collections::HashMap;

use crate::idx::{Idx, IdxPath};
use crate::utils::ValueExt;
use serde_json::Value;

pub type ValueMap<'a> = HashMap<RefKey<'a, Value>, &'a Value>;

#[derive(Clone)]
pub struct RefKey<'a, T>(&'a T);

impl<'a, T> PartialEq for RefKey<'a, T> {
    fn eq(&self, other: &Self) -> bool {
        core::ptr::eq(self.0, other.0)
    }
}

impl<'a, T> Eq for RefKey<'a, T> {}

impl<'a, T> Hash for RefKey<'a, T> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        state.write_usize(self.0 as *const T as usize);
    }
}

pub struct EvalCtx<'a, 'b> {
    root: &'a Value,
    cur_matched: Vec<&'a Value>,
    parents: Cow<'b, ValueMap<'a>>,
}

impl<'a, 'b> EvalCtx<'a, 'b> {
    pub fn new(root: &'a Value) -> EvalCtx<'a, 'b> {
        EvalCtx {
            root,
            cur_matched: vec![root],
            parents: Cow::Owned(HashMap::new()),
        }
    }

    pub fn new_parents<'c>(root: &'a Value, parents: &'c ValueMap<'a>) -> EvalCtx<'a, 'c> {
        EvalCtx {
            root,
            cur_matched: vec![root],
            parents: Cow::Borrowed(parents),
        }
    }

    fn parents_recur(parents: &mut HashMap<RefKey<'a, Value>, &'a Value>, parent: &'a Value) {
        parent.iter().for_each(|child| {
            parents.insert(RefKey(child), parent);
            EvalCtx::parents_recur(parents, child)
        })
    }

    pub fn prepopulate_parents(&mut self) {
        Self::parents_recur(self.parents.to_mut(), self.root);
    }

    pub fn root(&self) -> &'a Value {
        self.root
    }

    pub fn all_parents(&self) -> &HashMap<RefKey<'a, Value>, &'a Value> {
        &*self.parents
    }

    pub fn idx_of(&self, val: &'a Value) -> Option<Idx> {
        let parent = self.parent_of(val)?;
        match parent {
            Value::Array(v) => v.iter().enumerate().find_map(|(idx, p)| {
                if core::ptr::eq(p, val) {
                    Some(Idx::Array(idx))
                } else {
                    None
                }
            }),
            Value::Object(m) => m.iter().find_map(|(idx, p)| {
                if core::ptr::eq(p, val) {
                    Some(Idx::Object(idx.to_string()))
                } else {
                    None
                }
            }),
            _ => None,
        }
    }

    pub fn parent_of(&self, val: &'a Value) -> Option<&'a Value> {
        self.all_parents().get(&RefKey(val)).copied()
    }

    pub fn get_matched(&self) -> &[&'a Value] {
        &self.cur_matched
    }

    #[inline]
    pub fn set_matched(&mut self, matched: Vec<&'a Value>) {
        self.cur_matched = matched;
    }

    #[inline]
    pub fn apply_matched_ref<'c, T>(
        &'c self,
        f: impl Fn(&'c Self, &'a Value) -> T,
    ) -> Vec<&'a Value>
    where
        T: IntoIterator<Item = &'a Value>,
    {
        self.cur_matched.iter().flat_map(|&i| f(self, i)).collect()
    }

    #[inline]
    pub fn apply_matched<T>(&mut self, f: impl Fn(&Self, &'a Value) -> T)
    where
        T: IntoIterator<Item = &'a Value>,
    {
        self.cur_matched = self.apply_matched_ref(f);
    }

    pub fn paths_matched(&self) -> Vec<IdxPath> {
        self.cur_matched
            .iter()
            .copied()
            .map(|a| {
                let mut cur = a;
                let mut out = Vec::new();
                while let Some(p) = self.parent_of(cur) {
                    out.push(self.idx_of(cur).unwrap());
                    cur = p;
                }
                out.reverse();
                IdxPath::new(out)
            })
            .collect()
    }

    pub fn into_matched(self) -> Vec<&'a Value> {
        self.cur_matched
    }
}