mod atom;
mod comparable;
mod comparison;
mod filter;
mod jp_query;
pub mod queryable;
mod segment;
mod selector;
pub mod state;
mod test;
mod test_function;
use crate::parser::errors::JsonPathError;
use crate::parser::model::JpQuery;
use crate::parser::{parse_json_path, Parsed};
use crate::query::queryable::Queryable;
use crate::query::state::{Data, Pointer};
use state::State;
use std::borrow::Cow;
pub type QueryPath = String;
pub type Queried<T> = Result<T, JsonPathError>;
pub trait Query {
fn process<'a, T: Queryable>(&self, state: State<'a, T>) -> State<'a, T>;
}
#[derive(Debug, Clone, PartialEq)]
pub struct QueryRef<'a, T: Queryable>(&'a T, QueryPath);
impl<'a, T: Queryable> From<(&'a T, QueryPath)> for QueryRef<'a, T> {
fn from((inner, path): (&'a T, QueryPath)) -> Self {
QueryRef(inner, path)
}
}
impl<'a, T: Queryable> From<(&'a T, &str)> for QueryRef<'a, T> {
fn from((inner, path): (&'a T, &str)) -> Self {
QueryRef(inner, path.to_string())
}
}
impl<'a, T: Queryable> QueryRef<'a, T> {
pub fn val(self) -> &'a T {
self.0
}
pub fn path(self) -> QueryPath {
self.1
}
}
impl<'a, T: Queryable> From<Pointer<'a, T>> for QueryRef<'a, T> {
fn from(pointer: Pointer<'a, T>) -> Self {
QueryRef(pointer.inner, pointer.path)
}
}
pub fn js_path<'a, T: Queryable>(path: &str, value: &'a T) -> Queried<Vec<QueryRef<'a, T>>> {
js_path_process(&parse_json_path(path)?, value)
}
pub fn js_path_process<'a, 'b, T: Queryable>(
path: &'b JpQuery,
value: &'a T,
) -> Queried<Vec<QueryRef<'a, T>>> {
match path.process(State::root(value)).data {
Data::Ref(p) => Ok(vec![p.into()]),
Data::Refs(refs) => Ok(refs.into_iter().map(Into::into).collect()),
Data::Value(v) => Err(v.into()),
Data::Nothing => Ok(vec![]),
}
}
pub fn js_path_vals<'a, T: Queryable>(path: &str, value: &'a T) -> Queried<Vec<&'a T>> {
Ok(js_path(path, value)?
.into_iter()
.map(|r| r.val())
.collect::<Vec<_>>())
}
pub fn js_path_path<T: Queryable>(path: &str, value: &T) -> Queried<Vec<QueryPath>> {
Ok(js_path(path, value)?
.into_iter()
.map(|r| r.path())
.collect::<Vec<_>>())
}
#[cfg(test)]
mod tests {
use crate::parser::errors::JsonPathError;
use crate::parser::{parse_json_path, Parsed};
use crate::query::queryable::Queryable;
use crate::query::{js_path, js_path_process, Queried, QueryRef};
use crate::JsonPath;
use serde_json::{json, Value};
fn test<'a, R>(json: &'a str, path: &str, expected: Vec<R>) -> Parsed<()>
where
R: Into<QueryRef<'a, Value>>,
{
let json: Value = serde_json::from_str(json).map_err(|v| JsonPathError::NoRulePath)?;
let expected: Vec<QueryRef<'a, Value>> = expected.into_iter().map(|v| v.into()).collect();
assert_eq!(json.query_with_path(path)?, expected);
Ok(())
}
fn template_json<'a>() -> &'a str {
r#" {"store": { "book": [
{
"category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{
"category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{
"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
},
"array":[0,1,2,3,4,5,6,7,8,9],
"orders":[
{
"ref":[1,2,3],
"id":1,
"filled": true
},
{
"ref":[4,5,6],
"id":2,
"filled": false
},
{
"ref":[7,8,9],
"id":3,
"filled": null
}
],
"expensive": 10 }"#
}
fn update_by_path_test() -> Queried<()> {
let mut json = json!([
{"verb": "RUN","distance":[1]},
{"verb": "TEST"},
{"verb": "DO NOT RUN"}
]);
let path = json.query_only_path("$.[?(@.verb == 'RUN')]")?;
let elem = path.first().cloned().unwrap_or_default();
if let Some(v) = json
.reference_mut(elem)
.and_then(|v| v.as_object_mut())
.and_then(|v| v.get_mut("distance"))
.and_then(|v| v.as_array_mut())
{
v.push(json!(2))
}
assert_eq!(
json,
json!([
{"verb": "RUN","distance":[1,2]},
{"verb": "TEST"},
{"verb": "DO NOT RUN"}
])
);
Ok(())
}
#[test]
fn simple_test() {
let j1 = json!(2);
let _ = test("[1,2,3]", "$[1]", vec![(&j1, "$[1]".to_string())]);
}
#[test]
fn root_test() {
let js = serde_json::from_str(template_json()).unwrap();
let _ = test(template_json(), "$", vec![(&js, "$")]);
}
#[test]
fn descent_test() {
let v1 = json!("reference");
let v2 = json!("fiction");
let _ = test(
template_json(),
"$..category",
vec![
(&v1, "$['store']['book'][0]['category']"),
(&v2, "$['store']['book'][1]['category']"),
(&v2, "$['store']['book'][2]['category']"),
(&v2, "$['store']['book'][3]['category']"),
],
);
let js1 = json!(19.95);
let js2 = json!(8.95);
let js3 = json!(12.99);
let js4 = json!(8.99);
let js5 = json!(22.99);
let _ = test(
template_json(),
"$.store..price",
vec![
(&js1, "$['store']['bicycle']['price']"),
(&js2, "$['store']['book'][0]['price']"),
(&js3, "$['store']['book'][1]['price']"),
(&js4, "$['store']['book'][2]['price']"),
(&js5, "$['store']['book'][3]['price']"),
],
);
let js1 = json!("Nigel Rees");
let js2 = json!("Evelyn Waugh");
let js3 = json!("Herman Melville");
let js4 = json!("J. R. R. Tolkien");
let _ = test(
template_json(),
"$..author",
vec![
(&js1, "$['store']['book'][0]['author']"),
(&js2, "$['store']['book'][1]['author']"),
(&js3, "$['store']['book'][2]['author']"),
(&js4, "$['store']['book'][3]['author']"),
],
);
}
#[test]
fn wildcard_test() {
let js1 = json!("reference");
let js2 = json!("fiction");
let _ = test(
template_json(),
"$..book.[*].category",
vec![
(&js1, "$['store']['book'][0].['category']"),
(&js2, "$['store']['book'][1].['category']"),
(&js2, "$['store']['book'][2].['category']"),
(&js2, "$['store']['book'][3].['category']"),
],
);
let js1 = json!("Nigel Rees");
let js2 = json!("Evelyn Waugh");
let js3 = json!("Herman Melville");
let js4 = json!("J. R. R. Tolkien");
let _ = test(
template_json(),
"$.store.book[*].author",
vec![
(&js1, "$['store']['book'][0]['author']"),
(&js2, "$['store']['book'][1]['author']"),
(&js3, "$['store']['book'][2]['author']"),
(&js4, "$['store']['book'][3]['author']"),
],
);
}
#[test]
fn descendent_wildcard_test() {
let js1 = json!("0-553-21311-3");
let js2 = json!("0-395-19395-8");
let _ = test(
template_json(),
"$..*.[?@].isbn",
vec![
(&js1, "$['store']['book'][2]['isbn']"),
(&js2, "$['store']['book'][3]['isbn']"),
],
);
}
#[test]
fn field_test() {
let value = json!({"active":1});
let _ = test(
r#"{"field":{"field":[{"active":1},{"passive":1}]}}"#,
"$.field.field[?@.active]",
vec![(&value, "$['field']['field'][0]")],
);
}
#[test]
fn index_index_test() {
let value = json!("0-553-21311-3");
let _ = test(
template_json(),
"$..book[2].isbn",
vec![(&value, "$['store']['book'][2]['isbn']")],
);
}
#[test]
fn index_unit_index_test() {
let value = json!("0-553-21311-3");
let _ = test(
template_json(),
"$..book[2,4].isbn",
vec![(&value, "$['store']['book'][2]['isbn']")],
);
let value1 = json!("0-395-19395-8");
let _ = test(
template_json(),
"$..book[2,3].isbn",
vec![
(&value, "$['store']['book'][2]['isbn']"),
(&value1, "$['store']['book'][3]['isbn']"),
],
);
}
#[test]
fn index_unit_keys_test() {
let js1 = json!("Moby Dick");
let js2 = json!(8.99);
let js3 = json!("The Lord of the Rings");
let js4 = json!(22.99);
let _ = test(
template_json(),
"$..book[2,3]['title','price']",
vec![
(&js1, "$['store']['book'][2]['title']"),
(&js3, "$['store']['book'][3]['title']"),
(&js2, "$['store']['book'][2]['price']"),
(&js4, "$['store']['book'][3]['price']"),
],
);
}
#[test]
fn index_slice_test() -> Parsed<()> {
let i0 = "$['array'][0]";
let i1 = "$['array'][1]";
let i2 = "$['array'][2]";
let i3 = "$['array'][3]";
let i4 = "$['array'][4]";
let i5 = "$['array'][5]";
let i6 = "$['array'][6]";
let i7 = "$['array'][7]";
let i8 = "$['array'][8]";
let i9 = "$['array'][9]";
let j0 = json!(0);
let j1 = json!(1);
let j2 = json!(2);
let j3 = json!(3);
let j4 = json!(4);
let j5 = json!(5);
let j6 = json!(6);
let j7 = json!(7);
let j8 = json!(8);
let j9 = json!(9);
test(
template_json(),
"$.array[:]",
vec![
(&j0, i0),
(&j1, i1),
(&j2, i2),
(&j3, i3),
(&j4, i4),
(&j5, i5),
(&j6, i6),
(&j7, i7),
(&j8, i8),
(&j9, i9),
],
)?;
test(
template_json(),
"$.array[1:4:2]",
vec![(&j1, i1), (&j3, i3)],
)?;
test(
template_json(),
"$.array[::3]",
vec![(&j0, i0), (&j3, i3), (&j6, i6), (&j9, i9)],
)?;
test(template_json(), "$.array[-1:]", vec![(&j9, i9)])?;
test(template_json(), "$.array[-2:-1]", vec![(&j8, i8)])?;
Ok(())
}
#[test]
fn index_filter_test() -> Parsed<()> {
let moby = json!("Moby Dick");
let rings = json!("The Lord of the Rings");
test(
template_json(),
"$..book[?@.isbn].title",
vec![
(&moby, "$['store']['book'][2]['title']"),
(&rings, "$['store']['book'][3]['title']"),
],
)?;
let sword = json!("Sword of Honour");
test(
template_json(),
"$..book[?(@.price != 8.95)].title",
vec![
(&sword, "$['store']['book'][1]['title']"),
(&moby, "$['store']['book'][2]['title']"),
(&rings, "$['store']['book'][3]['title']"),
],
)?;
let sayings = json!("Sayings of the Century");
test(
template_json(),
"$..book[?(@.price == 8.95)].title",
vec![(&sayings, "$['store']['book'][0]['title']")],
)?;
let js12 = json!(12.99);
let js899 = json!(8.99);
let js2299 = json!(22.99);
test(
template_json(),
"$..book[?@.price >= 8.99].price",
vec![
(&js12, "$['store']['book'][1]['price']"),
(&js899, "$['store']['book'][2]['price']"),
(&js2299, "$['store']['book'][3]['price']"),
],
)?;
test(
template_json(),
"$..book[?(@.price >= $.expensive)].price",
vec![
(&js12, "$['store']['book'][1]['price']"),
(&js2299, "$['store']['book'][3]['price']"),
],
)?;
Ok(())
}
#[test]
fn union_quotes() -> Queried<()> {
let json = json!({
"a": "ab",
"b": "bc"
});
let vec = js_path("$['a',\r'b']", &json)?;
assert_eq!(
vec,
vec![
(&json!("ab"), "$['a']".to_string()).into(),
(&json!("bc"), "$['b']".to_string()).into(),
]
);
Ok(())
}
#[test]
fn space_between_selectors() -> Queried<()> {
let json = json!({
"a": {
"b": "ab"
}
});
let vec = js_path("$['a'] \r['b']", &json)?;
assert_eq!(vec, vec![(&json!("ab"), "$['a']['b']".to_string()).into(),]);
Ok(())
}
#[test]
fn space_in_search() -> Queried<()> {
let json = json!(["foo", "123"]);
let vec = js_path("$[?search(@\n,'[a-z]+')]", &json)?;
assert_eq!(vec, vec![(&json!("foo"), "$[0]".to_string()).into(),]);
Ok(())
}
#[test]
fn filter_key() -> Queried<()> {
let json = json!([
{
"a": "b",
"d": "e"
},
{
"a": 1,
"d": "f"
}
]);
let vec = js_path("$[?@.a!=\"b\"]", &json)?;
assert_eq!(
vec,
vec![(&json!({"a":1, "d":"f"}), "$[1]".to_string()).into(),]
);
Ok(())
}
#[test]
fn regex_key() -> Queried<()> {
let json = json!({
"regex": "b.?b",
"values": [
"abc",
"bcd",
"bab",
"bba",
"bbab",
"b",
true,
[],
{}
]
});
let vec = js_path("$.values[?match(@, $.regex)]", &json)?;
assert_eq!(
vec,
vec![(&json!("bab"), "$['values'][2]".to_string()).into(),]
);
Ok(())
}
#[test]
fn name_sel() -> Queried<()> {
let json = json!({
"/": "A"
});
let vec = js_path("$['\\/']", &json)?;
assert_eq!(vec, vec![(&json!("A"), "$['\\/']".to_string()).into(),]);
Ok(())
}
#[test]
fn unicode_fns() -> Queried<()> {
let json = json!(["ж", "Ж", "1", "жЖ", true, [], {}]);
let vec = js_path("$[?match(@, '\\\\p{Lu}')]", &json)?;
assert_eq!(vec, vec![(&json!("Ж"), "$[1]".to_string()).into(),]);
Ok(())
}
#[test]
fn fn_res_can_not_compare() -> Queried<()> {
let json = json!({});
let vec = js_path("$[?match(@.a, 'a.*')==true]", &json);
assert!(vec.is_err());
Ok(())
}
#[test]
fn too_small() -> Queried<()> {
let json = json!({});
let vec = js_path("$[-9007199254740992]", &json);
assert!(vec.is_err());
Ok(())
}
#[test]
fn filter_data() -> Queried<()> {
let json = json!({
"a": 1,
"b": 2,
"c": 3
});
let vec: Vec<String> = json.query_only_path("$[?@<3]")?.into_iter().collect();
assert_eq!(vec, vec!["$['a']".to_string(), "$['b']".to_string()]);
Ok(())
}
#[test]
fn exp_no_error() -> Queried<()> {
let json = json!([
{
"a": 100,
"d": "e"
},
{
"a": 100.1,
"d": "f"
},
{
"a": "100",
"d": "g"
}
]);
let vec: Vec<&Value> = json.query("$[?@.a==1E2]")?;
assert_eq!(vec, vec![&json!({"a":100, "d":"e"})]);
Ok(())
}
#[test]
fn single_quote() -> Queried<()> {
let json = json!({
"a'": "A",
"b": "B"
});
let vec = js_path("$[\"a'\"]", &json)?;
assert_eq!(vec, vec![(&json!("A"), "$['\"a\'\"']".to_string()).into(),]);
Ok(())
}
#[test]
fn union() -> Queried<()> {
let json = json!([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
let vec: Vec<QueryRef<Value>> = json.query_with_path("$[1,5:7]")?;
assert_eq!(
vec,
vec![
(&json!(1), "$[1]".to_string()).into(),
(&json!(5), "$[5]".to_string()).into(),
(&json!(6), "$[6]".to_string()).into(),
]
);
Ok(())
}
#[test]
fn basic_descendent() -> Queried<()> {
let json = json!({
"o": [
0,
1,
[
2,
3
]
]
});
let vec = js_path("$..[1]", &json)?;
assert_eq!(
vec,
vec![
(&json!(1), "$['o'][1]".to_string()).into(),
(&json!(3), "$['o'][2][1]".to_string()).into(),
]
);
Ok(())
}
#[test]
fn filter_absent() -> Queried<()> {
let json = json!([
{
"list": [
1
]
}
]);
let vec = js_path("$[?@.absent==@.list[9]]", &json)?;
assert_eq!(
vec,
vec![(&json!({"list": [1]}), "$[0]".to_string()).into(),]
);
Ok(())
}
#[test]
fn filter_star() -> Queried<()> {
let json = json!([1,[],[2],{},{"a": 3}]);
let vec = json.query_with_path("$[?@.*]")?;
assert_eq!(
vec,
vec![
(&json!([2]), "$[2]".to_string()).into(),
(&json!({"a": 3}), "$[4]".to_string()).into(),
]
);
Ok(())
}
#[test]
fn space_test() -> Queried<()> {
let json = json!({ " ": "A"});
let vec = json.query_with_path("$[' ']")?;
assert_eq!(vec, vec![(&json!("A"), "$[\' \']".to_string()).into(),]);
Ok(())
}
#[test]
fn neg_idx() -> Queried<()> {
let json = json!(["first", "second"]);
let vec = json.query_with_path("$[-2]")?;
assert_eq!(vec, vec![(&json!("first"), "$[0]".to_string()).into(),]);
Ok(())
}
#[test]
fn filter_slice() -> Queried<()> {
let json = json!([
1,
[],
[
2
],
[
2,
3,
4
],
{},
{
"a": 3
}
]);
let vec = json.query_with_path("$[?@[0:2]]")?;
assert_eq!(
vec,
vec![
(&json!([2]), "$[2]").into(),
(&json!([2, 3, 4]), "$[3]").into(),
]
);
Ok(())
}
#[test]
fn surr_pairs() -> Queried<()> {
let json = json!({
"𝄞": "A"
});
let vec = json.query_with_path("$['𝄞']")?;
assert_eq!(vec, vec![(&json!("A"), "$['𝄞']".to_string()).into()]);
Ok(())
}
#[test]
fn tab_key() -> Queried<()> {
let json = json!({
"\\t": "A"
});
let vec = json.query_with_path("$['\\t']")?;
assert_eq!(vec, vec![(&json!("A"), "$['\\t']".to_string()).into()]);
Ok(())
}
#[test]
fn escaped_up_hex() -> Queried<()> {
let json = json!({
"☺": "A"
});
let vec = json.query_with_path("$['☺']")?;
assert_eq!(vec, vec![(&json!("A"), "$['☺']".to_string()).into()]);
Ok(())
}
#[test]
fn carr_return() -> Queried<()> {
let json = json!({
"\\r": "A"
});
let vec = json.query_with_path("$['\\r']")?;
assert_eq!(vec, vec![(&json!("A"), "$['\\r']".to_string()).into()]);
Ok(())
}
#[test]
fn prepared_query() -> Queried<()> {
let json1 = json!({
"a": 1,
"b": 2,
"c": 3
});
let json2 = json!({
"a": 1,
"b": 2,
"c": 3
});
let jq = parse_json_path("$[?@<3]")?;
let v1 = js_path_process(&jq, &json1)?;
let v2 = js_path_process(&jq, &json2)?;
assert_eq!(v1, v2);
Ok(())
}
}