#![forbid(unsafe_code)]
#![warn(
missing_docs,
elided_lifetimes_in_paths,
explicit_outlives_requirements,
missing_abi,
noop_method_call,
pointer_structural_match,
semicolon_in_expressions_from_macros,
unused_import_braces,
unused_lifetimes,
clippy::cargo,
clippy::missing_panics_doc,
clippy::doc_markdown,
clippy::ptr_as_ptr,
clippy::cloned_instead_of_copied,
clippy::unreadable_literal,
clippy::must_use_candidate
)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
use serde_json::Value;
use ast::Span;
use error::{ParseError, ParseOrJsonError};
use eval::EvalCtx;
use idx::{Idx, IdxPath};
use utils::{delete_paths, replace_paths, try_replace_paths};
pub mod ast;
pub mod error;
mod eval;
pub mod idx;
mod utils;
#[doc(inline)]
pub use ast::Path as JsonPath;
pub fn find<'a>(pattern: &str, value: &'a Value) -> Result<Vec<&'a Value>, ParseError> {
Ok(JsonPath::compile(pattern)?.find(value))
}
pub fn find_str(pattern: &str, value: &str) -> Result<Vec<Value>, ParseOrJsonError> {
Ok(JsonPath::compile(pattern)?.find_str(value)?)
}
impl JsonPath {
pub fn compile(pattern: &str) -> Result<JsonPath, ParseError> {
use chumsky::{Parser, Stream};
let len = pattern.chars().count();
let stream = Stream::from_iter(
Span::from(len..len),
Box::new(
pattern
.chars()
.enumerate()
.map(|(i, c)| (c, Span::from(i..i + 1))),
),
);
Self::parser()
.parse(stream)
.map_err(|e| ParseError::new(pattern, e))
}
#[must_use = "this does not modify the path or provided value"]
pub fn find<'a>(&self, value: &'a Value) -> Vec<&'a Value> {
let mut ctx = EvalCtx::new(value);
if self.has_parent() {
ctx.prepopulate_parents();
}
self.eval(&mut ctx);
ctx.into_matched()
}
#[must_use = "this does not modify the path or provided value"]
pub fn find_paths(&self, value: &Value) -> Vec<IdxPath> {
let mut ctx = EvalCtx::new(value);
ctx.prepopulate_parents();
self.eval(&mut ctx);
ctx.paths_matched()
}
#[must_use = "this returns the new value, without modifying the original. To work in-place, \
use `delete_on`"]
pub fn delete(&self, value: &Value) -> Value {
let paths = self.find_paths(value);
let mut out = value.clone();
delete_paths(paths, &mut out);
out
}
pub fn delete_on(&self, value: &mut Value) {
let paths = self.find_paths(value);
delete_paths(paths, value);
}
#[must_use = "this returns the new value, without modifying the original. To work in-place, \
use `replace_on`"]
pub fn replace(&self, value: &Value, f: impl FnMut(&Value) -> Value) -> Value {
let paths = self.find_paths(value);
let mut out = value.clone();
replace_paths(paths, &mut out, f);
out
}
pub fn replace_on(&self, value: &mut Value, f: impl FnMut(&Value) -> Value) {
let paths = self.find_paths(value);
replace_paths(paths, value, f);
}
#[must_use = "this returns the new value, without modifying the original. To work in-place, \
use `try_replace_on`"]
pub fn try_replace(&self, value: &Value, f: impl FnMut(&Value) -> Option<Value>) -> Value {
let paths = self.find_paths(value);
let mut out = value.clone();
try_replace_paths(paths, &mut out, f);
out
}
pub fn try_replace_on(&self, value: &mut Value, f: impl FnMut(&Value) -> Option<Value>) {
let paths = self.find_paths(value);
try_replace_paths(paths, value, f);
}
pub fn find_str(&self, str: &str) -> Result<Vec<Value>, serde_json::Error> {
let val = serde_json::from_str(str)?;
Ok(self.find(&val).into_iter().cloned().collect())
}
pub fn delete_str(&self, str: &str) -> Result<Value, serde_json::Error> {
let val = serde_json::from_str(str)?;
Ok(self.delete(&val))
}
pub fn replace_str(
&self,
str: &str,
f: impl FnMut(&Value) -> Value,
) -> Result<Value, serde_json::Error> {
let val = serde_json::from_str(str)?;
Ok(self.replace(&val, f))
}
pub fn try_replace_str(
&self,
str: &str,
f: impl FnMut(&Value) -> Option<Value>,
) -> Result<Value, serde_json::Error> {
let val = serde_json::from_str(str)?;
Ok(self.try_replace(&val, f))
}
}
#[cfg(test)]
mod tests;