diff --git a/.gitignore b/.gitignore index 8647db2..5e2c017 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ .vscode !.idea/runConfigurations/ /target/ -Cargo.lock \ No newline at end of file +Cargo.lock +callgrind.out.* \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 7234633..d1724f6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,41 +36,41 @@ matrix: script: - cargo build --verbose --all - cargo test --verbose --all - - language: node_js - os: linux - node_js: - - '11' - - '10' - - '9' - - '8' - before_install: - - curl https://sh.rustup.rs -sSf > /tmp/rustup.sh - - sh /tmp/rustup.sh -y - - export PATH="$HOME/.cargo/bin:$PATH" - - source "$HOME/.cargo/env" - - npm install -g neon-cli - - cd nodejs - - node -v - - npm -v - - npm install - script: - - npm test - - language: node_js - os: osx - node_js: - - '11' - - '10' - - '9' - - '8' - before_install: - - curl https://sh.rustup.rs -sSf > /tmp/rustup.sh - - sh /tmp/rustup.sh -y - - export PATH="$HOME/.cargo/bin:$PATH" - - source "$HOME/.cargo/env" - - npm install -g neon-cli - - cd nodejs - - node -v - - npm -v - - npm install - script: - - npm test \ No newline at end of file +# - language: node_js +# os: linux +# node_js: +# - '11' +# - '10' +# - '9' +# - '8' +# before_install: +# - curl https://sh.rustup.rs -sSf > /tmp/rustup.sh +# - sh /tmp/rustup.sh -y +# - export PATH="$HOME/.cargo/bin:$PATH" +# - source "$HOME/.cargo/env" +# - npm install -g neon-cli +# - cd nodejs +# - node -v +# - npm -v +# - npm install +# script: +# - npm test +# - language: node_js +# os: osx +# node_js: +# - '11' +# - '10' +# - '9' +# - '8' +# before_install: +# - curl https://sh.rustup.rs -sSf > /tmp/rustup.sh +# - sh /tmp/rustup.sh -y +# - export PATH="$HOME/.cargo/bin:$PATH" +# - source "$HOME/.cargo/env" +# - npm install -g neon-cli +# - cd nodejs +# - node -v +# - npm -v +# - npm install +# script: +# - npm test \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 52ca42a..3daccb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jsonpath_lib" -version = "0.1.13" +version = "0.2.0" authors = ["Changseok Han "] description = "It is JsonPath engine written in Rust. it provide a similar API interface in Webassembly and Javascript also. - Webassembly Demo: https://freestrings.github.io/jsonpath" @@ -23,6 +23,7 @@ env_logger = "0.6.0" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["preserve_order"] } indexmap = "1.0.2" +array_tool = "~1.0.3" [dev-dependencies] bencher = "0.1.5" diff --git a/README.md b/README.md index 0ad07c3..8855383 100644 --- a/README.md +++ b/README.md @@ -15,43 +15,20 @@ It is JsonPath [JsonPath](https://goessner.net/articles/JsonPath/) engine writte ## Rust API -- [jsonpath_lib crate](#jsonpath_lib-crate) -- [Rust - jsonpath::Selector struct](#rust---jsonpathselector-struct) -- [Rust - jsonpath::select(json: &serde_json::value::Value, jsonpath: &str)](#rust---jsonpathselectjson-serde_jsonvaluevalue-jsonpath-str) -- [Rust - jsonpath::select_as_str(json_str: &str, jsonpath: &str)](#rust---jsonpathselect_as_strjson-str-jsonpath-str) -- [Rust - jsonpath::select_as\(json_str: &str, jsonpath: &str)](#rust---jsonpathselect_ast-serdededeserializeownedjson-str-jsonpath-str) -- [Rust - jsonpath::compile(jsonpath: &str)](#rust---jsonpathcompilejsonpath-str) -- [Rust - jsonpath::selector(json: &serde_json::value::Value)](#rust---jsonpathselectorjson-serde_jsonvaluevalue) -- [Rust - jsonpath::selector_as\(json: &serde_json::value::Value)](#rust---jsonpathselector_ast-serdededeserializeownedjson-serde_jsonvaluevalue) -- [Rust - Other Examples](https://github.com/freestrings/jsonpath/wiki/rust-examples) +
jsonpath_lib crate -## Javascript API - -- [npm package](#npm-package) -- [Javascript - jsonpath.Selector class](#javascript---selector-class) -- [Javascript - jsonpath.select(json: string|object, jsonpath: string)](#javascript---jsonpathselectjson-stringobject-jsonpath-string) -- [Javascript - jsonpath.compile(jsonpath: string)](#javascript---jsonpathcompilejsonpath-string) -- [Javascript - jsonpath.selector(json: string|object)](#javascript---jsonpathselectorjson-stringobject) -- [Javascript - allocJson, deallocJson (Webassembly Only)](#javascript---allocjson-deallocjson-webassembly-only) -- [Javascript - Other Examples](https://github.com/freestrings/jsonpath/wiki/Javascript-examples) - ---- - -### Rust API - -#### jsonpath_lib crate -[Go to creates.io](https://crates.io/crates/jsonpath_lib) +Go to [`jsonpath_lib` creates.io](https://crates.io/crates/jsonpath_lib) ```rust extern crate jsonpath_lib as jsonpath; -#[macro_use] -extern crate serde_json; ``` -#### Rust - jsonpath::Selector struct +
+ +
Rust - jsonpath::Selector struct ```rust -#[derive(Serialize, Deserialize, PartialEq, Debug)] +#[derive(Deserialize, PartialEq, Debug)] struct Friend { name: String, age: Option, @@ -73,46 +50,67 @@ let mut selector = Selector::new(); let result = selector .path("$..[?(@.age >= 30)]").unwrap() -// .value_from_str(&serde_json::to_string(&json_obj).unwrap() /*&str*/).unwrap() -// .value_from(&json_obj /*&impl serde::ser::Serialize*/).unwrap() - .value(&json_obj /*serde_json::value::Value*/).unwrap() - .select_as_value().unwrap(); + .value(&json_obj) + .select().unwrap(); -assert_eq!(json!([{"name": "친구3", "age": 30}]), result); +assert_eq!(vec![&json!({"name": "친구3", "age": 30})], result); let result = selector.select_as_str().unwrap(); assert_eq!(r#"[{"name":"친구3","age":30}]"#, result); -let result = selector.select_as::>().unwrap(); +let result = selector.select_as::().unwrap(); assert_eq!(vec![Friend { name: "친구3".to_string(), age: Some(30) }], result); - -let _ = selector.map(|v| { - let r = match v { - Value::Array(mut vec) => { - for mut v in &mut vec { - v.as_object_mut().unwrap().remove("age"); - } - Value::Array(vec) - } - _ => Value::Null - }; - Some(r) -}); -assert_eq!(json!([{ "name": "친구3" }]), selector.get().unwrap()); - -let _ = selector.value(&json_obj).unwrap() - .map_as(|mut v: Vec| { - let mut f = v.pop().unwrap(); - f.name = "friend3".to_string(); - f.age = None; - Some(vec![f]) - }); - -assert_eq!(vec![Friend { name: "friend3".to_string(), age: None }], - selector.get_as::>().unwrap()); ``` -#### Rust - jsonpath::select(json: &serde_json::value::Value, jsonpath: &str) +
+ +
Rust - jsonpath::SelectorMut struct + +```rust +let json_obj = json!({ + "school": { + "friends": [ + {"name": "친구1", "age": 20}, + {"name": "친구2", "age": 20} + ] + }, + "friends": [ + {"name": "친구3", "age": 30}, + {"name": "친구4"} +]}); + +let mut selector_mut = SelectorMut::new(); + +let result = selector_mut + .str_path("$..[?(@.age == 20)].age").unwrap() + .value(json_obj) + .replace_with(&mut |v| { + let age = if let Value::Number(n) = v { + n.as_u64().unwrap() * 2 + } else { + 0 + }; + + json!(age) + }).unwrap() + .take().unwrap(); + +assert_eq!(result, json!({ + "school": { + "friends": [ + {"name": "친구1", "age": 40}, + {"name": "친구2", "age": 40} + ] + }, + "friends": [ + {"name": "친구3", "age": 30}, + {"name": "친구4"} +]})); +``` + +
+ +
Rust - jsonpath::select(json: &serde_json::value::Value, jsonpath: &str) ```rust let json_obj = json!({ @@ -129,14 +127,16 @@ let json_obj = json!({ let json = jsonpath::select(&json_obj, "$..friends[0]").unwrap(); -let ret = json!([ - {"name": "친구3", "age": 30}, - {"name": "친구1", "age": 20} +assert_eq!(json, vec![ + &json!({"name": "친구3", "age": 30}), + &json!({"name": "친구1", "age": 20}) ]); -assert_eq!(json, ret); ``` -#### Rust - jsonpath::select_as_str(json: &str, jsonpath: &str) +
+ + +
Rust - jsonpath::select_as_str(json_str: &str, jsonpath: &str) ```rust let ret = jsonpath::select_as_str(r#" @@ -157,7 +157,9 @@ let ret = jsonpath::select_as_str(r#" assert_eq!(ret, r#"[{"name":"친구3","age":30},{"name":"친구1","age":20}]"#); ``` -#### Rust - jsonpath::select_as\(json: &str, jsonpath: &str) +
+ +
Rust - jsonpath::select_as<T: `serde::de::DeserializeOwned`>(json_str: &str, jsonpath: &str) ```rust #[derive(Deserialize, PartialEq, Debug)] @@ -167,7 +169,7 @@ struct Person { phones: Vec, } -let ret: Person = jsonpath::select_as(r#" +let ret: Vec = jsonpath::select_as(r#" { "person": { @@ -187,10 +189,12 @@ let person = Person { phones: vec!["+44 1234567".to_string(), "+44 2345678".to_string()], }; -assert_eq!(person, ret); +assert_eq!(ret[0], person); ``` -#### Rust - jsonpath::compile(jsonpath: &str) +
+ +
Rust - jsonpath::compile(jsonpath: &str) ```rust let mut template = jsonpath::compile("$..friends[0]"); @@ -209,15 +213,15 @@ let json_obj = json!({ let json = template(&json_obj).unwrap(); -let ret = json!([ - {"name": "친구3", "age": 30}, - {"name": "친구1", "age": 20} +assert_eq!(json, vec![ + &json!({"name": "친구3", "age": 30}), + &json!({"name": "친구1", "age": 20}) ]); - -assert_eq!(json, ret); ``` -#### Rust - jsonpath::selector(json: &serde_json::value::Value) +
+ +
Rust - jsonpath::selector(json: &serde_json::value::Value) ```rust let json_obj = json!({ @@ -236,24 +240,22 @@ let mut selector = jsonpath::selector(&json_obj); let json = selector("$..friends[0]").unwrap(); -let ret = json!([ - {"name": "친구3", "age": 30}, - {"name": "친구1", "age": 20} +assert_eq!(json, vec![ + &json!({"name": "친구3", "age": 30}), + &json!({"name": "친구1", "age": 20}) ]); -assert_eq!(json, ret); - let json = selector("$..friends[1]").unwrap(); -let ret = json!([ - {"name": "친구4"}, - {"name": "친구2", "age": 20} +assert_eq!(json, vec![ + &json!({"name": "친구4"}), + &json!({"name": "친구2", "age": 20}) ]); - -assert_eq!(json, ret); ``` -#### Rust - jsonpath::selector_as\(json: &serde_json::value::Value) +
+ +
Rust - jsonpath::selector_as<T: serde::de::DeserializeOwned>(json: &serde_json::value::Value) ```rust let json_obj = json!({ @@ -268,13 +270,13 @@ let json_obj = json!({ {"name": "친구4"} ]}); -#[derive(Serialize, Deserialize, PartialEq, Debug)] +#[derive(Deserialize, PartialEq, Debug)] struct Friend { name: String, age: Option, } -let mut selector = jsonpath::selector_as::>(&json_obj); +let mut selector = jsonpath::selector_as::(&json_obj); let json = selector("$..friends[0]").unwrap(); @@ -294,14 +296,90 @@ let ret = vec!( assert_eq!(json, ret); ``` ---- +
-### Javascript API +
Rust - jsonpath::delete(value: &Value, path: &str) -#### npm package +```rust +let json_obj = json!({ + "school": { + "friends": [ + {"name": "친구1", "age": 20}, + {"name": "친구2", "age": 20} + ] + }, + "friends": [ + {"name": "친구3", "age": 30}, + {"name": "친구4"} +]}); + +let ret = jsonpath::delete(json_obj, "$..[?(20 == @.age)]").unwrap(); + +assert_eq!(ret, json!({ + "school": { + "friends": [ + null, + null + ] + }, + "friends": [ + {"name": "친구3", "age": 30}, + {"name": "친구4"} +]})); +``` + +
+ +
Rust - jsonpath::replace_with<F: FnMut(&Value) -> Value>(value: &Value, path: &str, fun: &mut F) + +```rust +let json_obj = json!({ + "school": { + "friends": [ + {"name": "친구1", "age": 20}, + {"name": "친구2", "age": 20} + ] + }, + "friends": [ + {"name": "친구3", "age": 30}, + {"name": "친구4"} +]}); + +let ret = jsonpath::replace_with(json_obj, "$..[?(@.age == 20)].age", &mut |v| { + let age = if let Value::Number(n) = v { + n.as_u64().unwrap() * 2 + } else { + 0 + }; + + json!(age) +}).unwrap(); + +assert_eq!(ret, json!({ + "school": { + "friends": [ + {"name": "친구1", "age": 40}, + {"name": "친구2", "age": 40} + ] + }, + "friends": [ + {"name": "친구3", "age": 30}, + {"name": "친구4"} +]})); +``` + +
+ +[Rust - Other Examples](https://github.com/freestrings/jsonpath/wiki/rust-examples) + +## Javascript API + +
npm package ##### jsonpath-wasm +Goto [`jsonpath-wasm` npmjs.org](https://www.npmjs.com/package/jsonpath-wasm) + ```javascript // browser import * as jsonpath from "jsonpath-wasm"; @@ -311,13 +389,15 @@ const jsonpath = require('jsonpath-wasm'); ##### jsonpath-rs (NodeJS only) -[Goto npmjs.org](https://www.npmjs.com/package/jsonpath-rs) +Goto [`jsonpath-rs` npmjs.org](https://www.npmjs.com/package/jsonpath-rs) ```javascript const jsonpath = require('jsonpath-rs'); ``` -#### javascript - Selector class +
+ +
Javascript - jsonpath.Selector class ##### jsonpath-wasm `wasm-bindgen` 리턴 타입 제약 때문에 빌더 패턴은 지원하지 않는다. @@ -347,21 +427,10 @@ let selector = new jsonpath.Selector(); selector.path('$..friends[0]'); selector.value(jsonObj); -let selectAsObj = selector.selectAs(); -let selectAsString = selector.selectAsStr(); +let retObj = selector.select(); -console.log( - JSON.stringify(ret) == JSON.stringify(selectAsObj), - JSON.stringify(ret) == selectAsString -); +console.log(JSON.stringify(ret) == JSON.stringify(retObj)); -selector.map(function(v) { - let f1 = v[0]; - f1.name = 'friend3'; - return [f1]; -}); - -console.log(JSON.stringify(selector.get()) === JSON.stringify([{"name": "friend3", "age": 30}])); // => true ``` @@ -390,18 +459,82 @@ let selector = new jsonpath.Selector() .path('$..friends[0]') .value(jsonObj); -let selectAsObj = selector.selectAs(); -let selectAsString = selector.selectAsStr(); +let retObj = selector.select(); -console.log( - JSON.stringify(ret) == JSON.stringify(selectAsObj), - JSON.stringify(ret) == selectAsString -); +console.log(JSON.stringify(ret) == JSON.stringify(retObj)); -// => true, true +// => true ``` -#### Javascript - jsonpath.select(json: string|object, jsonpath: string) +
+ +
Javascript - jsonpath.SelectorMut class + +`since 0.2.0` + +빌더 패턴 제약은 `Selector class`와 동일하다. + +```javascript +let jsonObj = { + 'school': { + 'friends': [ + {'name': '친구1', 'age': 20}, + {'name': '친구2', 'age': 20}, + ], + }, + 'friends': [ + {'name': '친구3', 'age': 30}, + {'name': '친구4'}, + ], +}; + +let selector = new jsonpath.SelectorMut(); +selector.path('$..[?(@.age == 20)]'); + +{ + selector.value(jsonObj); + selector.deleteValue(); + + let resultObj = { + 'school': {'friends': [null, null]}, + 'friends': [ + {'name': '친구3', 'age': 30}, + {'name': '친구4'}, + ], + }; + console.log(JSON.stringify(selector.take()) !== JSON.stringify(resultObj)); + + // => true +} + +{ + selector.value(jsonObj); + selector.replaceWith((v) => { + v.age = v.age * 2; + return v; + }); + + let resultObj = { + 'school': { + 'friends': [ + {'name': '친구1', 'age': 40}, + {'name': '친구2', 'age': 40}, + ], + }, + 'friends': [ + {'name': '친구3', 'age': 30}, + {'name': '친구4'}, + ], + }; + console.log(JSON.stringify(selector.take()) !== JSON.stringify(resultObj)); + + // => true +} +``` + +
+ +
Javascript - jsonpath.select(json: string|object, jsonpath: string) ```javascript let jsonObj = { @@ -434,7 +567,9 @@ console.log( // => true, true ``` -#### Javascript - jsonpath.compile(jsonpath: string) +
+ +
Javascript - jsonpath.compile(jsonpath: string) ```javascript let template = jsonpath.compile('$..friends[0]'); @@ -493,8 +628,10 @@ console.log( // => true, true ``` -#### Javascript - jsonpath.selector(json: string|object) - +
+ +
Javascript - jsonpath.selector(json: string|object) + ```javascript let jsonObj = { "school": { @@ -534,14 +671,13 @@ console.log( // => true, true ``` -#### Javascript - allocJson, deallocJson (Webassembly Only) -wasm-bindgen은 Javascript와 Webassembly간 값을 주고받을 때 JSON 객체는 String으로 변환되기 때문에, 반복해서 사용되는 JSON 객체는 Webassembly 영역에 생성해 두면 성능에 도움이 된다. +
-Since wasm-bindgen converts JSON objects to String when exchanging values between Javascript and Webassembly, creating frequently used JSON objects in the WebAssembly area helps performance. +
Javascript - jsonpath.deleteValue(json: string|object, path: string) + +`since 0.2.0` ```javascript -const jsonpath = require('jsonpath-wasm'); - let jsonObj = { "school": { "friends": [ @@ -555,37 +691,60 @@ let jsonObj = { ] }; -// allocate jsonObj in webassembly -let ptr = jsonpath.allocJson(jsonObj); +let _1 = jsonpath.deleteValue(jsonObj, '$..friends[0]'); +let result = jsonpath.deleteValue(_1, '$..friends[1]'); -// `0` is invalid pointer -if(ptr == 0) { - console.error('invalid ptr'); -} +console.log(JSON.stringify(result) !== JSON.stringify({ + "school": { "friends": [null, null]}, + "friends": [null, null] +})); -let path = '$..friends[0]'; -let template = jsonpath.compile(path); -let selector = jsonpath.selector(jsonObj); -// create selector as pointer -let ptrSelector = jsonpath.selector(ptr); +// => true -let ret1 = selector(path) -let ret2 = ptrSelector(path) -let ret3 = template(jsonObj); -// select as pointer -let ret4 = template(ptr); -let ret5 = jsonpath.select(jsonObj, path); -// select as pointer -let ret6 = jsonpath.select(ptr, path); - -console.log( - JSON.stringify(ret1) == JSON.stringify(ret2), - JSON.stringify(ret1) == JSON.stringify(ret3), - JSON.stringify(ret1) == JSON.stringify(ret4), - JSON.stringify(ret1) == JSON.stringify(ret5), - JSON.stringify(ret1) == JSON.stringify(ret6)); - -// => true true true true true - -jsonpath.deallocJson(ptr); ``` + +
+ +
Javascript - jsonpath.replaceWith(json: string|object, path: string, fun: function(json: object) => json: object + +`since 0.2.0` + +```javascript +let jsonObj = { + "school": { + "friends": [ + {"name": "친구1", "age": 20}, + {"name": "친구2", "age": 20} + ] + }, + "friends": [ + {"name": "친구3", "age": 30}, + {"name": "친구4"} + ] +}; + +let result = jsonpath.replaceWith(jsonObj, '$..friends[0]', (v) => { + v.age = v.age * 2; + return v; +}); + +console.log(JSON.stringify(result) === JSON.stringify({ + "school": { + "friends": [ + {"name": "친구1", "age": 40}, + {"name": "친구2", "age": 20} + ] + }, + "friends": [ + {"name": "친구3", "age": 60}, + {"name": "친구4"} + ] +})); + +// => true + +``` + +
+ +[Javascript - Other Examples](https://github.com/freestrings/jsonpath/wiki/Javascript-examples) \ No newline at end of file diff --git a/benches/bench.rs b/benches/bench.rs index 33c5cbf..27b1a37 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -1,18 +1,18 @@ #![feature(test)] +extern crate bencher; extern crate jsonpath_lib as jsonpath; extern crate serde; extern crate serde_json; extern crate test; -extern crate bencher; use std::io::Read; use serde::Deserialize; use serde_json::Value; +use jsonpath::{SelectorMut, Selector}; + use self::test::Bencher; -use jsonpath::ref_value::model::RefValue; -use serde::ser::Serialize; fn read_json(path: &str) -> String { let mut f = std::fs::File::open(path).unwrap(); @@ -101,28 +101,37 @@ fn bench_select_as(b: &mut Bencher) { b.iter(move || { for _ in 1..100 { - let _: Book = jsonpath::select_as(&json, r#"$..book[?(@.price<30 && @.category=="fiction")][0]"#).unwrap(); + let _: Vec = jsonpath::select_as(&json, r#"$..book[?(@.price<30 && @.category=="fiction")][0]"#).unwrap(); } }); } #[bench] -fn bench_serde_ser(b: &mut Bencher) { +fn bench_delete(b: &mut Bencher) { let json = get_json(); + let mut selector = SelectorMut::new(); + let _ = selector.str_path(get_path()); b.iter(move || { for _ in 1..100 { - let _: RefValue = json.serialize(jsonpath::ref_value::ser::Serializer).unwrap().into(); + let _ = selector.value(json.clone()).delete(); } }); } #[bench] -fn bench_serde_de(b: &mut Bencher) { - let json_string = get_string(); - let json_str = json_string.as_str(); +fn bench_select_to_compare_with_delete(b: &mut Bencher) { + let json = &get_json(); - b.iter(move || for _ in 1..100 { - let _: RefValue = serde_json::from_str(json_str).unwrap(); + let mut selector = Selector::new(); + let _ = selector.str_path(get_path()); + + b.iter(move || { + for _ in 1..100 { + let json = json.clone(); + let mut s = Selector::new(); + let _ = s.compiled_path(selector.node_ref().unwrap()).value(&json); + let _ = s.select(); + } }); } \ No newline at end of file diff --git a/benches/bench_example.rs b/benches/bench_example.rs new file mode 100644 index 0000000..dd77bca --- /dev/null +++ b/benches/bench_example.rs @@ -0,0 +1,89 @@ +#![feature(test)] +extern crate bencher; +extern crate jsonpath_lib as jsonpath; +extern crate serde; +extern crate serde_json; +extern crate test; + +use std::io::Read; + +use serde_json::Value; + +use self::test::Bencher; + +fn read_json(path: &str) -> String { + let mut f = std::fs::File::open(path).unwrap(); + let mut contents = String::new(); + f.read_to_string(&mut contents).unwrap(); + contents +} + +fn get_string() -> String { + read_json("./benches/example.json") +} + +fn get_json() -> Value { + let string = get_string(); + serde_json::from_str(string.as_str()).unwrap() +} + +fn get_path(i: usize) -> &'static str { + let paths = vec![ + "$.store.book[*].author", //0 + "$..author", //1 + "$.store.*", //2 + "$.store..price", //3 + "$..book[2]", //4 + "$..book[-2]", //5 + "$..book[0,1]", //6 + "$..book[:2]", //7 + "$..book[1:2]", //8 + "$..book[-2:]", //9 + "$..book[2:]", //10 + "$..book[?(@.isbn)]", //11 + "$.store.book[?(@.price == 10)]", //12 + "$..*", //13 + "$..book[ ?( (@.price < 13 || $.store.bicycle.price < @.price) && @.price <=10 ) ]", //14 + "$.store.book[?( (@.price < 10 || @.price > 10) && @.price > 10 )]" + ]; + paths[i] +} + +fn _selector(b: &mut Bencher, index: usize) { + let json = get_json(); + b.iter(move || { + for _ in 1..100 { + let mut selector = jsonpath::Selector::new(); + let _ = selector.str_path(get_path(index)); + selector.value(&json); + let r = selector.select(); + if r.is_err() { + panic!() + } + } + }); +} + +macro_rules! selector { + ($name:ident, $i:expr) => { + #[bench] + fn $name(b: &mut Bencher) { _selector(b, $i); } + }; +} + +selector!(example0_1, 0); +selector!(example1_1, 1); +selector!(example2_1, 2); +selector!(example3_1, 3); +selector!(example4_1, 4); +selector!(example5_1, 5); +selector!(example6_1, 6); +selector!(example7_1, 7); +selector!(example8_1, 8); +selector!(example9_1, 9); +selector!(example_10_1, 10); +selector!(example_11_1, 11); +selector!(example_12_1, 12); +selector!(example_13_1, 13); +selector!(example_14_1, 14); +selector!(example_15_1, 15); \ No newline at end of file diff --git a/benches/bench_native.sh b/benches/bench_native.sh index efb6a83..c9346c0 100755 --- a/benches/bench_native.sh +++ b/benches/bench_native.sh @@ -35,8 +35,6 @@ __extra () { sleep 1 cd "${DIR}"/javascript && echo "NodeJs - jsonpath-rs - compile: " && time ./bench.sh nativeCompile ${ITER} printf "\n" - sleep 1 - cd "${DIR}"/javascript && echo "NodeJs - jsonpath-rs - selectorClassMap: " && time ./bench.sh nativeSelectorClassMap ${ITER} } if [ "$1" = "extra" ]; then diff --git a/benches/bench_wasm.sh b/benches/bench_wasm.sh index 35be013..90ff812 100755 --- a/benches/bench_wasm.sh +++ b/benches/bench_wasm.sh @@ -36,13 +36,8 @@ __extra () { cd "${DIR}"/javascript && echo "NodeJs - jsonpath-wasm - compile: " && time ./bench.sh wasmCompile ${ITER} printf "\n" sleep 1 - cd "${DIR}"/javascript && echo "NodeJs - jsonpath-wasm - compile-alloc: " && time ./bench.sh wasmCompileAlloc ${ITER} - printf "\n" - sleep 1 cd "${DIR}"/javascript && echo "NodeJs - jsonpath-wasm - Selector: " && time ./bench.sh wasmSelectorClass ${ITER} printf "\n" - sleep 1 - cd "${DIR}"/javascript && echo "NodeJs - jsonpath-wasm - Selector map: " && time ./bench.sh wasmSelectorClassMap ${ITER} } if [ "$1" = "extra" ]; then diff --git a/benches/javascript/bench.js b/benches/javascript/bench.js index a4c7a0b..6856b3d 100644 --- a/benches/javascript/bench.js +++ b/benches/javascript/bench.js @@ -71,13 +71,6 @@ function nativeSelect() { } } -function nativeSelectorClassMap() { - let selector = new jpwRs.Selector(); - for (var i = 0; i < iter; i++) { - let _ = selector.path(path).value(jsonStr).map((v) => v).get(); - } -} - function wasmSelector() { let selector = jpw.selector(getJson()); for (var i = 0; i < iter; i++) { @@ -92,61 +85,18 @@ function wasmCompile() { } } -function wasmCompileAlloc() { - let ptr = jpw.allocJson(getJson()); - if (ptr == 0) { - console.error('Invalid pointer'); - return; - } - - try { - let template = jpw.compile(path); - for (var i = 0; i < iter; i++) { - let _ = template(ptr); - } - } finally { - jpw.deallocJson(ptr); - } -} - function wasmSelect() { for (var i = 0; i < iter; i++) { let _ = jpw.select(getJson(), path); } } -function wasmSelectAlloc() { - let ptr = jpw.allocJson(getJson()); - if (ptr == 0) { - console.error('Invalid pointer'); - return; - } - - try { - for (var i = 0; i < iter; i++) { - let _ = jpw.select(ptr, path); - } - } finally { - jpw.deallocJson(ptr); - } -} - function wasmSelectorClass() { let selector = new jpw.Selector(); for (var i = 0; i < iter; i++) { selector.path(path); selector.value(jsonStr); - let _ = selector.selectToStr(); - } -} - -function wasmSelectorClassMap() { - let selector = new jpw.Selector(); - for (var i = 0; i < iter; i++) { - selector.path(path); - selector.value(jsonStr); - let _1 = selector.map((v) => v); - let _2 = selector.get(); + let _ = selector.select(); } } diff --git a/build-wasm.sh b/build-wasm.sh new file mode 100755 index 0000000..fe7039a --- /dev/null +++ b/build-wasm.sh @@ -0,0 +1,104 @@ +#!/bin/bash + +set -e + +# project_root +DIR="$(pwd)" +WASM="${DIR}"/wasm +WASM_WWW="${WASM}"/www +WASM_WWW_BENCH="${WASM}"/www_bench +WASM_BROWSER_PKG="${WASM}"/browser_pkg +WASM_NODEJS_PKG="${WASM}"/nodejs_pkg +WASM_ALL_PKG="${WASM}"/all_pkg +WASM_TEST="${WASM}"/tests +DOCS="${DIR}"/docs +DOCS_BENCH="${DOCS}"/bench + +__msg () { + echo ">>>>>>>>>>$1<<<<<<<<<<" +} + +__cargo_clean () { + cd "${WASM}" && cargo clean && \ + cd "${DIR}" && cargo clean +} + +echo +__msg "clean wasm" +rm -rf \ + "${WASM_NODEJS_PKG}" \ + "${WASM_BROWSER_PKG}" \ + "${WASM_ALL_PKG}" \ + "${WASM_WWW}"/node_modules \ + "${WASM_WWW_BENCH}"/node_modules \ + "${WASM_WWW}"/dist \ + "${WASM_WWW_BENCH}"/dist \ + "${WASM_TEST}"/node_modules + +if [ "$1" = "all" ]; then + __msg "clean all wasm" + __cargo_clean +fi + +__msg "npm install: wasm" +cd "${WASM_WWW}" && npm install +__msg "npm install: wasm_bench" +cd "${WASM_WWW_BENCH}" && npm install +__msg "npm install: wasm test" +cd "${WASM_TEST}" && npm install + +echo +echo +__msg "wasm-pack" +cd "${WASM}" && \ + wasm-pack build --release --target=nodejs --out-dir "${WASM_NODEJS_PKG}" + +cd "${WASM}" && \ + wasm-pack build --release --target=browser --out-dir "${WASM_BROWSER_PKG}" +# && \ +# wasm-pack test --chrome --firefox --headless + +__msg "wasm npm packaging" +cp -r "${WASM_BROWSER_PKG}" "${WASM_ALL_PKG}/" && \ + sed "s/require[\(]'\.\/jsonpath_wasm_bg/require\('\.\/jsonpath_wasm_nodejs/" "${WASM_NODEJS_PKG}/jsonpath_wasm.js" \ + > "${WASM_ALL_PKG}/jsonpath_wasm_main.js" && \ + sed "s/require[\(]'\.\/jsonpath_wasm/require\('\.\/jsonpath_wasm_main/" "${WASM_NODEJS_PKG}/jsonpath_wasm_bg.js" \ + > "${WASM_ALL_PKG}/jsonpath_wasm_nodejs.js" && \ + jq ".files += [\"jsonpath_wasm_nodejs.js\"]" ${WASM_ALL_PKG}/package.json \ + | jq ".main = \"jsonpath_wasm_main.js\"" \ + | jq ".keywords += [\"jsonpath\", \"json\", \"webassembly\", \"parsing\", \"rust\"]" \ + > ${WASM_ALL_PKG}/temp.json && \ + mv -v "${WASM_ALL_PKG}/temp.json" "${WASM_ALL_PKG}/package.json" && \ + cd "${WASM_ALL_PKG}" && npm link + +echo +__msg "link" +cd "${WASM_WWW}" && \ + npm link jsonpath-wasm + +cd "${WASM_WWW_BENCH}" && \ + npm link jsonpath-wasm + +cd "${WASM_TEST}" && \ + npm link jsonpath-wasm + +echo +echo +__msg "wasm test" +cd "${WASM_TEST}" && npm test + +if [ "$1" = "all" ] || [ "$1" = "docs" ]; then + echo + __msg "docs" + cd "${WASM_WWW}" && \ + npm run build && + rm -f "${DOCS}"/*.js "${DOCS}"/*.wasm "${DOCS}"/*.html && \ + cp "${WASM_WWW}"/dist/*.* "${DOCS}"/ + + cd "${WASM_WWW_BENCH}" && \ + npm run build && + rm -f "${DOCS_BENCH}"/*.js "${DOCS_BENCH}"/*.wasm "${DOCS_BENCH}"/*.html && \ + cp "${WASM_WWW_BENCH}"/dist/*.* "${DOCS_BENCH}"/ +fi + +__msg "wasm done" \ No newline at end of file diff --git a/build.sh b/build.sh index 446fc36..70d0d7f 100755 --- a/build.sh +++ b/build.sh @@ -120,4 +120,4 @@ cd "${WASM_WWW_BENCH}" && \ rm -f "${DOCS_BENCH}"/*.js "${DOCS_BENCH}"/*.wasm "${DOCS_BENCH}"/*.html && \ cp "${WASM_WWW_BENCH}"/dist/*.* "${DOCS_BENCH}"/ -__msg "done" +__msg "done" \ No newline at end of file diff --git a/nodejs/lib/index.js b/nodejs/lib/index.js index 20032c7..bf962f7 100644 --- a/nodejs/lib/index.js +++ b/nodejs/lib/index.js @@ -1,4 +1,12 @@ -const { CompileFn, SelectorFn, selectStr, Selector: _Selector } = require('../native'); +const { + CompileFn, + SelectorFn, + selectStr, + deleteValue: _deleteValue, + replaceWith: _replaceWith, + Selector: _Selector, + SelectorMut: _SelectorMut +} = require('../native'); function compile(path) { let compile = new CompileFn(path); @@ -27,6 +35,30 @@ function select(json, path) { return JSON.parse(selectStr(json, path)); } +function deleteValue(json, path) { + if(typeof json != 'string') { + json = JSON.stringify(json) + } + return JSON.parse(_deleteValue(json, path)); +} + +function replaceWith(json, path, fun) { + if(typeof json != 'string') { + json = JSON.stringify(json) + } + let result = _replaceWith(json, path, (v) => { + let result = fun(JSON.parse(v)); + if(typeof result != 'string') { + result = JSON.stringify(result) + } + return result; + }); + if(typeof result == 'string') { + result = JSON.parse(result); + } + return result; +} + class Selector { constructor() { this._selector = new _Selector(); @@ -42,39 +74,67 @@ class Selector { if(typeof json != 'string') { json = JSON.stringify(json) } - this._selector.valueFromStr(json); + this._selector.value(json); return this; } - selectToStr() { - return this.selectAsStr(); + select() { + return JSON.parse(this._selector.select()); } +} - selectTo() { - return this.selectAs(); - } - - selectAsStr() { - return this._selector.selectAsStr(); - } - - selectAs() { - return JSON.parse(this.selectAsStr()); - } - - map(func) { - this._selector.map((json) => { - var result = func.call(null, JSON.parse(json)); - if(typeof result !== 'string') { - result = JSON.stringify(result); - } - return result; - }); +class SelectorMut { + constructor() { return this; } - get() { - return JSON.parse(this._selector.get()); + path(path) { + this._path = path; + return this; + } + + value(json) { + if(typeof json != 'string') { + json = JSON.stringify(json) + } + this._json = json; + return this; + } + + deleteValue() { + let selector = new _SelectorMut(); + if(!this._path) { + selector.emptyPathError(); + return; + } + + if(!this._json) { + selector.emptyValueError(); + return; + } + + this._json = deleteValue(this._json, this._path); + return this; + } + + replaceWith(fun) { + let selector = new _SelectorMut(); + if(!this._path) { + selector.emptyPathError(); + return; + } + if(!this._json) { + selector.emptyValueError(); + return; + } + this._json = replaceWith(this._json, this._path, fun); + return this; + } + + take() { + let json = this._json; + delete this._json; + return json; } } @@ -82,5 +142,8 @@ module.exports = { compile, selector, select, - Selector + deleteValue, + replaceWith, + Selector, + SelectorMut }; \ No newline at end of file diff --git a/nodejs/native/Cargo.toml b/nodejs/native/Cargo.toml index 2e6238a..2c52d00 100644 --- a/nodejs/native/Cargo.toml +++ b/nodejs/native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jsonpath4nodejs" -version = "0.1.3" +version = "0.2.0" authors = ["Changseok Han "] description = "jsonpath_lib bindings for nodejs" keywords = ["library", "jsonpath", "json", "nodejs"] @@ -14,7 +14,8 @@ exclude = ["artifacts.json", "index.node"] neon-build = "0.2.0" [dependencies] -jsonpath_lib = "0.1.13" +jsonpath_lib = "0.2.0" +#jsonpath_lib = { path = "../../" } neon = "0.2.0" neon-serde = "0.1.1" serde_json = { version = "1.0", features = ["preserve_order"] } diff --git a/nodejs/native/src/lib.rs b/nodejs/native/src/lib.rs index 09f5c72..eb77863 100644 --- a/nodejs/native/src/lib.rs +++ b/nodejs/native/src/lib.rs @@ -4,13 +4,9 @@ extern crate neon; extern crate neon_serde; extern crate serde_json; -use jsonpath::filter::value_filter::JsonValueFilter; -use jsonpath::parser::parser::{Node, NodeVisitor, Parser}; -use jsonpath::ref_value::model::{RefValue, RefValueWrapper}; -use jsonpath::Selector; +use jsonpath::{JsonPathError, Node, Parser, Selector}; use neon::prelude::*; use serde_json::Value; -use std::ops::Deref; /// /// `neon_serde::from_value` has very poor performance. @@ -35,96 +31,176 @@ fn select_str(mut ctx: FunctionContext) -> JsResult { } } -pub struct CompileFn { - node: Node +fn delete(mut ctx: FunctionContext) -> JsResult { + let json_val = ctx.argument::(0)?.value(); + let json: Value = match serde_json::from_str(&json_val) { + Ok(value) => value, + Err(e) => panic!("{:?}", JsonPathError::Serde(e.to_string())) + }; + let path = ctx.argument::(1)?.value(); + match jsonpath::delete(json, &path) { + Ok(value) => Ok(JsString::new(&mut ctx, match serde_json::to_string(&value) { + Ok(value) => value, + Err(e) => panic!("{:?}", JsonPathError::Serde(e.to_string())) + }).upcast()), + Err(e) => panic!("{:?}", e) + } } -pub struct SelectorFn { - json: RefValueWrapper +fn replace_with(mut ctx: FunctionContext) -> JsResult { + let json_val = ctx.argument::(0)?.value(); + let json: Value = match serde_json::from_str(&json_val) { + Ok(value) => value, + Err(e) => panic!("{:?}", JsonPathError::Serde(e.to_string())) + }; + let path = ctx.argument::(1)?.value(); + let fun = ctx.argument::(2)?; + match jsonpath::replace_with(json, &path, &mut |v| { + let json_str = JsString::new(&mut ctx, match serde_json::to_string(v) { + Ok(value) => value, + Err(e) => panic!("{:?}", JsonPathError::Serde(e.to_string())) + }); + + let null = ctx.null(); + let args = vec![ctx.string(json_str.value())]; + let result = match fun.call(&mut ctx, null, args) { + Ok(result) => result, + Err(e) => panic!("{:?}", e) + }; + let json_str = match result.downcast::() { + Ok(v) => v.value(), + Err(e) => panic!("{:?}", JsonPathError::Serde(e.to_string())) + }; + match serde_json::from_str(&json_str) { + Ok(v) => v, + Err(e) => panic!("{:?}", JsonPathError::Serde(e.to_string())) + } + }) { + Ok(value) => Ok(JsString::new(&mut ctx, match serde_json::to_string(&value) { + Ok(value) => value, + Err(e) => panic!("{:?}", JsonPathError::Serde(e.to_string())) + }).upcast()), + Err(e) => panic!("{:?}", e) + } } pub struct SelectorCls { - selector: Selector + node: Option, + value: Option, } +impl SelectorCls { + fn path(&mut self, path: &str) { + let node = match Parser::compile(path) { + Ok(node) => node, + Err(e) => panic!("{:?}", e) + }; + + self.node = Some(node); + } + + fn value(&mut self, json_str: &str) { + let value: Value = match serde_json::from_str(&json_str) { + Ok(value) => value, + Err(e) => panic!("{:?}", JsonPathError::Serde(e.to_string())) + }; + + self.value = Some(value); + } + + fn select(&self) -> String { + let node = match &self.node { + Some(node) => node, + None => panic!("{:?}", JsonPathError::EmptyPath) + }; + + let value = match &self.value { + Some(value) => value, + None => panic!("{:?}", JsonPathError::EmptyValue) + }; + + let mut selector = Selector::new(); + selector.compiled_path(node); + selector.value(&value); + match selector.select_as_str() { + Ok(ret) => ret, + Err(e) => panic!("{:?}", e) + } + } +} + +pub struct SelectorMutCls {} + declare_types! { - pub class JsCompileFn for CompileFn { + pub class JsCompileFn for SelectorCls { init(mut ctx) { let path = ctx.argument::(0)?.value(); - let mut parser = Parser::new(path.as_str()); - - let node = match parser.compile() { + let node = match Parser::compile(path.as_str()) { Ok(node) => node, Err(e) => panic!("{:?}", e) }; - Ok(CompileFn { node }) + Ok(SelectorCls { node: Some(node), value: None }) } method template(mut ctx) { - let this = ctx.this(); - - let node = { - let guard = ctx.lock(); - let this = this.borrow(&guard); - this.node.clone() - }; + let mut this = ctx.this(); let json_str = ctx.argument::(0)?.value(); - let ref_value: RefValue = match serde_json::from_str(&json_str) { - Ok(ref_value) => ref_value, - Err(e) => panic!("{:?}", e) + { + let guard = ctx.lock(); + let mut this = this.borrow_mut(&guard); + let value: Value = match serde_json::from_str(&json_str) { + Ok(value) => value, + Err(e) => panic!("{:?}", JsonPathError::Serde(e.to_string())) + }; + this.value = Some(value); }; - let mut jf = JsonValueFilter::new_from_value(ref_value.into()); - jf.visit(node); - match serde_json::to_string(&jf.take_value().deref()) { - Ok(json_str) => Ok(JsString::new(&mut ctx, &json_str).upcast()), - Err(e) => panic!("{:?}", e) - } + let result_str = { + let guard = ctx.lock(); + let this = this.borrow(&guard); + this.select() + }; + + Ok(JsString::new(&mut ctx, &result_str).upcast()) } } - pub class JsSelectorFn for SelectorFn { + pub class JsSelectorFn for SelectorCls { init(mut ctx) { let json_str = ctx.argument::(0)?.value(); - let ref_value: RefValue = match serde_json::from_str(&json_str) { - Ok(ref_value) => ref_value, - Err(e) => panic!("{:?}", e) + let value: Value = match serde_json::from_str(&json_str) { + Ok(value) => value, + Err(e) => panic!("{:?}", JsonPathError::Serde(e.to_string())) }; - Ok(SelectorFn { json: ref_value.into() }) + Ok(SelectorCls { node: None, value: Some(value) }) } method select(mut ctx) { - let this = ctx.this(); - - let json = { - let guard = ctx.lock(); - let this = this.borrow(&guard); - this.json.clone() - }; + let mut this = ctx.this(); let path = ctx.argument::(0)?.value(); - let mut parser = Parser::new(path.as_str()); + { + let guard = ctx.lock(); + let mut this = this.borrow_mut(&guard); + this.path(&path); + } - let node = match parser.compile() { - Ok(node) => node, - Err(e) => panic!("{:?}", e) + let result_str = { + let guard = ctx.lock(); + let this = this.borrow(&guard); + this.select() }; - let mut jf = JsonValueFilter::new_from_value(json); - jf.visit(node); - match serde_json::to_string(&jf.take_value().deref()) { - Ok(json_str) => Ok(JsString::new(&mut ctx, &json_str).upcast()), - Err(e) => panic!("{:?}", e) - } + Ok(JsString::new(&mut ctx, &result_str).upcast()) } } pub class JsSelector for SelectorCls { init(mut _ctx) { - Ok(SelectorCls { selector: Selector::new() }) + Ok(SelectorCls { node: None, value: None }) } method path(mut ctx) { @@ -134,80 +210,49 @@ declare_types! { { let guard = ctx.lock(); let mut this = this.borrow_mut(&guard); - let _ = this.selector.path(&path); + let _ = this.path(&path); } + Ok(JsUndefined::new().upcast()) } - method valueFromStr(mut ctx) { + method value(mut ctx) { let mut this = ctx.this(); let json_str = ctx.argument::(0)?.value(); { let guard = ctx.lock(); let mut this = this.borrow_mut(&guard); - let _ = this.selector.value_from_str(&json_str); + let _ = this.value(&json_str); } + Ok(JsUndefined::new().upcast()) } - method selectAsStr(mut ctx) { - let mut this = ctx.this(); + method select(mut ctx) { + let this = ctx.this(); - let result = { + let result_str = { let guard = ctx.lock(); - let this = this.borrow_mut(&guard); - this.selector.select_as_str() + let this = this.borrow(&guard); + this.select() }; - match result { - Ok(json_str) => Ok(JsString::new(&mut ctx, &json_str).upcast()), - Err(e) => panic!("{:?}", e) - } + Ok(JsString::new(&mut ctx, &result_str).upcast()) + } + } + + pub class JsSelectorMut for SelectorMutCls { + init(mut _ctx) { + Ok(SelectorMutCls {}) } - method map(mut ctx) { - let null = ctx.null(); - let mut this = ctx.this(); - - let func = ctx.argument::(0)?; - - let value = { - let guard = ctx.lock(); - let this = this.borrow_mut(&guard); - match this.selector.select_as_str() { - Ok(v) => v, - Err(e) => panic!("{:?}", e) - } - }; - - let js_value = JsString::new(&mut ctx, &value); - let json_str = func.call(&mut ctx, null, vec![js_value])? - .downcast::() - .or_throw(&mut ctx)? - .value(); - { - let guard = ctx.lock(); - let mut this = this.borrow_mut(&guard); - let _ = this.selector.value_from_str(&json_str); - } - - Ok(JsUndefined::new().upcast()) + method emptyPathError(mut _ctx) { + panic!("{:?}", JsonPathError::EmptyPath); } - method get(mut ctx) { - let mut this = ctx.this(); - - let result = { - let guard = ctx.lock(); - let this = this.borrow_mut(&guard); - match this.selector.get() { - Ok(v) => v, - Err(e) => panic!("{:?}", e) - } - }; - - Ok(JsString::new(&mut ctx, &result.to_string()).upcast()) + method emptyValueError(mut _ctx) { + panic!("{:?}", JsonPathError::EmptyValue); } } } @@ -215,7 +260,10 @@ register_module!(mut m, { m.export_class::("CompileFn").expect("CompileFn class error"); m.export_class::("SelectorFn").expect("SelectorFn class error"); m.export_class::("Selector").expect("Selector class error"); + m.export_class::("SelectorMut").expect("SelectorMut class error"); m.export_function("select", select)?; + m.export_function("deleteValue", delete)?; + m.export_function("replaceWith", replace_with)?; m.export_function("selectStr", select_str)?; Ok(()) }); \ No newline at end of file diff --git a/nodejs/test/index.spec.js b/nodejs/test/index.spec.js index 99ee6e8..c948aae 100644 --- a/nodejs/test/index.spec.js +++ b/nodejs/test/index.spec.js @@ -359,7 +359,7 @@ describe('compile test', () => { it('basic', (done) => { let template = jsonpath.compile('$.a'); let result = template({'a': 1}); - if (result === 1) { + if (result[0] === 1) { done(); } }); @@ -369,7 +369,7 @@ describe('selector test', () => { it('basic', (done) => { let selector = jsonpath.selector({'a': 1}); let result = selector('$.a'); - if (result === 1) { + if (result[0] === 1) { done(); } }); @@ -378,7 +378,7 @@ describe('selector test', () => { describe('select test', () => { it('basic', (done) => { let result = jsonpath.select({'a': 1}, '$.a'); - if (result === 1) { + if (result[0] === 1) { done(); } }); @@ -400,25 +400,105 @@ describe('filter test', () => { } }); +describe('SelectorMut test', () => { + it('delete', (done) => { + let jsonObjNew = JSON.parse(JSON.stringify(jsonObj)); + let result = jsonpath.deleteValue(jsonObjNew, '$.store.book'); + if (JSON.stringify(result) === JSON.stringify({ + 'store': { + 'book': null, + 'bicycle': { + 'color': 'red', + 'price': 19.95, + }, + }, + 'expensive': 10, + })) { + done(); + } + }); + + it('replaceWith', (done) => { + let jsonObjNew = JSON.parse(JSON.stringify(jsonObj)); + let result = jsonpath.replaceWith(jsonObjNew, '$.store.book', (v) => { + let ret = v[0]; + ret.price = 9; + return ret; + }); + if (JSON.stringify(result) === JSON.stringify({ + 'store': { + 'book': { + 'category': 'reference', + 'author': 'Nigel Rees', + 'title': 'Sayings of the Century', + 'price': 9, + }, + 'bicycle': { + 'color': 'red', + 'price': 19.95, + }, + }, + 'expensive': 10, + })) { + done(); + } + }); + + it('SeletorMut delete', (done) => { + let jsonObjNew = JSON.parse(JSON.stringify(jsonObj)); + let selector = new jsonpath.SelectorMut(); + selector.path('$.store.book').value(jsonObjNew).deleteValue(); + + let result = selector.take(); + if (JSON.stringify(result) === JSON.stringify({ + 'store': { + 'book': null, + 'bicycle': { + 'color': 'red', + 'price': 19.95, + }, + }, + 'expensive': 10, + })) { + done(); + } + }); + + it('SeletorMut replaceWith', (done) => { + let jsonObjNew = JSON.parse(JSON.stringify(jsonObj)); + let selector = new jsonpath.SelectorMut(); + selector.path('$.store.book').value(jsonObjNew).replaceWith((v) => { + let ret = v[0]; + ret.price = 9; + return ret; + }); + + let result = selector.take(); + if (JSON.stringify(result) === JSON.stringify({ + 'store': { + 'book': { + 'category': 'reference', + 'author': 'Nigel Rees', + 'title': 'Sayings of the Century', + 'price': 9, + }, + 'bicycle': { + 'color': 'red', + 'price': 19.95, + }, + }, + 'expensive': 10, + })) { + done(); + } + }); +}); + describe('Selector test', () => { - it('basic selectTo', (done) => { - let result = new jsonpath.Selector().path('$.a').value({'a': 1}).selectTo(); - if (result === 1) { - done(); - } - }); - - it('basic selectToStr', (done) => { - let result = new jsonpath.Selector().path('$.a').value({'a': 1}).selectToStr(); - if (result === '1') { - done(); - } - }); - it('select', (done) => { let selector = new jsonpath.Selector().value(jsonObj); for(var i in list) { - if(JSON.stringify(list[i]) !== selector.path(i).selectToStr()) { + if(JSON.stringify(list[i]) !== JSON.stringify(selector.path(i).select())) { throw `fail: ${i}`; } } @@ -444,7 +524,7 @@ describe('README test', () => { let selector = new jsonpath.Selector().value(jsonObj); { - let jsonObj = selector.path('$..[?(@.age >= 30)]').selectAs(); + let jsonObj = selector.path('$..[?(@.age >= 30)]').select(); let resultObj = [{"name": "친구3", "age": 30}]; if(JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) { throw 'jsonpath.Selector: $..[?(@.age >= 30)]'; @@ -452,7 +532,7 @@ describe('README test', () => { } { - let jsonObj = selector.path('$..[?(@.age == 20)]').selectAs(); + let jsonObj = selector.path('$..[?(@.age == 20)]').select(); let resultObj = [{"name": "친구1", "age": 20}, {"name": "친구2", "age": 20}]; if(JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) { throw 'jsonpath.Selector: $..[?(@.age >= 20)]'; @@ -460,29 +540,68 @@ describe('README test', () => { } { - let jsonObj = selector.value({"friends": [ {"name": "친구5", "age": 20} ]}).selectAs(); + let jsonObj = selector.value({"friends": [ {"name": "친구5", "age": 20} ]}).select(); let resultObj = [{"name": "친구5", "age": 20}]; if(JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) { throw 'jsonpath.Selector: change value'; } } + done(); + }); + + it('jsonpath.SelectorMut', (done) => { + let jsonObj = { + 'school': { + 'friends': [ + {'name': '친구1', 'age': 20}, + {'name': '친구2', 'age': 20}, + ], + }, + 'friends': [ + {'name': '친구3', 'age': 30}, + {'name': '친구4'}, + ], + }; + + let selector = new jsonpath.SelectorMut(); + selector.path('$..[?(@.age == 20)]'); + { - let jsonObj1 = selector.value(jsonObj).map(function(v) { - let f1 = v[0]; - f1.age = 30; - return v; - }).get(); + selector.value(jsonObj).deleteValue(); - let resultObj1 = [{"name": "친구1", "age": 30}, {"name": "친구2", "age": 20}]; - if(JSON.stringify(jsonObj1) !== JSON.stringify(resultObj1)) { - throw 'jsonpath.Selector.map'; + let resultObj = { + 'school': {'friends': [null, null]}, + 'friends': [ + {'name': '친구3', 'age': 30}, + {'name': '친구4'}, + ], + }; + if (JSON.stringify(selector.take()) !== JSON.stringify(resultObj)) { + throw 'jsonpath.SelectorMut.deleteValue'; } + } - let jsonObj2 = selector.path('$..[?(@.age == 20)]').selectAs(); - let resultObj2 = [{"name": "친구2", "age": 20}]; - if(JSON.stringify(jsonObj2) !== JSON.stringify(resultObj2)) { - throw 'jsonpath.Selector.map and then select'; + { + selector.value(jsonObj).replaceWith((v) => { + v.age = v.age * 2; + return v; + }); + + let resultObj = { + 'school': { + 'friends': [ + {'name': '친구1', 'age': 40}, + {'name': '친구2', 'age': 40}, + ], + }, + 'friends': [ + {'name': '친구3', 'age': 30}, + {'name': '친구4'}, + ], + }; + if (JSON.stringify(selector.take()) !== JSON.stringify(resultObj)) { + throw 'jsonpath.SelectorMut.replaceWith'; } } @@ -618,4 +737,64 @@ describe('README test', () => { done(); }); + + it('jsonpath.deleteValue(json: string|object, path: string)', (done) => { + let jsonObj = { + "school": { + "friends": [ + {"name": "친구1", "age": 20}, + {"name": "친구2", "age": 20} + ] + }, + "friends": [ + {"name": "친구3", "age": 30}, + {"name": "친구4"} + ] + }; + + let _1 = jsonpath.deleteValue(jsonObj, '$..friends[0]'); + let result = jsonpath.deleteValue(_1, '$..friends[1]'); + + if(JSON.stringify(result) === JSON.stringify({ + "school": { "friends": [null, null]}, + "friends": [null, null] + })) { + done(); + } + }); + + it('jsonpath.replaceWith(json: string|object, path: string, fun: function(json: object) => json: object', (done) => { + let jsonObj = { + "school": { + "friends": [ + {"name": "친구1", "age": 20}, + {"name": "친구2", "age": 20} + ] + }, + "friends": [ + {"name": "친구3", "age": 30}, + {"name": "친구4"} + ] + }; + + let result = jsonpath.replaceWith(jsonObj, '$..friends[0]', (v) => { + v.age = v.age * 2; + return v; + }); + + if(JSON.stringify(result) === JSON.stringify({ + "school": { + "friends": [ + {"name": "친구1", "age": 40}, + {"name": "친구2", "age": 20} + ] + }, + "friends": [ + {"name": "친구3", "age": 60}, + {"name": "친구4"} + ] + })) { + done(); + } + }); }); \ No newline at end of file diff --git a/profiling.sh b/profiling.sh new file mode 100755 index 0000000..c262a73 --- /dev/null +++ b/profiling.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -e + +valgrind \ + --tool=callgrind \ + --dump-instr=yes \ + --collect-jumps=yes \ + --simulate-cache=yes $1 -- $2 \ No newline at end of file diff --git a/src/filter/cmp.rs b/src/filter/cmp.rs deleted file mode 100644 index a83ee16..0000000 --- a/src/filter/cmp.rs +++ /dev/null @@ -1,191 +0,0 @@ -#[derive(Debug)] -pub enum CmpType { - Eq, - Ne, - Gt, - Ge, - Lt, - Le, -} - -#[derive(Debug)] -pub enum CmpCondType { - And, - Or, -} - -pub trait PrivCmp { - fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool; - - fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool; - - fn cmp_string(&self, v1: &String, v2: &String) -> bool; -} - -pub trait IntoType { - fn into_type(&self) -> CmpType; -} - -pub struct CmpEq; - -impl PrivCmp for CmpEq { - fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { - v1 == v2 - } - - fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { - v1 == v2 - } - - fn cmp_string(&self, v1: &String, v2: &String) -> bool { - v1 == v2 - } -} - -impl IntoType for CmpEq { - fn into_type(&self) -> CmpType { - CmpType::Eq - } -} - -pub struct CmpNe; - -impl PrivCmp for CmpNe { - fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { - v1 != v2 - } - - fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { - v1 != v2 - } - - fn cmp_string(&self, v1: &String, v2: &String) -> bool { - v1 != v2 - } -} - -impl IntoType for CmpNe { - fn into_type(&self) -> CmpType { - CmpType::Ne - } -} - -pub struct CmpGt; - -impl PrivCmp for CmpGt { - fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { - v1 > v2 - } - - fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { - v1 > v2 - } - - fn cmp_string(&self, v1: &String, v2: &String) -> bool { - v1 > v2 - } -} - -impl IntoType for CmpGt { - fn into_type(&self) -> CmpType { - CmpType::Gt - } -} - -pub struct CmpGe; - -impl PrivCmp for CmpGe { - fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { - v1 >= v2 - } - - fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { - v1 >= v2 - } - - fn cmp_string(&self, v1: &String, v2: &String) -> bool { - v1 >= v2 - } -} - -impl IntoType for CmpGe { - fn into_type(&self) -> CmpType { - CmpType::Ge - } -} - -pub struct CmpLt; - -impl PrivCmp for CmpLt { - fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { - v1 < v2 - } - - fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { - v1 < v2 - } - - fn cmp_string(&self, v1: &String, v2: &String) -> bool { - v1 < v2 - } -} - -impl IntoType for CmpLt { - fn into_type(&self) -> CmpType { - CmpType::Lt - } -} - -pub struct CmpLe; - -impl PrivCmp for CmpLe { - fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { - v1 <= v2 - } - - fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { - v1 <= v2 - } - - fn cmp_string(&self, v1: &String, v2: &String) -> bool { - v1 <= v2 - } -} - -impl IntoType for CmpLe { - fn into_type(&self) -> CmpType { - CmpType::Le - } -} - -pub struct CmpAnd; - -impl PrivCmp for CmpAnd { - fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { - *v1 && *v2 - } - - fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { - v1 > &0_f64 && v2 > &0_f64 - } - - fn cmp_string(&self, v1: &String, v2: &String) -> bool { - !v1.is_empty() && !v2.is_empty() - } -} - -pub struct CmpOr; - -impl PrivCmp for CmpOr { - fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { - *v1 || *v2 - } - - fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { - v1 > &0_f64 || v2 > &0_f64 - } - - fn cmp_string(&self, v1: &String, v2: &String) -> bool { - !v1.is_empty() || !v2.is_empty() - } -} \ No newline at end of file diff --git a/src/filter/mod.rs b/src/filter/mod.rs deleted file mode 100644 index b3b7581..0000000 --- a/src/filter/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod cmp; -mod term; -pub mod value_filter; -pub mod value_wrapper; \ No newline at end of file diff --git a/src/filter/term.rs b/src/filter/term.rs deleted file mode 100644 index 8a1571f..0000000 --- a/src/filter/term.rs +++ /dev/null @@ -1,154 +0,0 @@ -use super::cmp::*; -use super::value_filter::ValueFilterKey; -use super::value_wrapper::*; - -#[derive(Debug)] -pub enum TermContext { - Constants(ExprTerm), - Json(Option, ValueWrapper), -} - -impl TermContext { - fn cmp(&self, other: &TermContext, cmp_fn: F, default: bool) -> TermContext { - match self { - TermContext::Constants(et) => { - match other { - TermContext::Constants(oet) => { - trace!("const-const"); - TermContext::Constants(ExprTerm::Bool(et.cmp(oet, cmp_fn, default))) - } - TermContext::Json(key, v) => { - trace!("const-json"); - TermContext::Json(None, v.take_with(key, et, cmp_fn, true)) - } - } - } - TermContext::Json(key, v) => { - match other { - TermContext::Json(key_other, ov) => { - trace!("json-json"); - - fn is_json(t: &TermContext) -> bool { - match t { - TermContext::Json(_, _) => true, - _ => false - } - } - - let c = v.into_term(key); - let oc = ov.into_term(key_other); - if is_json(&c) && is_json(&oc) { - v.cmp(&ov, cmp_fn.into_type()) - } else { - c.cmp(&oc, cmp_fn, default) - } - } - TermContext::Constants(et) => { - trace!("json-const"); - TermContext::Json(None, v.take_with(key, et, cmp_fn, false)) - } - } - } - } - } - - fn cmp_cond(&self, other: &TermContext, cmp_cond_type: CmpCondType) -> TermContext { - match self { - TermContext::Constants(et) => { - match other { - TermContext::Constants(oet) => { - match cmp_cond_type { - CmpCondType::Or => { - TermContext::Constants(ExprTerm::Bool(et.cmp(oet, CmpOr, false))) - } - CmpCondType::And => { - TermContext::Constants(ExprTerm::Bool(et.cmp(oet, CmpAnd, false))) - } - } - } - TermContext::Json(_, v) => { - TermContext::Json(None, ValueWrapper::new(v.get_val().clone(), false)) - } - } - } - TermContext::Json(_, v) => { - match other { - TermContext::Json(_, ov) => { - match cmp_cond_type { - CmpCondType::Or => TermContext::Json(None, v.union(ov)), - CmpCondType::And => TermContext::Json(None, v.intersect(ov)), - } - } - _ => { - TermContext::Json(None, ValueWrapper::new(v.get_val().clone(), false)) - } - } - } - } - } - - pub fn eq(&self, other: &TermContext) -> TermContext { - trace!("eq"); - self.cmp(other, CmpEq, false) - } - - pub fn ne(&self, other: &TermContext) -> TermContext { - trace!("ne"); - self.cmp(other, CmpNe, true) - } - - pub fn gt(&self, other: &TermContext) -> TermContext { - trace!("gt"); - self.cmp(other, CmpGt, false) - } - - pub fn ge(&self, other: &TermContext) -> TermContext { - trace!("ge"); - self.cmp(other, CmpGe, false) - } - - pub fn lt(&self, other: &TermContext) -> TermContext { - trace!("lt"); - self.cmp(other, CmpLt, false) - } - - pub fn le(&self, other: &TermContext) -> TermContext { - trace!("le"); - self.cmp(other, CmpLe, false) - } - - pub fn and(&self, other: &TermContext) -> TermContext { - self.cmp_cond(other, CmpCondType::And) - } - - pub fn or(&self, other: &TermContext) -> TermContext { - self.cmp_cond(other, CmpCondType::Or) - } -} - - -#[derive(Debug)] -pub enum ExprTerm { - String(String), - Number(f64), - Bool(bool), -} - -impl ExprTerm { - fn cmp(&self, other: &ExprTerm, cmp_fn: F, default: bool) -> bool { - match self { - ExprTerm::Bool(v1) => match other { - ExprTerm::Bool(v2) => cmp_fn.cmp_bool(v1, v2), - _ => default - } - ExprTerm::Number(v1) => match other { - ExprTerm::Number(v2) => cmp_fn.cmp_f64(v1, v2), - _ => default - } - ExprTerm::String(v1) => match other { - ExprTerm::String(v2) => cmp_fn.cmp_string(v1, v2), - _ => default - } - } - } -} diff --git a/src/filter/value_filter.rs b/src/filter/value_filter.rs deleted file mode 100644 index fb8c82b..0000000 --- a/src/filter/value_filter.rs +++ /dev/null @@ -1,640 +0,0 @@ -use std::error::Error; -use std::ops::Deref; -use std::result::Result; - -use serde_json::Value; - -use filter::term::*; -use filter::value_wrapper::*; -use parser::parser::{FilterToken, NodeVisitor, ParseToken}; -use ref_value::model::*; - -trait ArrayIndex { - fn index(&self, v: &RefValueWrapper) -> usize; - - fn take_value(&self, v: &RefValueWrapper) -> RefValueWrapper { - let idx = self.index(v); - match v.get(idx) { - Some(v) => v.clone(), - _ => RefValue::Null.into() - } - } -} - -impl ArrayIndex for f64 { - fn index(&self, v: &RefValueWrapper) -> usize { - if v.is_array() && self < &0_f64 { - (v.as_array().unwrap().len() as f64 + self) as usize - } else { - *self as usize - } - } -} - -impl ArrayIndex for isize { - fn index(&self, v: &RefValueWrapper) -> usize { - if v.is_array() && self < &0_isize { - (v.as_array().unwrap().len() as isize + self) as usize - } else { - *self as usize - } - } -} - -impl ArrayIndex for usize { - fn index(&self, _: &RefValueWrapper) -> usize { - *self as usize - } -} - -#[derive(Debug, Clone)] -pub enum ValueFilterKey { - Num(usize), - String(String), - All, -} - -fn iter_to_value_vec<'a, I: Iterator>(iter: I) -> Vec { - iter - .map(|v| v.clone()) - .filter(|v| !v.is_null()) - .collect() -} - -fn get_nested_array(v: &RefValueWrapper, key: F, filter_mode: bool) -> RefValueWrapper { - if v.is_array() && v.as_array().unwrap().get(key.index(v)).is_some() { - if filter_mode { - v.clone() - } else { - let idx = key.index(v); - v.get(idx).unwrap().clone() - } - } else { - key.take_value(v) - } -} - -fn get_nested_object(v: &RefValueWrapper, key: &String, filter_mode: bool) -> RefValueWrapper { - if v.is_object() && v.as_object().unwrap().contains_key(key) { - if filter_mode { - v.clone() - } else { - v.get(key.clone()).unwrap().clone() - } - } else { - RefValue::Null.into() - } -} - -fn traverse(key: Option<&String>, v: &RefValueWrapper, buf: &mut Vec) { - match v.deref() { - RefValue::Array(vec) => { - if key.is_none() { - for v in vec { - buf.push(v.clone()); - } - } - for i in vec { - traverse(key, i, buf); - } - } - RefValue::Object(v) => { - for (k, v) in v.into_iter() { - if match key { - Some(map_key) => map_key == k, - _ => true - } { - buf.push(v.clone()); - } - } - for (_, v) in v.into_iter() { - traverse(key, v, buf); - } - } - _ => {} - } -} - -fn collect_all(key: Option<&String>, v: &RefValueWrapper) -> Vec { - let mut buf = Vec::new(); - traverse(key, v, &mut buf); - buf -} - -#[derive(Debug)] -pub struct ValueFilter { - val_wrapper: ValueWrapper, - last_key: Option, - filter_mode: bool, -} - -impl ValueFilter { - pub fn new(v: RefValueWrapper, is_leaves: bool, filter_mode: bool) -> Self { - ValueFilter { val_wrapper: ValueWrapper::new(v, is_leaves), last_key: None, filter_mode } - } - - fn step_leaves(&mut self, key: Option<&String>) { - let buf = collect_all(key, &self.val_wrapper.get_val()); - trace!("step_leaves - {:?}", buf); - self.val_wrapper = ValueWrapper::new(RefValue::Array(buf).into(), true); - } - - pub fn step_leaves_all(&mut self) -> &ValueWrapper { - debug!("step_leaves_all"); - self.step_leaves(None); - self.last_key = Some(ValueFilterKey::All); - &self.val_wrapper - } - - pub fn step_leaves_str(&mut self, key: &str) -> &ValueWrapper { - self.step_leaves_string(&key.to_string()) - } - - pub fn step_leaves_string(&mut self, key: &String) -> &ValueWrapper { - debug!("step_leaves_string"); - self.step_leaves(Some(key)); - self.last_key = Some(ValueFilterKey::String(key.clone())); - &self.val_wrapper - } - - pub fn step_in_all(&mut self) -> &ValueWrapper { - debug!("step_in_all"); - - let vec = match self.val_wrapper.get_val().deref() { - RefValue::Object(ref map) => { - iter_to_value_vec(map.values()) - } - RefValue::Array(ref list) => { - iter_to_value_vec(list.iter()) - } - RefValue::Null => Vec::new(), - _ => vec![self.val_wrapper.get_val().clone()] - }; - - self.last_key = Some(ValueFilterKey::All); - self.val_wrapper.replace(RefValue::Array(vec).into()); - trace!("step_in_all - {:?}", self.val_wrapper.get_val()); - &self.val_wrapper - } - - pub fn step_in_num(&mut self, key: &f64) -> &ValueWrapper { - debug!("step_in_num"); - trace!("step_in_num - before: leaves {}, filterMode {} - {:?}" - , self.val_wrapper.is_leaves() - , self.filter_mode - , self.val_wrapper.get_val()); - - let v = if self.val_wrapper.is_leaves() { - let filter_mode = self.filter_mode; - match self.val_wrapper.get_val().deref() { - RefValue::Array(ref vec) => { - let mut ret = Vec::new(); - for v in vec { - let wrapper = get_nested_array(v, *key, filter_mode); - if !wrapper.is_null() { - ret.push(wrapper.clone()); - } - } - RefValue::Array(ret).into() - } - _ => key.take_value(&self.val_wrapper.get_val()) - } - } else { - key.take_value(&self.val_wrapper.get_val()) - }; - - self.last_key = Some(ValueFilterKey::Num(key.index(&v))); - self.val_wrapper.replace(v); - trace!("step_in_num - after: {:?}", self.val_wrapper.get_val()); - &self.val_wrapper - } - - pub fn step_in_str(&mut self, key: &str) -> &ValueWrapper { - self.step_in_string(&key.to_string()) - } - - pub fn step_in_string(&mut self, key: &String) -> &ValueWrapper { - debug!("step_in_string"); - trace!("step_in_string - before: {},{},{:?}" - , self.val_wrapper.is_leaves() - , self.filter_mode - , self.val_wrapper.get_val()); - - let filter_mode = self.filter_mode; - let is_leaves = self.val_wrapper.is_leaves(); - let val = match self.val_wrapper.get_val().deref() { - RefValue::Array(ref vec) if is_leaves => { - let mut buf = Vec::new(); - for v in vec { - if v.is_array() { - let vec = v.as_array().unwrap(); - let mut ret = Vec::new(); - for v in vec { - let nested_wrapper = get_nested_object(v, key, filter_mode); - if !nested_wrapper.is_null() { - ret.push(nested_wrapper); - } - } - buf.append(&mut ret); - } else if v.is_object() { - let nested_wrapper = get_nested_object(v, key, filter_mode); - if !nested_wrapper.is_null() { - buf.push(nested_wrapper); - } - } else { - match v.get(key.clone()) { - Some(v) => buf.push(v.clone()), - _ => {} - } - } - } - - RefValue::Array(buf).into() - } - RefValue::Array(ref vec) if !is_leaves => { - let mut ret = Vec::new(); - for v in vec { - let wrapper = get_nested_object(v, key, filter_mode); - if !wrapper.is_null() { - ret.push(wrapper); - } - } - RefValue::Array(ret).into() - } - _ => { - match self.val_wrapper.get_val().get(key.clone()) { - Some(v) => v.clone(), - _ => RefValue::Null.into() - } - } - }; - - self.last_key = Some(ValueFilterKey::String(key.clone())); - self.val_wrapper.replace(val); - trace!("step_in_string - after: {},{},{:?}" - , self.val_wrapper.is_leaves() - , self.filter_mode - , self.val_wrapper.get_val()); - &self.val_wrapper - } -} - -pub struct JsonValueFilter { - json: RefValueWrapper, - filter_stack: Vec, - token_stack: Vec, - term_stack: Vec, -} - -impl JsonValueFilter { - pub fn new(json: &str) -> Result { - let json: RefValue = serde_json::from_str(json) - .map_err(|e| e.description().to_string())?; - Ok(JsonValueFilter::new_from_value(json.into())) - } - - pub fn new_from_value(json: RefValueWrapper) -> Self { - JsonValueFilter { - json, - filter_stack: Vec::new(), - token_stack: Vec::new(), - term_stack: Vec::new(), - } - } - - fn is_peek_token_array(&self) -> bool { - if let Some(ParseToken::Array) = self.token_stack.last() { - true - } else { - false - } - } - - fn push_value_filter(&mut self, from_current: bool) { - if from_current { - self.filter_stack.last() - .map(|vf| { - ValueFilter::new(vf.val_wrapper.get_val().clone(), vf.val_wrapper.is_leaves(), from_current) - }) - .and_then(|vf| { - Some(self.filter_stack.push(vf)) - }); - } else { - self.filter_stack.push({ - ValueFilter::new(self.json.clone(), false, from_current) - }); - } - } - - fn replace_filter_stack(&mut self, v: RefValueWrapper, is_leaves: bool) { - if self.filter_stack.is_empty() { - self.filter_stack.push(ValueFilter::new(v, is_leaves, false)); - } else { - match self.filter_stack.last_mut() { - Some(vf) => { - vf.val_wrapper.set_leaves(is_leaves); - if v.is_null() { - vf.val_wrapper.replace(v); - } else if v.is_array() && v.as_array().unwrap().is_empty() { - vf.val_wrapper.replace(RefValue::Null.into()); - } else if vf.val_wrapper.is_array() { - vf.val_wrapper.replace(v); - } - } - _ => {} - } - } - } - - pub fn into_value(&self) -> Value { - match self.filter_stack.last() { - Some(v) => v.val_wrapper.into_value(), - _ => Value::Null - } - } - - pub fn take_value(&mut self) -> RefValueWrapper { - match self.filter_stack.last_mut() { - Some(v) => v.val_wrapper.get_val().clone(), - _ => RefValue::Null.into() - } - } - - fn token_union(&mut self, indices: Vec) { - self.token_stack.pop(); - - match self.filter_stack.last_mut() { - Some(ref mut vf) if vf.val_wrapper.is_array() && vf.val_wrapper.is_leaves() => { - let mut ret = Vec::new(); - if let RefValue::Array(val) = vf.val_wrapper.get_val().deref() { - for v in val { - for i in &indices { - let v = i.take_value(v); - if !v.is_null() { - ret.push(v.clone()); - } - } - } - } - vf.val_wrapper.replace(RefValue::Array(ret).into()); - } - Some(ref mut vf) if vf.val_wrapper.is_array() && !vf.val_wrapper.is_leaves() => { - let mut ret = Vec::new(); - for i in indices { - let wrapper = i.take_value(&vf.val_wrapper.get_val()); - if !wrapper.is_null() { - ret.push(wrapper.clone()); - } - } - vf.val_wrapper.replace(RefValue::Array(ret).into()); - } - _ => {} - } - } - - fn token_range(&mut self, from: Option, to: Option) { - self.token_stack.pop(); - - fn _from_to(from: Option, to: Option, val: &RefValueWrapper) -> (usize, usize) { - let from = match from { - Some(v) => v.index(val), - _ => 0 - }; - let to = match to { - Some(v) => v.index(val), - _ => { - if let RefValue::Array(v) = val.deref() { - v.len() - } else { - 0 - } - } - }; - (from, to) - } - - fn _range(from: usize, to: usize, v: &RefValueWrapper) -> Vec { - trace!("range - {}:{}", from, to); - - (from..to).into_iter() - .map(|i| i.take_value(v)) - .filter(|v| !v.is_null()) - .map(|v| v.clone()) - .collect() - } - - match self.filter_stack.last_mut() { - Some(ref mut vf) if vf.val_wrapper.is_array() && vf.val_wrapper.is_leaves() => { - let mut buf = Vec::new(); - if let RefValue::Array(vec) = vf.val_wrapper.get_val().deref() { - for v in vec { - let (from, to) = _from_to(from, to, v); - let mut v: Vec = _range(from, to, v); - buf.append(&mut v); - } - } - vf.val_wrapper.replace(RefValue::Array(buf).into()); - } - Some(ref mut vf) if vf.val_wrapper.is_array() && !vf.val_wrapper.is_leaves() => { - let (from, to) = _from_to(from, to, &vf.val_wrapper.get_val()); - let vec: Vec = _range(from, to, vf.val_wrapper.get_val()); - vf.val_wrapper.replace(RefValue::Array(vec).into()); - } - _ => {} - } - } - - fn token_key(&mut self, key: String) { - match self.filter_stack.last_mut() { - Some(vf) => { - match self.token_stack.pop() { - Some(ParseToken::In) | Some(ParseToken::Array) => { - vf.step_in_string(&key); - } - Some(ParseToken::Leaves) => { - vf.step_leaves_string(&key); - } - _ => { - self.term_stack.push(TermContext::Constants(ExprTerm::String(key))); - } - } - } - _ => {} - } - } - - fn token_all(&mut self) { - match self.filter_stack.last_mut() { - Some(vf) => { - match self.token_stack.pop() { - Some(ParseToken::In) => { - vf.step_in_all(); - } - Some(ParseToken::Leaves) => { - vf.step_leaves_all(); - } - _ => {} - } - } - _ => {} - } - } - - fn token_end_array(&mut self) { - trace!("array_eof - term_stack: {:?}", self.term_stack); - trace!("array_eof - filter_stack: {:?}", self.filter_stack); - - match self.term_stack.pop() { - Some(TermContext::Constants(ExprTerm::Number(v))) => { - match self.filter_stack.last_mut() { - Some(vf) => { - vf.step_in_num(&v); - } - _ => {} - } - } - Some(TermContext::Constants(ExprTerm::Bool(false))) => { - self.replace_filter_stack(RefValue::Null.into(), false); - } - Some(TermContext::Json(_, vw)) => { - self.replace_filter_stack(vw.get_val().clone(), vw.is_leaves()); - } - _ => { - match self.filter_stack.pop() { - Some(vf) => { - let is_leaves = vf.val_wrapper.is_leaves(); - match vf.val_wrapper.get_val().deref() { - RefValue::Null | RefValue::Bool(false) => { - self.replace_filter_stack(RefValue::Null.into(), is_leaves); - } - _ => { - self.replace_filter_stack(vf.val_wrapper.get_val().clone(), is_leaves); - } - } - } - _ => {} - } - } - } - } - - fn token_op(&mut self, ft: &FilterToken) { - let right = self.term_stack.pop(); - let left = self.term_stack.pop(); - - trace!("left {:?}", left); - trace!("right {:?}", right); - - if left.is_some() && right.is_some() { - let left = left.unwrap(); - let right = right.unwrap(); - - let tc = match ft { - FilterToken::Equal => left.eq(&right), - FilterToken::NotEqual => left.ne(&right), - FilterToken::Greater => left.gt(&right), - FilterToken::GreaterOrEqual => left.ge(&right), - FilterToken::Little => left.lt(&right), - FilterToken::LittleOrEqual => left.le(&right), - FilterToken::And => left.and(&right), - FilterToken::Or => left.or(&right), - }; - self.term_stack.push(tc); - } - - trace!("filter - {:?}", self.term_stack) - } -} - -impl NodeVisitor for JsonValueFilter { - fn visit_token(&mut self, token: ParseToken) { - debug!("visit_token: {:?}", token); - - match token { - ParseToken::Absolute - | ParseToken::Relative => { - if self.is_peek_token_array() { - self.token_stack.pop(); - } - self.push_value_filter(ParseToken::Relative == token); - } - ParseToken::In - | ParseToken::Leaves => { - self.token_stack.push(token); - } - ParseToken::Array => { - if let Some(ParseToken::Leaves) = self.token_stack.last() { - self.token_all(); - } - self.token_stack.push(token); - } - ParseToken::ArrayEof => { - self.token_end_array(); - } - ParseToken::All => { - self.token_all(); - } - ParseToken::Key(key) => { - self.token_key(key); - } - ParseToken::Filter(ref ft) => { - self.token_op(ft); - } - ParseToken::Number(v) => { - self.term_stack.push(TermContext::Constants(ExprTerm::Number(v))) - } - ParseToken::Range(from, to) => { - self.token_range(from, to); - } - ParseToken::Union(v) => { - self.token_union(v); - } - ParseToken::Eof => { - debug!("visit_token eof"); - } - } - } - - fn end_term(&mut self) { - debug!("end_term"); - - if let Some(ParseToken::Array) = self.token_stack.last() { - self.token_stack.pop(); - } - - trace!("end_term - term_stack {:?}", self.term_stack); - trace!("end_term - token_stack {:?}", self.token_stack); - trace!("end_term - filter_stack {:?}", self.filter_stack); - - if self.token_stack.is_empty() && self.filter_stack.len() > 1 { - match self.filter_stack.pop() { - Some(vf) => { - self.term_stack.push(TermContext::Json(vf.last_key, vf.val_wrapper)); - } - _ => {} - } - } - - if match self.token_stack.last() { - Some(ParseToken::Key(_)) - | Some(ParseToken::Number(_)) => true, - _ => false - } { - match self.token_stack.pop() { - Some(ParseToken::Key(ref v)) if v.eq_ignore_ascii_case("true") => { - self.term_stack.push(TermContext::Constants(ExprTerm::Bool(true))) - } - Some(ParseToken::Key(ref v)) if v.eq_ignore_ascii_case("false") => { - self.term_stack.push(TermContext::Constants(ExprTerm::Bool(false))) - } - Some(ParseToken::Key(v)) => { - self.term_stack.push(TermContext::Constants(ExprTerm::String(v))) - } - Some(ParseToken::Number(v)) => { - self.term_stack.push(TermContext::Constants(ExprTerm::Number(v))) - } - _ => {} - } - } - } -} diff --git a/src/filter/value_wrapper.rs b/src/filter/value_wrapper.rs deleted file mode 100644 index 7782f8e..0000000 --- a/src/filter/value_wrapper.rs +++ /dev/null @@ -1,307 +0,0 @@ -use std::ops::Deref; - -use indexmap::{IndexSet, IndexMap}; -use serde_json::Value; - -use ref_value::model::*; - -use super::cmp::*; -use super::term::*; -use super::value_filter::*; - -fn cmp_with_term(val: &RefValueWrapper, et: &ExprTerm, cmp_fn: &F, default: bool, reverse: bool) -> bool { - match val.deref() { - RefValue::Bool(ref v1) => { - match et { - ExprTerm::Bool(v2) => if reverse { cmp_fn.cmp_bool(v2, v1) } else { cmp_fn.cmp_bool(v1, v2) }, - _ => default - } - } - RefValue::Number(ref v1) => match et { - ExprTerm::Number(v2) => if reverse { cmp_fn.cmp_f64(v2, &v1.as_f64().unwrap()) } else { cmp_fn.cmp_f64(&v1.as_f64().unwrap(), v2) }, - _ => default - }, - RefValue::String(ref v1) => { - match et { - ExprTerm::String(v2) => if reverse { cmp_fn.cmp_string(v2, v1) } else { cmp_fn.cmp_string(v1, v2) }, - _ => default - } - } - _ => default - } -} - -#[derive(Debug)] -pub struct ValueWrapper { - val: RefValueWrapper, - is_leaves: bool, -} - -impl ValueWrapper { - pub fn new(val: RefValueWrapper, leaves: bool) -> Self { - ValueWrapper { val, is_leaves: leaves } - } - - pub fn is_leaves(&self) -> bool { - self.is_leaves - } - - pub fn set_leaves(&mut self, is_leaves: bool) { - self.is_leaves = is_leaves; - } - - pub fn cmp(&self, other: &ValueWrapper, cmp_type: CmpType) -> TermContext { - match cmp_type { - CmpType::Eq => { - TermContext::Json(None, self.intersect(other)) - } - CmpType::Ne => { - TermContext::Json(None, self.except(other)) - } - CmpType::Gt | CmpType::Ge | CmpType::Lt | CmpType::Le => { - TermContext::Constants(ExprTerm::Bool(false)) - } - } - } - - fn take_object_in_array(&self, key: &String, et: &ExprTerm, cmp: &F, reverse: bool) -> Option { - fn _filter_with_object bool>(v: &RefValueWrapper, key: &String, fun: F) -> bool { - match v.deref() { - RefValue::Object(map) => { - match map.get(key) { - Some(val) => fun(val), - _ => false - } - } - _ => false - } - } - - match self.val.deref() { - RefValue::Array(vec) => { - let mut set = IndexSet::new(); - for v in vec { - if _filter_with_object(v, key, |vv| { - cmp_with_term(vv, et, cmp, false, reverse) - }) { - set.insert(v.clone()); - } - } - - let ret = set.into_iter().collect(); - Some(ValueWrapper::new(RefValue::Array(ret).into(), false)) - } - _ => None - } - } - - fn take_with_key_type(&self, key: &Option, et: &ExprTerm, cmp: &F, reverse: bool) -> Option { - match key { - Some(ValueFilterKey::String(key)) => { - self.take_object_in_array(key, et, cmp, reverse) - } - _ => None - } - } - - pub fn take_with(&self, key: &Option, et: &ExprTerm, cmp: F, reverse: bool) -> Self { - match self.take_with_key_type(key, et, &cmp, reverse) { - Some(vw) => vw, - _ => { - match &(*self.val) { - RefValue::Array(vec) => { - let mut set = IndexSet::new(); - for v in vec { - if cmp_with_term(v, et, &cmp, false, reverse) { - set.insert(v.clone()); - } - } - let ret = set.into_iter().collect(); - ValueWrapper::new(RefValue::Array(ret).into(), false) - } - _ => { - if cmp_with_term(&self.val, et, &cmp, false, reverse) { - ValueWrapper::new(self.val.clone(), false) - } else { - ValueWrapper::new(RefValue::Null.into(), false) - } - } - } - } - } - } - - pub fn replace(&mut self, val: RefValueWrapper) { - let is_null = match val.deref() { - RefValue::Array(v) => v.is_empty(), - RefValue::Object(m) => m.is_empty(), - _ => val.is_null() - }; - self.val = if is_null { - RefValue::Null.into() - } else { - val - }; - } - - pub fn get_val(&self) -> &RefValueWrapper { - &self.val - } - - pub fn into_value(&self) -> Value { - self.get_val().into() - } - - pub fn is_array(&self) -> bool { - self.val.is_array() - } - - fn into_hashset(&self) -> IndexSet<&RefValue> { - trace!("into_hashset"); - let mut hashset = IndexSet::new(); - match self.val.deref() { - RefValue::Array(ref v1) => { - for v in v1 { - hashset.insert(v.deref()); - } - } - _ => { - hashset.insert(self.val.deref()); - } - } - hashset - } - - fn into_hashmap(&self) -> IndexMap<&RefValue, RefValueWrapper> { - trace!("into_hashmap"); - let mut hashmap = IndexMap::new(); - match self.val.deref() { - RefValue::Array(ref v1) => { - for v in v1 { - hashmap.insert(v.deref(), v.clone()); - } - } - _ => { - hashmap.insert(self.val.deref(), self.val.clone()); - } - } - hashmap - } - - pub fn except(&self, other: &Self) -> Self { - trace!("except"); - let hashset = self.into_hashset(); - let mut ret: IndexSet = IndexSet::new(); - match other.val.deref() { - RefValue::Array(ref v1) => { - for v in v1 { - if !hashset.contains(v.deref()) { - ret.insert(v.clone()); - } - } - } - _ => { - if !hashset.contains(&other.val.deref()) { - ret.insert(other.val.clone()); - } - } - } - - let vec = ret.into_iter().map(|v| v.clone()).collect(); - ValueWrapper::new(RefValue::Array(vec).into(), false) - } - - pub fn intersect(&self, other: &Self) -> Self { - trace!("intersect"); - let hashset = self.into_hashset(); - let mut ret: IndexSet = IndexSet::new(); - match other.val.deref() { - RefValue::Array(ref v1) => { - for v in v1 { - if hashset.contains(v.deref()) { - ret.insert(v.clone()); - } - } - } - e => { - if hashset.contains(e) { - ret.insert(other.val.clone()); - } - } - } - - let vec = ret.into_iter().map(|v| v.clone()).collect(); - ValueWrapper::new(RefValue::Array(vec).into(), false) - } - - pub fn union(&self, other: &Self) -> Self { - trace!("union"); - let origin = self.into_hashmap(); - let mut ret = IndexSet::new(); - - for o in origin.values() { - ret.insert(o.clone()); - } - - match other.val.deref() { - RefValue::Array(ref v1) => { - for v in v1 { - if !origin.contains_key(v.deref()) { - ret.insert(v.clone()); - } - } - } - _ => { - if !origin.contains_key(&other.val.deref()) { - ret.insert(other.val.clone()); - } - } - } - - let mut vw = ValueWrapper::new(RefValue::Null.into(), false); - let list = ret.iter().map(|v| v.clone()).collect(); - vw.replace(RefValue::Array(list).into()); - vw - } - - pub fn into_term(&self, key: &Option) -> TermContext { - match self.val.deref() { - RefValue::String(ref s) => TermContext::Constants(ExprTerm::String(s.clone())), - RefValue::Number(ref n) => TermContext::Constants(ExprTerm::Number(n.as_f64().unwrap())), - RefValue::Bool(b) => TermContext::Constants(ExprTerm::Bool(*b)), - _ => TermContext::Json(match key { - Some(vk) => Some(vk.clone()), - _ => None - }, ValueWrapper::new(self.val.clone(), false)) - } - } - - pub fn filter(&self, key: &Option) -> Self { - trace!("filter"); - let v = match self.val.deref() { - RefValue::Array(ref vec) => { - let mut ret = Vec::new(); - for v in vec { - if let Some(ValueFilterKey::String(k)) = key { - if v.get(k.clone()).is_some() { - ret.push(v.clone()); - } - } - } - RefValue::Array(ret).into() - } - RefValue::Object(ref map) => { - match key { - Some(ValueFilterKey::String(k)) => match map.get(k) { - Some(v) => v.clone(), - _ => RefValue::Null.into() - }, - _ => RefValue::Null.into() - } - } - _ => self.val.clone() - }; - - ValueWrapper::new(v, false) - } -} diff --git a/src/lib.rs b/src/lib.rs index 6fa94e4..7b5c5b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,271 +1,230 @@ -//! JsonPath implementation for Rust +//! JsonPath implementation written in Rust. //! //! # Example //! ``` -//! extern crate jsonpath_lib as jsonpath; -//! #[macro_use] extern crate serde_json; +//! extern crate jsonpath_lib as jsonpath; +//! #[macro_use] extern crate serde_json; +//! let json_obj = json!({ +//! "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 +//! } +//! }, +//! "expensive": 10 +//! }); //! -//! let json_obj = json!({ -//! "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 -//! } -//! }, -//! "expensive": 10 -//! }); +//! let mut selector = jsonpath::selector(&json_obj); //! -//! let mut selector = jsonpath::selector(&json_obj); +//! assert_eq!(selector("$.store.book[*].author").unwrap(), +//! vec![ +//! "Nigel Rees", "Evelyn Waugh", "Herman Melville", "J. R. R. Tolkien" +//! ]); //! -//! // -//! // $.store.book[*].author -//! // -//! let json = selector("$.store.book[*].author").unwrap(); -//! let ret = json!(["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]); -//! assert_eq!(json, ret); +//! assert_eq!(selector("$..author").unwrap(), +//! vec![ +//! "Nigel Rees", "Evelyn Waugh", "Herman Melville", "J. R. R. Tolkien" +//! ]); //! -//! // -//! // $..author -//! // -//! let json = selector("$..author").unwrap(); -//! let ret = json!(["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]); -//! assert_eq!(json, ret); +//! assert_eq!(selector("$.store.*").unwrap(), +//! vec![ +//! &json!([ +//! { "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 } +//! ]), +//! &json!({ "color": "red", "price": 19.95 }) +//! ]); //! -//! // -//! // $.store.* -//! // -//! let json = selector("$.store.*").unwrap(); -//! let ret = json!([ -//! [ -//! {"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} -//! ], -//! {"color" : "red","price" : 19.95}, -//! ]); -//! assert_eq!(ret, json); +//! assert_eq!(selector("$.store..price").unwrap(), +//! vec![ +//! 8.95, 12.99, 8.99, 22.99, 19.95 +//! ]); //! -//! // -//! // $.store..price -//! // -//! let json = selector("$.store..price").unwrap(); -//! let ret = json!([8.95, 12.99, 8.99, 22.99, 19.95]); -//! assert_eq!(ret, json); +//! assert_eq!(selector("$..book[2]").unwrap(), +//! vec![ +//! &json!({ +//! "category" : "fiction", +//! "author" : "Herman Melville", +//! "title" : "Moby Dick", +//! "isbn" : "0-553-21311-3", +//! "price" : 8.99 +//! }) +//! ]); //! -//! // -//! // $..book[2] -//! // -//! let json = selector("$..book[2]").unwrap(); -//! let ret = json!([{ -//! "category" : "fiction", -//! "author" : "Herman Melville", -//! "title" : "Moby Dick", -//! "isbn" : "0-553-21311-3", -//! "price" : 8.99 -//! }]); -//! assert_eq!(ret, json); +//! assert_eq!(selector("$..book[-2]").unwrap(), +//! vec![ +//! &json!({ +//! "category" : "fiction", +//! "author" : "Herman Melville", +//! "title" : "Moby Dick", +//! "isbn" : "0-553-21311-3", +//! "price" : 8.99 +//! }) +//! ]); //! -//! // -//! // $..book[-2] -//! // -//! let json = selector("$..book[-2]").unwrap(); -//! let ret = json!([{ -//! "category" : "fiction", -//! "author" : "Herman Melville", -//! "title" : "Moby Dick", -//! "isbn" : "0-553-21311-3", -//! "price" : 8.99 -//! }]); -//! assert_eq!(ret, json); +//! assert_eq!(selector("$..book[0,1]").unwrap(), +//! vec![ +//! &json!({"category" : "reference","author" : "Nigel Rees","title" : "Sayings of the Century","price" : 8.95}), +//! &json!({"category" : "fiction","author" : "Evelyn Waugh","title" : "Sword of Honour","price" : 12.99}) +//! ]); //! -//! // -//! // $..book[0,1] -//! // -//! let json = selector("$..book[0,1]").unwrap(); -//! let ret = json!([ -//! {"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} -//! ]); -//! assert_eq!(ret, json); +//! assert_eq!(selector("$..book[:2]").unwrap(), +//! vec![ +//! &json!({"category" : "reference","author" : "Nigel Rees","title" : "Sayings of the Century","price" : 8.95}), +//! &json!({"category" : "fiction","author" : "Evelyn Waugh","title" : "Sword of Honour","price" : 12.99}) +//! ]); //! -//! // -//! // $..book[:2] -//! // -//! let json = selector("$..book[:2]").unwrap(); -//! let ret = json!([ -//! {"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} -//! ]); -//! assert_eq!(ret, json); +//! assert_eq!(selector("$..book[:2]").unwrap(), +//! vec![ +//! &json!({"category" : "reference","author" : "Nigel Rees","title" : "Sayings of the Century","price" : 8.95}), +//! &json!({"category" : "fiction","author" : "Evelyn Waugh","title" : "Sword of Honour","price" : 12.99}) +//! ]); //! -//! // -//! // $..book[2:] -//! // -//! let json = selector("$..book[2:]").unwrap(); -//! let ret = json!([ -//! {"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} -//! ]); -//! assert_eq!(ret, json); +//! assert_eq!(selector("$..book[?(@.isbn)]").unwrap(), +//! vec![ +//! &json!({"category" : "fiction","author" : "Herman Melville","title" : "Moby Dick","isbn" : "0-553-21311-3","price" : 8.99}), +//! &json!({"category" : "fiction","author" : "J. R. R. Tolkien","title" : "The Lord of the Rings","isbn" : "0-395-19395-8","price" : 22.99}) +//! ]); //! -//! // -//! // $..book[?(@.isbn)] -//! // -//! let json = selector("$..book[?(@.isbn)]").unwrap(); -//! let ret = json!([ -//! {"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} -//! ]); -//! assert_eq!(ret, json); -//! -//! // -//! // $.store.book[?(@.price < 10)] -//! // -//! let json = selector("$.store.book[?(@.price < 10)]").unwrap(); -//! let ret = json!([ -//! {"category" : "reference","author" : "Nigel Rees","title" : "Sayings of the Century","price" : 8.95}, -//! {"category" : "fiction","author" : "Herman Melville","title" : "Moby Dick","isbn" : "0-553-21311-3","price" : 8.99} -//! ]); -//! assert_eq!(ret, json); +//! assert_eq!(selector("$.store.book[?(@.price < 10)]").unwrap(), +//! vec![ +//! &json!({"category" : "reference","author" : "Nigel Rees","title" : "Sayings of the Century","price" : 8.95}), +//! &json!({"category" : "fiction","author" : "Herman Melville","title" : "Moby Dick","isbn" : "0-553-21311-3","price" : 8.99}) +//! ]); //! ``` - +extern crate array_tool; extern crate core; extern crate env_logger; extern crate indexmap; #[macro_use] extern crate log; -#[macro_use] extern crate serde; extern crate serde_json; -use core::borrow::BorrowMut; -use std::result; - use serde_json::Value; -#[doc(hidden)] -pub mod parser; -#[doc(hidden)] -pub mod filter; -#[doc(hidden)] -pub mod ref_value; -#[doc(hidden)] -pub mod select; +pub use parser::parser::{Node, Parser}; +pub use select::{Selector, SelectorMut}; +pub use select::JsonPathError; -pub use select::Selector; +#[doc(hidden)] +mod parser; +#[doc(hidden)] +mod select; -/// It is a high-order function. it compile a JsonPath and then returns a function. this return-function can be reused for different JsonObjects. +/// It is a high-order function. it compile a jsonpath and then returns a closure that has JSON as argument. if you need to reuse a jsonpath, it is good for performance. /// /// ```rust /// extern crate jsonpath_lib as jsonpath; /// #[macro_use] extern crate serde_json; /// -/// let mut template = jsonpath::compile("$..friends[0]"); +/// let mut first_firend = jsonpath::compile("$..friends[0]"); /// /// let json_obj = json!({ -/// "school": { -/// "friends": [ -/// {"name": "친구1", "age": 20}, -/// {"name": "친구2", "age": 20} -/// ] -/// }, -/// "friends": [ -/// {"name": "친구3", "age": 30}, -/// {"name": "친구4"} +/// "school": { +/// "friends": [ +/// {"name": "친구1", "age": 20}, +/// {"name": "친구2", "age": 20} +/// ] +/// }, +/// "friends": [ +/// {"name": "친구3", "age": 30}, +/// {"name": "친구4"} /// ]}); /// -/// let json = template(&json_obj).unwrap(); -/// let ret = json!([ -/// {"name": "친구3", "age": 30}, -/// {"name": "친구1", "age": 20} +/// let json = first_firend(&json_obj).unwrap(); +/// +/// assert_eq!(json, vec![ +/// &json!({"name": "친구3", "age": 30}), +/// &json!({"name": "친구1", "age": 20}) /// ]); -/// assert_eq!(json, ret); /// ``` -pub fn compile<'a>(path: &'a str) -> impl FnMut(&Value) -> result::Result + 'a { - let mut selector = Selector::new(); - let _ = selector.path(path); - let mut selector = Box::new(selector); +pub fn compile(path: &str) -> impl FnMut(&Value) -> Result, JsonPathError> { + let node = Parser::compile(path); move |json| { - let s: &mut Selector = selector.borrow_mut(); - let _ = s.value(&json); - s.select_as_value() + match &node { + Ok(node) => { + let mut selector = Selector::new(); + selector.compiled_path(node).value(json).select() + } + Err(e) => Err(JsonPathError::Path(e.to_string())) + } } } -/// It is a high-order function that return a function. this return-function has a jsonpath as argument and return a serde_json::value::Value. so you can use different JsonPath for one JsonObject. +/// It is a high-order function. it returns a closure that has a jsonpath string as argument. you can use diffenent jsonpath for one JSON object. /// /// ```rust /// extern crate jsonpath_lib as jsonpath; /// #[macro_use] extern crate serde_json; /// /// let json_obj = json!({ -/// "school": { -/// "friends": [ -/// {"name": "친구1", "age": 20}, -/// {"name": "친구2", "age": 20} -/// ] -/// }, -/// "friends": [ -/// {"name": "친구3", "age": 30}, -/// {"name": "친구4"} +/// "school": { +/// "friends": [ +/// {"name": "친구1", "age": 20}, +/// {"name": "친구2", "age": 20} +/// ] +/// }, +/// "friends": [ +/// {"name": "친구3", "age": 30}, +/// {"name": "친구4"} /// ]}); /// /// let mut selector = jsonpath::selector(&json_obj); /// /// let json = selector("$..friends[0]").unwrap(); -/// let ret = json!([ -/// {"name": "친구3", "age": 30}, -/// {"name": "친구1", "age": 20} +/// +/// assert_eq!(json, vec![ +/// &json!({"name": "친구3", "age": 30}), +/// &json!({"name": "친구1", "age": 20}) /// ]); -/// assert_eq!(json, ret); /// /// let json = selector("$..friends[1]").unwrap(); -/// let ret = json!([ -/// {"name": "친구4"}, -/// {"name": "친구2", "age": 20} +/// +/// assert_eq!(json, vec![ +/// &json!({"name": "친구4"}), +/// &json!({"name": "친구2", "age": 20}) /// ]); -/// assert_eq!(json, ret); /// ``` -pub fn selector<'a>(json: &Value) -> impl FnMut(&'a str) -> result::Result { +pub fn selector<'a>(json: &'a Value) -> impl FnMut(&'a str) -> Result, JsonPathError> { let mut selector = Selector::new(); - let _ = selector.value(json.into()); - let mut selector = Box::new(selector); - move |path: &'a str| { - let s: &mut Selector = selector.borrow_mut(); - s.path(path)?.select_as_value() + let _ = selector.value(json); + move |path: &str| { + selector.str_path(path)?.reset_value().select() } } -/// It is a high-order function that returns a function. this return-function has a jsonpath as argument and return a serde::Deserialize. so you can use different JsonPath for one JsonObject. +/// It is the same to `selector` function. but it deserialize the result as given type `T`. /// /// ```rust /// extern crate jsonpath_lib as jsonpath; @@ -275,26 +234,27 @@ pub fn selector<'a>(json: &Value) -> impl FnMut(&'a str) -> result::Result, /// } /// -/// let mut selector = jsonpath::selector_as::>(&json_obj); +/// let mut selector = jsonpath::selector_as::(&json_obj); /// /// let json = selector("$..friends[0]").unwrap(); +/// /// let ret = vec!( /// Friend { name: "친구3".to_string(), age: Some(30) }, /// Friend { name: "친구1".to_string(), age: Some(20) } @@ -302,67 +262,52 @@ pub fn selector<'a>(json: &Value) -> impl FnMut(&'a str) -> result::Result(json: &Value) -> impl FnMut(&str) -> result::Result { +pub fn selector_as(json: &Value) -> impl FnMut(&str) -> Result, JsonPathError> + '_ { let mut selector = Selector::new(); - let _ = selector.value(json.into()); + let _ = selector.value(json); move |path: &str| { - selector.path(path)?.select_as() + selector.str_path(path)?.reset_value().select_as() } } -#[deprecated(since = "0.1.4", note = "Please use the selector function instead")] -pub fn reader<'a>(json: &Value) -> impl FnMut(&'a str) -> result::Result { - selector(json) -} - -/// This function compile a jsonpath everytime and it convert `serde_json's Value` to `jsonpath's RefValue` everytime and then it return a `serde_json::value::Value`. +/// It is a simple select function. but it compile the jsonpath argument every time. /// /// ```rust /// extern crate jsonpath_lib as jsonpath; /// #[macro_use] extern crate serde_json; /// /// let json_obj = json!({ -/// "school": { -/// "friends": [ -/// {"name": "친구1", "age": 20}, -/// {"name": "친구2", "age": 20} -/// ] -/// }, -/// "friends": [ -/// {"name": "친구3", "age": 30}, -/// {"name": "친구4"} +/// "school": { +/// "friends": [ +/// {"name": "친구1", "age": 20}, +/// {"name": "친구2", "age": 20} +/// ] +/// }, +/// "friends": [ +/// {"name": "친구3", "age": 30}, +/// {"name": "친구4"} /// ]}); /// /// let json = jsonpath::select(&json_obj, "$..friends[0]").unwrap(); /// -/// let ret = json!([ -/// {"name": "친구3", "age": 30}, -/// {"name": "친구1", "age": 20} +/// assert_eq!(json, vec![ +/// &json!({"name": "친구3", "age": 30}), +/// &json!({"name": "친구1", "age": 20}) /// ]); -/// assert_eq!(json, ret); /// ``` -pub fn select(json: &Value, path: &str) -> result::Result { - let mut selector = Selector::new(); - selector.path(path)?.value(json.into())?.select_as_value() +pub fn select<'a>(json: &'a Value, path: &'a str) -> Result, JsonPathError> { + Selector::new().str_path(path)?.value(json).select() } -#[deprecated(since = "0.1.4", note = "Please use the select function instead")] -pub fn read(json: &Value, path: &str) -> result::Result { - select(json, path) -} - -#[deprecated(since = "0.1.7", note = "Please use the select_as_str function instead")] -pub fn select_str(json: &str, path: &str) -> result::Result { - select_as_str(json, path) -} - -/// This function compile a jsonpath everytime and it convert `&str` to `jsonpath's RefValue` everytime and then it return a json string. +/// It is the same to `select` function but it return the result as string. /// /// ```rust /// extern crate jsonpath_lib as jsonpath; @@ -385,14 +330,13 @@ pub fn select_str(json: &str, path: &str) -> result::Result { /// /// assert_eq!(ret, r#"[{"name":"친구3","age":30},{"name":"친구1","age":20}]"#); /// ``` -pub fn select_as_str(json: &str, path: &str) -> result::Result { - Selector::new() - .path(path)? - .value_from_str(json)? - .select_as_str() +pub fn select_as_str(json_str: &str, path: &str) -> Result { + let json = serde_json::from_str(json_str).map_err(|e| JsonPathError::Serde(e.to_string()))?; + let ret = Selector::new().str_path(path)?.value(&json).select()?; + serde_json::to_string(&ret).map_err(|e| JsonPathError::Serde(e.to_string())) } -/// This function compile a jsonpath everytime and it convert `&str` to `jsonpath's RefValue` everytime and then it return a deserialized-instance of type `T`. +/// It is the same to `select` function but it deserialize the the result as given type `T`. /// /// ```rust /// extern crate jsonpath_lib as jsonpath; @@ -408,7 +352,7 @@ pub fn select_as_str(json: &str, path: &str) -> result::Result { /// phones: Vec, /// } /// -/// let ret: Person = jsonpath::select_as(r#" +/// let ret: Vec = jsonpath::select_as(r#" /// { /// "person": /// { @@ -428,11 +372,97 @@ pub fn select_as_str(json: &str, path: &str) -> result::Result { /// phones: vec!["+44 1234567".to_string(), "+44 2345678".to_string()], /// }; /// -/// assert_eq!(person, ret); +/// assert_eq!(ret[0], person); /// ``` -pub fn select_as(json: &str, path: &str) -> result::Result { - Selector::new() - .path(path)? - .value_from_str(json)? - .select_as() +pub fn select_as(json_str: &str, path: &str) -> Result, JsonPathError> { + let json = serde_json::from_str(json_str).map_err(|e| JsonPathError::Serde(e.to_string()))?; + Selector::new().str_path(path)?.value(&json).select_as() +} + +/// Delete(= replace with null) the JSON property using the jsonpath. +/// +/// ```rust +/// extern crate jsonpath_lib as jsonpath; +/// #[macro_use] extern crate serde_json; +/// +/// let json_obj = json!({ +/// "school": { +/// "friends": [ +/// {"name": "친구1", "age": 20}, +/// {"name": "친구2", "age": 20} +/// ] +/// }, +/// "friends": [ +/// {"name": "친구3", "age": 30}, +/// {"name": "친구4"} +/// ]}); +/// +/// let ret = jsonpath::delete(json_obj, "$..[?(20 == @.age)]").unwrap(); +/// +/// assert_eq!(ret, json!({ +/// "school": { +/// "friends": [ +/// null, +/// null +/// ] +/// }, +/// "friends": [ +/// {"name": "친구3", "age": 30}, +/// {"name": "친구4"} +/// ]})); +/// ``` +pub fn delete(value: Value, path: &str) -> Result { + let mut selector = SelectorMut::new(); + let ret = selector.str_path(path)?.value(value).delete()?.take().unwrap_or(Value::Null); + Ok(ret) +} + +/// Select JSON properties using a jsonpath and transform the result and then replace it. via closure that implements `FnMut` you can transform the selected results. +/// +/// ```rust +/// extern crate jsonpath_lib as jsonpath; +/// #[macro_use] extern crate serde_json; +/// +/// use serde_json::Value; +/// +/// let json_obj = json!({ +/// "school": { +/// "friends": [ +/// {"name": "친구1", "age": 20}, +/// {"name": "친구2", "age": 20} +/// ] +/// }, +/// "friends": [ +/// {"name": "친구3", "age": 30}, +/// {"name": "친구4"} +/// ]}); +/// +/// let ret = jsonpath::replace_with(json_obj, "$..[?(@.age == 20)].age", &mut |v| { +/// let age = if let Value::Number(n) = v { +/// n.as_u64().unwrap() * 2 +/// } else { +/// 0 +/// }; +/// +/// json!(age) +/// }).unwrap(); +/// +/// assert_eq!(ret, json!({ +/// "school": { +/// "friends": [ +/// {"name": "친구1", "age": 40}, +/// {"name": "친구2", "age": 40} +/// ] +/// }, +/// "friends": [ +/// {"name": "친구3", "age": 30}, +/// {"name": "친구4"} +/// ]})); +/// ``` +pub fn replace_with(value: Value, path: &str, fun: &mut F) -> Result + where F: FnMut(&Value) -> Value +{ + let mut selector = SelectorMut::new(); + let ret = selector.str_path(path)?.value(value).replace_with(fun)?.take().unwrap_or(Value::Null); + Ok(ret) } \ No newline at end of file diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 331b73a..42ac385 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,3 +1,524 @@ mod path_reader; -pub mod tokenizer; -pub mod parser; \ No newline at end of file +pub(crate) mod tokenizer; +pub mod parser; + +#[cfg(test)] +mod parser_tests { + use parser::parser::{ParseToken, Parser, NodeVisitor, FilterToken}; + + struct NodeVisitorTestImpl<'a> { + input: &'a str, + stack: Vec, + } + + impl<'a> NodeVisitorTestImpl<'a> { + fn new(input: &'a str) -> Self { + NodeVisitorTestImpl { input, stack: Vec::new() } + } + + fn start(&mut self) -> Result, String> { + let node = Parser::compile(self.input)?; + self.visit(&node); + Ok(self.stack.split_off(0)) + } + } + + impl<'a> NodeVisitor for NodeVisitorTestImpl<'a> { + fn visit_token(&mut self, token: &ParseToken) { + self.stack.push(token.clone()); + } + } + + fn setup() { + let _ = env_logger::try_init(); + } + + fn run(input: &str) -> Result, String> { + let mut interpreter = NodeVisitorTestImpl::new(input); + interpreter.start() + } + + #[test] + fn parse_path() { + setup(); + + assert_eq!(run("$.aa"), Ok(vec![ + ParseToken::Absolute, + ParseToken::In, + ParseToken::Key("aa".to_owned()) + ])); + + assert_eq!(run("$.00.a"), Ok(vec![ + ParseToken::Absolute, + ParseToken::In, + ParseToken::Key("00".to_owned()), + ParseToken::In, + ParseToken::Key("a".to_owned()) + ])); + + assert_eq!(run("$.00.韓창.seok"), Ok(vec![ + ParseToken::Absolute, + ParseToken::In, + ParseToken::Key("00".to_owned()), + ParseToken::In, + ParseToken::Key("韓창".to_owned()), + ParseToken::In, + ParseToken::Key("seok".to_owned()) + ])); + + assert_eq!(run("$.*"), Ok(vec![ + ParseToken::Absolute, + ParseToken::In, + ParseToken::All + ])); + + assert_eq!(run("$..*"), Ok(vec![ + ParseToken::Absolute, + ParseToken::Leaves, + ParseToken::All + ])); + + assert_eq!(run("$..[0]"), Ok(vec![ + ParseToken::Absolute, + ParseToken::Leaves, + ParseToken::Array, + ParseToken::Number(0.0), + ParseToken::ArrayEof + ])); + + match run("$.") { + Ok(_) => panic!(), + _ => {} + } + + match run("$..") { + Ok(_) => panic!(), + _ => {} + } + + match run("$. a") { + Ok(_) => panic!(), + _ => {} + } + } + + #[test] + fn parse_array_sytax() { + setup(); + + assert_eq!(run("$.book[?(@.isbn)]"), Ok(vec![ + ParseToken::Absolute, + ParseToken::In, + ParseToken::Key("book".to_string()), + ParseToken::Array, + ParseToken::Relative, + ParseToken::In, + ParseToken::Key("isbn".to_string()), + ParseToken::ArrayEof + ])); + + // + // Array도 컨텍스트 In으로 간주 할거라서 중첩되면 하나만 + // + assert_eq!(run("$.[*]"), Ok(vec![ + ParseToken::Absolute, + ParseToken::Array, + ParseToken::All, + ParseToken::ArrayEof + ])); + + assert_eq!(run("$.a[*]"), Ok(vec![ + ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), + ParseToken::Array, + ParseToken::All, + ParseToken::ArrayEof + ])); + + assert_eq!(run("$.a[*].가"), Ok(vec![ + ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), + ParseToken::Array, + ParseToken::All, + ParseToken::ArrayEof, + ParseToken::In, ParseToken::Key("가".to_owned()) + ])); + + assert_eq!(run("$.a[0][1]"), Ok(vec![ + ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), + ParseToken::Array, + ParseToken::Number(0_f64), + ParseToken::ArrayEof, + ParseToken::Array, + ParseToken::Number(1_f64), + ParseToken::ArrayEof + ])); + + assert_eq!(run("$.a[1,2]"), Ok(vec![ + ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), + ParseToken::Array, + ParseToken::Union(vec![1, 2]), + ParseToken::ArrayEof + ])); + + assert_eq!(run("$.a[10:]"), Ok(vec![ + ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), + ParseToken::Array, + ParseToken::Range(Some(10), None), + ParseToken::ArrayEof + ])); + + assert_eq!(run("$.a[:11]"), Ok(vec![ + ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), + ParseToken::Array, + ParseToken::Range(None, Some(11)), + ParseToken::ArrayEof + ])); + + assert_eq!(run("$.a[-12:13]"), Ok(vec![ + ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), + ParseToken::Array, + ParseToken::Range(Some(-12), Some(13)), + ParseToken::ArrayEof + ])); + + assert_eq!(run("$.a[?(1>2)]"), Ok(vec![ + ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), + ParseToken::Array, + ParseToken::Number(1_f64), ParseToken::Number(2_f64), ParseToken::Filter(FilterToken::Greater), + ParseToken::ArrayEof + ])); + + assert_eq!(run("$.a[?($.b>3)]"), Ok(vec![ + ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), + ParseToken::Array, + ParseToken::Absolute, ParseToken::In, ParseToken::Key("b".to_owned()), ParseToken::Number(3_f64), ParseToken::Filter(FilterToken::Greater), + ParseToken::ArrayEof + ])); + + assert_eq!(run("$[?($.c>@.d && 1==2)]"), Ok(vec![ + ParseToken::Absolute, + ParseToken::Array, + ParseToken::Absolute, ParseToken::In, ParseToken::Key("c".to_owned()), + ParseToken::Relative, ParseToken::In, ParseToken::Key("d".to_owned()), + ParseToken::Filter(FilterToken::Greater), + ParseToken::Number(1_f64), ParseToken::Number(2_f64), ParseToken::Filter(FilterToken::Equal), + ParseToken::Filter(FilterToken::And), + ParseToken::ArrayEof + ])); + + assert_eq!(run("$[?($.c>@.d&&(1==2||3>=4))]"), Ok(vec![ + ParseToken::Absolute, + ParseToken::Array, + ParseToken::Absolute, ParseToken::In, ParseToken::Key("c".to_owned()), + ParseToken::Relative, ParseToken::In, ParseToken::Key("d".to_owned()), + ParseToken::Filter(FilterToken::Greater), + ParseToken::Number(1_f64), ParseToken::Number(2_f64), ParseToken::Filter(FilterToken::Equal), + ParseToken::Number(3_f64), ParseToken::Number(4_f64), ParseToken::Filter(FilterToken::GreaterOrEqual), + ParseToken::Filter(FilterToken::Or), + ParseToken::Filter(FilterToken::And), + ParseToken::ArrayEof + ])); + + assert_eq!(run("$[?(@.a<@.b)]"), Ok(vec![ + ParseToken::Absolute, + ParseToken::Array, + ParseToken::Relative, ParseToken::In, ParseToken::Key("a".to_owned()), + ParseToken::Relative, ParseToken::In, ParseToken::Key("b".to_owned()), + ParseToken::Filter(FilterToken::Little), + ParseToken::ArrayEof + ])); + + assert_eq!(run("$[*][*][*]"), Ok(vec![ + ParseToken::Absolute, + ParseToken::Array, + ParseToken::All, + ParseToken::ArrayEof, + ParseToken::Array, + ParseToken::All, + ParseToken::ArrayEof, + ParseToken::Array, + ParseToken::All, + ParseToken::ArrayEof + ])); + + assert_eq!(run("$['a']['bb']"), Ok(vec![ + ParseToken::Absolute, + ParseToken::Array, + ParseToken::Key("a".to_string()), + ParseToken::ArrayEof, + ParseToken::Array, + ParseToken::Key("bb".to_string()), + ParseToken::ArrayEof + ])); + + assert_eq!(run("$.a[?(@.e==true)]"), Ok(vec![ + ParseToken::Absolute, + ParseToken::In, + ParseToken::Key("a".to_string()), + ParseToken::Array, + ParseToken::Relative, + ParseToken::In, + ParseToken::Key("e".to_string()), + ParseToken::Bool(true), + ParseToken::Filter(FilterToken::Equal), + ParseToken::ArrayEof + ])); + + match run("$[") { + Ok(_) => panic!(), + _ => {} + } + + match run("$[]") { + Ok(_) => panic!(), + _ => {} + } + + match run("$[a]") { + Ok(_) => panic!(), + _ => {} + } + + match run("$[?($.a)]") { + Ok(_) => panic!(), + _ => {} + } + + match run("$[?(@.a > @.b]") { + Ok(_) => panic!(), + _ => {} + } + + match run("$[?(@.a < @.b&&(@.c < @.d)]") { + Ok(_) => panic!(), + _ => {} + } + } + + #[test] + fn parse_array_float() { + setup(); + + assert_eq!(run("$[?(1.1<2.1)]"), Ok(vec![ + ParseToken::Absolute, + ParseToken::Array, + ParseToken::Number(1.1), ParseToken::Number(2.1), ParseToken::Filter(FilterToken::Little), + ParseToken::ArrayEof + ])); + + match run("$[1.1]") { + Ok(_) => panic!(), + _ => {} + } + + match run("$[?(1.1<.2)]") { + Ok(_) => panic!(), + _ => {} + } + + match run("$[?(1.1<2.)]") { + Ok(_) => panic!(), + _ => {} + } + + match run("$[?(1.1<2.a)]") { + Ok(_) => panic!(), + _ => {} + } + } +} + +#[cfg(test)] +mod tokenizer_tests { + use parser::tokenizer::{Token, TokenError, Tokenizer, TokenReader}; + + fn collect_token(input: &str) -> (Vec, Option) { + let mut tokenizer = Tokenizer::new(input); + let mut vec = vec![]; + loop { + match tokenizer.next_token() { + Ok(t) => vec.push(t), + Err(e) => return (vec, Some(e)), + } + } + } + + fn run(input: &str, expected: (Vec, Option)) { + let (vec, err) = collect_token(input.clone()); + assert_eq!((vec, err), expected, "\"{}\"", input); + } + + #[test] + fn peek() { + let mut tokenizer = TokenReader::new("$.a"); + match tokenizer.next_token() { + Ok(t) => assert_eq!(Token::Absolute(0), t), + _ => panic!() + } + + match tokenizer.peek_token() { + Ok(t) => assert_eq!(&Token::Dot(1), t), + _ => panic!() + } + + match tokenizer.peek_token() { + Ok(t) => assert_eq!(&Token::Dot(1), t), + _ => panic!() + } + + match tokenizer.next_token() { + Ok(t) => assert_eq!(Token::Dot(1), t), + _ => panic!() + } + } + + #[test] + fn token() { + run("$.01.a", + ( + vec![ + Token::Absolute(0), + Token::Dot(1), + Token::Key(2, "01".to_string()), + Token::Dot(4), + Token::Key(5, "a".to_string()) + ] + , Some(TokenError::Eof) + )); + + run("$. []", + ( + vec![ + Token::Absolute(0), + Token::Dot(1), + Token::Whitespace(2, 2), + Token::OpenArray(5), + Token::CloseArray(6) + ] + , Some(TokenError::Eof) + )); + + run("$..", + ( + vec![ + Token::Absolute(0), + Token::Dot(1), + Token::Dot(2), + ] + , Some(TokenError::Eof) + )); + + run("$..ab", + ( + vec![ + Token::Absolute(0), + Token::Dot(1), + Token::Dot(2), + Token::Key(3, "ab".to_string()) + ] + , Some(TokenError::Eof) + )); + + run("$..가 [", + ( + vec![ + Token::Absolute(0), + Token::Dot(1), + Token::Dot(2), + Token::Key(3, "가".to_string()), + Token::Whitespace(6, 0), + Token::OpenArray(7), + ] + , Some(TokenError::Eof) + )); + + run("[-1, 2 ]", + ( + vec![ + Token::OpenArray(0), + Token::Key(1, "-1".to_string()), + Token::Comma(3), + Token::Whitespace(4, 0), + Token::Key(5, "2".to_string()), + Token::Whitespace(6, 0), + Token::CloseArray(7), + ] + , Some(TokenError::Eof) + )); + + run("[ 1 2 , 3 \"abc\" : -10 ]", + ( + vec![ + Token::OpenArray(0), + Token::Whitespace(1, 0), + Token::Key(2, "1".to_string()), + Token::Whitespace(3, 0), + Token::Key(4, "2".to_string()), + Token::Whitespace(5, 0), + Token::Comma(6), + Token::Whitespace(7, 0), + Token::Key(8, "3".to_string()), + Token::Whitespace(9, 0), + Token::DoubleQuoted(10, "abc".to_string()), + Token::Whitespace(15, 0), + Token::Split(16), + Token::Whitespace(17, 0), + Token::Key(18, "-10".to_string()), + Token::Whitespace(21, 0), + Token::CloseArray(22), + ] + , Some(TokenError::Eof) + )); + + run("?(@.a가 <41.01)", + ( + vec![ + Token::Question(0), + Token::OpenParenthesis(1), + Token::At(2), + Token::Dot(3), + Token::Key(4, "a가".to_string()), + Token::Whitespace(8, 0), + Token::Little(9), + Token::Key(10, "41".to_string()), + Token::Dot(12), + Token::Key(13, "01".to_string()), + Token::CloseParenthesis(15), + ] + , Some(TokenError::Eof) + )); + + run("?(@.a <4a.01)", + ( + vec![ + Token::Question(0), + Token::OpenParenthesis(1), + Token::At(2), + Token::Dot(3), + Token::Key(4, "a".to_string()), + Token::Whitespace(5, 0), + Token::Little(6), + Token::Key(7, "4a".to_string()), + Token::Dot(9), + Token::Key(10, "01".to_string()), + Token::CloseParenthesis(12), + ] + , Some(TokenError::Eof) + )); + + run("?($.c>@.d)", ( + vec![ + Token::Question(0), + Token::OpenParenthesis(1), + Token::Absolute(2), + Token::Dot(3), + Token::Key(4, "c".to_string()), + Token::Greater(5), + Token::At(6), + Token::Dot(7), + Token::Key(8, "d".to_string()), + Token::CloseParenthesis(9) + ] + , Some(TokenError::Eof) + )); + } +} \ No newline at end of file diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 7b3432c..4c76103 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -1,5 +1,3 @@ -use std::result::Result; - use super::tokenizer::*; const DUMMY: usize = 0; @@ -7,7 +5,6 @@ const DUMMY: usize = 0; type ParseResult = Result; mod utils { - pub fn string_to_isize(string: &String, msg_handler: F) -> Result where F: Fn() -> String { match string.as_str().parse::() { @@ -52,6 +49,8 @@ pub enum ParseToken { Number(f64), + Bool(bool), + Eof, } @@ -74,50 +73,39 @@ pub struct Node { token: ParseToken, } -pub struct Parser<'a> { - tokenizer: PreloadedTokenizer<'a> -} +pub struct Parser; -impl<'a> Parser<'a> { - pub fn new(input: &'a str) -> Self { - Parser { tokenizer: PreloadedTokenizer::new(input) } +impl Parser { + pub fn compile(input: &str) -> ParseResult { + let mut tokenizer = TokenReader::new(input); + Ok(Self::json_path(&mut tokenizer)?) } - pub fn compile(&mut self) -> ParseResult { - Ok(self.json_path()?) - } - - pub fn parse(&mut self, visitor: &mut V) -> ParseResult<()> { - let node = self.json_path()?; - visitor.visit(node); - Ok(()) - } - - fn json_path(&mut self) -> ParseResult { + fn json_path(tokenizer: &mut TokenReader) -> ParseResult { debug!("#json_path"); - match self.tokenizer.next_token() { + match tokenizer.next_token() { Ok(Token::Absolute(_)) => { - let node = self.node(ParseToken::Absolute); - self.paths(node) + let node = Self::node(ParseToken::Absolute); + Self::paths(node, tokenizer) } _ => { - Err(self.tokenizer.err_msg()) + Err(tokenizer.err_msg()) } } } - fn paths(&mut self, prev: Node) -> ParseResult { + fn paths(prev: Node, tokenizer: &mut TokenReader) -> ParseResult { debug!("#paths"); - match self.tokenizer.peek_token() { + match tokenizer.peek_token() { Ok(Token::Dot(_)) => { - self.eat_token(); - self.paths_dot(prev) + Self::eat_token(tokenizer); + Self::paths_dot(prev, tokenizer) } Ok(Token::OpenArray(_)) => { - self.eat_token(); - self.eat_whitespace(); - let node = self.array(prev)?; - self.paths(node) + Self::eat_token(tokenizer); + Self::eat_whitespace(tokenizer); + let node = Self::array(prev, tokenizer)?; + Self::paths(node, tokenizer) } _ => { Ok(prev) @@ -125,10 +113,10 @@ impl<'a> Parser<'a> { } } - fn paths_dot(&mut self, prev: Node) -> ParseResult { + fn paths_dot(prev: Node, tokenizer: &mut TokenReader) -> ParseResult { debug!("#paths_dot"); - let node = self.path(prev)?; - match self.tokenizer.peek_token() { + let node = Self::path(prev, tokenizer)?; + match tokenizer.peek_token() { Ok(Token::Equal(_)) | Ok(Token::NotEqual(_)) | Ok(Token::Little(_)) @@ -140,319 +128,331 @@ impl<'a> Parser<'a> { Ok(node) } _ => { - self.paths(node) + Self::paths(node, tokenizer) } } } - fn path(&mut self, prev: Node) -> ParseResult { + fn path(prev: Node, tokenizer: &mut TokenReader) -> ParseResult { debug!("#path"); - match self.tokenizer.peek_token() { + match tokenizer.peek_token() { Ok(Token::Dot(_)) => { - self.path_leaves(prev) + Self::path_leaves(prev, tokenizer) } Ok(Token::Asterisk(_)) => { - self.path_in_all(prev) + Self::path_in_all(prev, tokenizer) } Ok(Token::Key(_, _)) => { - self.path_in_key(prev) + Self::path_in_key(prev, tokenizer) } Ok(Token::OpenArray(_)) => { - self.eat_token(); - self.array(prev) + Self::eat_token(tokenizer); + Self::array(prev, tokenizer) } _ => { - Err(self.tokenizer.err_msg()) + Err(tokenizer.err_msg()) } } } - fn path_leaves(&mut self, prev: Node) -> ParseResult { + fn path_leaves(prev: Node, tokenizer: &mut TokenReader) -> ParseResult { debug!("#path_leaves"); - self.eat_token(); - match self.tokenizer.peek_token() { + Self::eat_token(tokenizer); + match tokenizer.peek_token() { Ok(Token::Asterisk(_)) => { - self.path_leaves_all(prev) + Self::path_leaves_all(prev, tokenizer) } Ok(Token::OpenArray(_)) => { - let mut leaves_node = self.node(ParseToken::Leaves); + let mut leaves_node = Self::node(ParseToken::Leaves); leaves_node.left = Some(Box::new(prev)); - Ok(self.paths(leaves_node)?) + Ok(Self::paths(leaves_node, tokenizer)?) } _ => { - self.path_leaves_key(prev) + Self::path_leaves_key(prev, tokenizer) } } } - fn path_leaves_key(&mut self, prev: Node) -> ParseResult { + fn path_leaves_key(prev: Node, tokenizer: &mut TokenReader) -> ParseResult { debug!("#path_leaves_key"); Ok(Node { token: ParseToken::Leaves, left: Some(Box::new(prev)), - right: Some(Box::new(self.key()?)), + right: Some(Box::new(Self::key(tokenizer)?)), }) } - fn path_leaves_all(&mut self, prev: Node) -> ParseResult { + fn path_leaves_all(prev: Node, tokenizer: &mut TokenReader) -> ParseResult { debug!("#path_leaves_all"); - self.eat_token(); + Self::eat_token(tokenizer); Ok(Node { token: ParseToken::Leaves, left: Some(Box::new(prev)), - right: Some(Box::new(self.node(ParseToken::All))), + right: Some(Box::new(Self::node(ParseToken::All))), }) } - fn path_in_all(&mut self, prev: Node) -> ParseResult { + fn path_in_all(prev: Node, tokenizer: &mut TokenReader) -> ParseResult { debug!("#path_in_all"); - self.eat_token(); + Self::eat_token(tokenizer); Ok(Node { token: ParseToken::In, left: Some(Box::new(prev)), - right: Some(Box::new(self.node(ParseToken::All))), + right: Some(Box::new(Self::node(ParseToken::All))), }) } - fn path_in_key(&mut self, prev: Node) -> ParseResult { + fn path_in_key(prev: Node, tokenizer: &mut TokenReader) -> ParseResult { debug!("#path_in_key"); Ok(Node { token: ParseToken::In, left: Some(Box::new(prev)), - right: Some(Box::new(self.key()?)), + right: Some(Box::new(Self::key(tokenizer)?)), }) } - fn key(&mut self) -> ParseResult { + fn key(tokenizer: &mut TokenReader) -> ParseResult { debug!("#key"); - match self.tokenizer.next_token() { + match tokenizer.next_token() { Ok(Token::Key(_, v)) => { - Ok(self.node(ParseToken::Key(v))) + Ok(Self::node(ParseToken::Key(v))) } _ => { - Err(self.tokenizer.err_msg()) + Err(tokenizer.err_msg()) } } } - fn array_quota_value(&mut self) -> ParseResult { + fn boolean(tokenizer: &mut TokenReader) -> ParseResult { + debug!("#boolean"); + match tokenizer.next_token() { + Ok(Token::Key(_, v)) => { + Ok(Self::node(ParseToken::Bool(v.eq_ignore_ascii_case("true")))) + } + _ => { + Err(tokenizer.err_msg()) + } + } + } + + fn array_quota_value(tokenizer: &mut TokenReader) -> ParseResult { debug!("#array_quota_value"); - match self.tokenizer.next_token() { + match tokenizer.next_token() { Ok(Token::SingleQuoted(_, val)) | Ok(Token::DoubleQuoted(_, val)) => { - Ok(self.node(ParseToken::Key(val))) + Ok(Self::node(ParseToken::Key(val))) } Err(TokenError::Eof) => { - Ok(self.node(ParseToken::Eof)) + Ok(Self::node(ParseToken::Eof)) } _ => { - Err(self.tokenizer.err_msg()) + Err(tokenizer.err_msg()) } } } - fn array_start(&mut self, prev: Node) -> ParseResult { + fn array_start(prev: Node, tokenizer: &mut TokenReader) -> ParseResult { debug!("#array_start"); - match self.tokenizer.peek_token() { + match tokenizer.peek_token() { Ok(Token::Question(_)) => { - self.eat_token(); + Self::eat_token(tokenizer); Ok(Node { token: ParseToken::Array, left: Some(Box::new(prev)), - right: Some(Box::new(self.filter()?)), + right: Some(Box::new(Self::filter(tokenizer)?)), }) } Ok(Token::Asterisk(_)) => { - self.eat_token(); + Self::eat_token(tokenizer); Ok(Node { token: ParseToken::Array, left: Some(Box::new(prev)), - right: Some(Box::new(self.node(ParseToken::All))), + right: Some(Box::new(Self::node(ParseToken::All))), }) } _ => { Ok(Node { token: ParseToken::Array, left: Some(Box::new(prev)), - right: Some(Box::new(self.array_value()?)), + right: Some(Box::new(Self::array_value(tokenizer)?)), }) } } } - fn array(&mut self, prev: Node) -> ParseResult { + fn array(prev: Node, tokenizer: &mut TokenReader) -> ParseResult { debug!("#array"); - let ret = self.array_start(prev)?; - self.eat_whitespace(); - self.close_token(ret, Token::CloseArray(DUMMY)) + let ret = Self::array_start(prev, tokenizer)?; + Self::eat_whitespace(tokenizer); + Self::close_token(ret, Token::CloseArray(DUMMY), tokenizer) } - fn array_value_key(&mut self) -> ParseResult { + fn array_value_key(tokenizer: &mut TokenReader) -> ParseResult { debug!("#array_value_key"); - match self.tokenizer.next_token() { + match tokenizer.next_token() { Ok(Token::Key(pos, ref val)) => { - let digit = utils::string_to_isize(val, || self.tokenizer.err_msg_with_pos(pos))?; - self.eat_whitespace(); + let digit = utils::string_to_isize(val, || tokenizer.err_msg_with_pos(pos))?; + Self::eat_whitespace(tokenizer); - match self.tokenizer.peek_token() { + match tokenizer.peek_token() { Ok(Token::Comma(_)) => { - self.union(digit) + Self::union(digit, tokenizer) } Ok(Token::Split(_)) => { - self.range_from(digit) + Self::range_from(digit, tokenizer) } _ => { - Ok(self.node(ParseToken::Number(digit as f64))) + Ok(Self::node(ParseToken::Number(digit as f64))) } } } _ => { - Err(self.tokenizer.err_msg()) + Err(tokenizer.err_msg()) } } } - fn array_value(&mut self) -> ParseResult { + fn array_value(tokenizer: &mut TokenReader) -> ParseResult { debug!("#array_value"); - match self.tokenizer.peek_token() { + match tokenizer.peek_token() { Ok(Token::Key(_, _)) => { - self.array_value_key() + Self::array_value_key(tokenizer) } Ok(Token::Split(_)) => { - self.eat_token(); - self.range_to() + Self::eat_token(tokenizer); + Self::range_to(tokenizer) } Ok(Token::DoubleQuoted(_, _)) | Ok(Token::SingleQuoted(_, _)) => { - self.array_quota_value() + Self::array_quota_value(tokenizer) } Err(TokenError::Eof) => { - Ok(self.node(ParseToken::Eof)) + Ok(Self::node(ParseToken::Eof)) } _ => { - self.eat_token(); - Err(self.tokenizer.err_msg()) + Self::eat_token(tokenizer); + Err(tokenizer.err_msg()) } } } - fn union(&mut self, num: isize) -> ParseResult { + fn union(num: isize, tokenizer: &mut TokenReader) -> ParseResult { debug!("#union"); let mut values = vec![num]; - while match self.tokenizer.peek_token() { + while match tokenizer.peek_token() { Ok(Token::Comma(_)) => true, _ => false } { - self.eat_token(); - self.eat_whitespace(); - match self.tokenizer.next_token() { + Self::eat_token(tokenizer); + Self::eat_whitespace(tokenizer); + match tokenizer.next_token() { Ok(Token::Key(pos, ref val)) => { - let digit = utils::string_to_isize(val, || self.tokenizer.err_msg_with_pos(pos))?; + let digit = utils::string_to_isize(val, || tokenizer.err_msg_with_pos(pos))?; values.push(digit); } _ => { - return Err(self.tokenizer.err_msg()); + return Err(tokenizer.err_msg()); } } } - Ok(self.node(ParseToken::Union(values))) + Ok(Self::node(ParseToken::Union(values))) } - fn range_from(&mut self, num: isize) -> ParseResult { + fn range_from(num: isize, tokenizer: &mut TokenReader) -> ParseResult { debug!("#range_from"); - self.eat_token(); - self.eat_whitespace(); - match self.tokenizer.peek_token() { + Self::eat_token(tokenizer); + Self::eat_whitespace(tokenizer); + match tokenizer.peek_token() { Ok(Token::Key(_, _)) => { - self.range(num) + Self::range(num, tokenizer) } _ => { - Ok(self.node(ParseToken::Range(Some(num), None))) + Ok(Self::node(ParseToken::Range(Some(num), None))) } } } - fn range_to(&mut self) -> ParseResult { + fn range_to(tokenizer: &mut TokenReader) -> ParseResult { debug!("#range_to"); - match self.tokenizer.next_token() { + match tokenizer.next_token() { Ok(Token::Key(pos, ref val)) => { - let digit = utils::string_to_isize(val, || self.tokenizer.err_msg_with_pos(pos))?; - Ok(self.node(ParseToken::Range(None, Some(digit)))) + let digit = utils::string_to_isize(val, || tokenizer.err_msg_with_pos(pos))?; + Ok(Self::node(ParseToken::Range(None, Some(digit)))) } _ => { - Err(self.tokenizer.err_msg()) + Err(tokenizer.err_msg()) } } } - fn range(&mut self, num: isize) -> ParseResult { + fn range(num: isize, tokenizer: &mut TokenReader) -> ParseResult { debug!("#range"); - match self.tokenizer.next_token() { + match tokenizer.next_token() { Ok(Token::Key(pos, ref val)) => { - let digit = utils::string_to_isize(val, || self.tokenizer.err_msg_with_pos(pos))?; - Ok(self.node(ParseToken::Range(Some(num), Some(digit)))) + let digit = utils::string_to_isize(val, || tokenizer.err_msg_with_pos(pos))?; + Ok(Self::node(ParseToken::Range(Some(num), Some(digit)))) } _ => { - Err(self.tokenizer.err_msg()) + Err(tokenizer.err_msg()) } } } - fn filter(&mut self) -> ParseResult { + fn filter(tokenizer: &mut TokenReader) -> ParseResult { debug!("#filter"); - match self.tokenizer.next_token() { + match tokenizer.next_token() { Ok(Token::OpenParenthesis(_)) => { - let ret = self.exprs()?; - self.eat_whitespace(); - self.close_token(ret, Token::CloseParenthesis(DUMMY)) + let ret = Self::exprs(tokenizer)?; + Self::eat_whitespace(tokenizer); + Self::close_token(ret, Token::CloseParenthesis(DUMMY), tokenizer) } Err(TokenError::Eof) => { - Ok(self.node(ParseToken::Eof)) + Ok(Self::node(ParseToken::Eof)) } _ => { - Err(self.tokenizer.err_msg()) + Err(tokenizer.err_msg()) } } } - fn exprs(&mut self) -> ParseResult { - self.eat_whitespace(); + fn exprs(tokenizer: &mut TokenReader) -> ParseResult { + Self::eat_whitespace(tokenizer); debug!("#exprs"); - let node = match self.tokenizer.peek_token() { + let node = match tokenizer.peek_token() { Ok(Token::OpenParenthesis(_)) => { - self.eat_token(); + Self::eat_token(tokenizer); trace!("\t-exprs - open_parenthesis"); - let ret = self.exprs()?; - self.eat_whitespace(); - self.close_token(ret, Token::CloseParenthesis(DUMMY))? + let ret = Self::exprs(tokenizer)?; + Self::eat_whitespace(tokenizer); + Self::close_token(ret, Token::CloseParenthesis(DUMMY), tokenizer)? } _ => { trace!("\t-exprs - else"); - self.expr()? + Self::expr(tokenizer)? } }; - self.eat_whitespace(); - self.condition_expr(node) + Self::eat_whitespace(tokenizer); + Self::condition_expr(node, tokenizer) } - fn condition_expr(&mut self, prev: Node) -> ParseResult { + fn condition_expr(prev: Node, tokenizer: &mut TokenReader) -> ParseResult { debug!("#condition_expr"); - match self.tokenizer.peek_token() { + match tokenizer.peek_token() { Ok(Token::And(_)) => { - self.eat_token(); + Self::eat_token(tokenizer); Ok(Node { token: ParseToken::Filter(FilterToken::And), left: Some(Box::new(prev)), - right: Some(Box::new(self.exprs()?)), + right: Some(Box::new(Self::exprs(tokenizer)?)), }) } Ok(Token::Or(_)) => { - self.eat_token(); + Self::eat_token(tokenizer); Ok(Node { token: ParseToken::Filter(FilterToken::Or), left: Some(Box::new(prev)), - right: Some(Box::new(self.exprs()?)), + right: Some(Box::new(Self::exprs(tokenizer)?)), }) } _ => { @@ -461,18 +461,18 @@ impl<'a> Parser<'a> { } } - fn expr(&mut self) -> ParseResult { + fn expr(tokenizer: &mut TokenReader) -> ParseResult { debug!("#expr"); - let has_prop_candidate = match self.tokenizer.peek_token() { + let has_prop_candidate = match tokenizer.peek_token() { Ok(Token::At(_)) => true, _ => false }; - let node = self.term()?; - self.eat_whitespace(); + let node = Self::term(tokenizer)?; + Self::eat_whitespace(tokenizer); - if match self.tokenizer.peek_token() { + if match tokenizer.peek_token() { Ok(Token::Equal(_)) | Ok(Token::NotEqual(_)) | Ok(Token::Little(_)) @@ -481,91 +481,108 @@ impl<'a> Parser<'a> { | Ok(Token::GreaterOrEqual(_)) => true, _ => false } { - self.op(node) + Self::op(node, tokenizer) } else if has_prop_candidate { Ok(node) } else { - return Err(self.tokenizer.err_msg()); + return Err(tokenizer.err_msg()); } } - fn term_num(&mut self) -> ParseResult { + fn term_num(tokenizer: &mut TokenReader) -> ParseResult { debug!("#term_num"); - match self.tokenizer.next_token() { + match tokenizer.next_token() { Ok(Token::Key(pos, val)) => { - match self.tokenizer.peek_token() { + match tokenizer.peek_token() { Ok(Token::Dot(_)) => { - self.term_num_float(val.as_str()) + Self::term_num_float(val.as_str(), tokenizer) } _ => { - let number = utils::string_to_f64(&val, || self.tokenizer.err_msg_with_pos(pos))?; - Ok(self.node(ParseToken::Number(number))) + let number = utils::string_to_f64(&val, || tokenizer.err_msg_with_pos(pos))?; + Ok(Self::node(ParseToken::Number(number))) } } } Err(TokenError::Eof) => { - Ok(self.node(ParseToken::Eof)) + Ok(Self::node(ParseToken::Eof)) } _ => { - Err(self.tokenizer.err_msg()) + Err(tokenizer.err_msg()) } } } - fn term_num_float(&mut self, mut num: &str) -> ParseResult { + fn term_num_float(mut num: &str, tokenizer: &mut TokenReader) -> ParseResult { debug!("#term_num_float"); - self.eat_token(); - match self.tokenizer.next_token() { + Self::eat_token(tokenizer); + match tokenizer.next_token() { Ok(Token::Key(pos, frac)) => { let mut f = String::new(); f.push_str(&mut num); f.push('.'); f.push_str(frac.as_str()); - let number = utils::string_to_f64(&f, || self.tokenizer.err_msg_with_pos(pos))?; - Ok(self.node(ParseToken::Number(number))) + let number = utils::string_to_f64(&f, || tokenizer.err_msg_with_pos(pos))?; + Ok(Self::node(ParseToken::Number(number))) } _ => { - Err(self.tokenizer.err_msg()) + Err(tokenizer.err_msg()) } } } - fn term(&mut self) -> ParseResult { + fn peek_key(tokenizer: &mut TokenReader) -> Option { + if let Ok(Token::Key(_, k)) = tokenizer.peek_token() { + Some(k.clone()) + } else { + None + } + } + + fn term(tokenizer: &mut TokenReader) -> ParseResult { debug!("#term"); - match self.tokenizer.peek_token() { - Ok(Token::At(_)) => { - self.eat_token(); - let node = self.node(ParseToken::Relative); - match self.tokenizer.peek_token() { - Ok(Token::Whitespace(_, _)) => { - self.eat_whitespace(); - Ok(node) - } - _ => { - self.paths(node) - } + if tokenizer.peek_is(AT) { + Self::eat_token(tokenizer); + let node = Self::node(ParseToken::Relative); + + return match tokenizer.peek_token() { + Ok(Token::Whitespace(_, _)) => { + Self::eat_whitespace(tokenizer); + Ok(node) } - } - Ok(Token::Absolute(_)) => { - self.json_path() - } - Ok(Token::DoubleQuoted(_, _)) - | Ok(Token::SingleQuoted(_, _)) => { - self.array_quota_value() - } - Ok(Token::Key(_, _)) => { - self.term_num() - } - _ => { - Err(self.tokenizer.err_msg()) - } + _ => { + Self::paths(node, tokenizer) + } + }; } + + if tokenizer.peek_is(ABSOLUTE) { + return Self::json_path(tokenizer); + } + + if tokenizer.peek_is(DOUBLE_QUOTA) || tokenizer.peek_is(SINGLE_QUOTA) { + return Self::array_quota_value(tokenizer); + } + + if tokenizer.peek_is(KEY) { + return match Self::peek_key(tokenizer) { + Some(key) => match key.chars().next() { + Some(ch) => match ch { + '-' | '0'...'9' => Self::term_num(tokenizer), + _ => Self::boolean(tokenizer) + } + _ => Err(tokenizer.err_msg()) + }, + _ => Err(tokenizer.err_msg()) + }; + } + + return Err(tokenizer.err_msg()); } - fn op(&mut self, prev: Node) -> ParseResult { + fn op(prev: Node, tokenizer: &mut TokenReader) -> ParseResult { debug!("#op"); - let token = match self.tokenizer.next_token() { + let token = match tokenizer.next_token() { Ok(Token::Equal(_)) => { ParseToken::Filter(FilterToken::Equal) } @@ -588,90 +605,122 @@ impl<'a> Parser<'a> { ParseToken::Eof } _ => { - return Err(self.tokenizer.err_msg()); + return Err(tokenizer.err_msg()); } }; - self.eat_whitespace(); + Self::eat_whitespace(tokenizer); Ok(Node { token, left: Some(Box::new(prev)), - right: Some(Box::new(self.term()?)), + right: Some(Box::new(Self::term(tokenizer)?)), }) } - fn eat_whitespace(&mut self) { - while let Ok(Token::Whitespace(_, _)) = self.tokenizer.peek_token() { - let _ = self.tokenizer.next_token(); + fn eat_whitespace(tokenizer: &mut TokenReader) { + while let Ok(Token::Whitespace(_, _)) = tokenizer.peek_token() { + let _ = tokenizer.next_token(); } } - fn eat_token(&mut self) { - let _ = self.tokenizer.next_token(); + fn eat_token(tokenizer: &mut TokenReader) { + let _ = tokenizer.next_token(); } - fn node(&mut self, token: ParseToken) -> Node { - Node { left: None, right: None, token: token } + fn node(token: ParseToken) -> Node { + Node { left: None, right: None, token } } - fn close_token(&mut self, ret: Node, token: Token) -> ParseResult { + fn close_token(ret: Node, token: Token, tokenizer: &mut TokenReader) -> ParseResult { debug!("#close_token"); - match self.tokenizer.next_token() { + match tokenizer.next_token() { Ok(ref t) if t.partial_eq(token) => { Ok(ret) } _ => { - Err(self.tokenizer.err_msg()) + Err(tokenizer.err_msg()) } } } } pub trait NodeVisitor { - fn visit(&mut self, node: Node) { - match node.token { + fn visit(&mut self, node: &Node) { + match &node.token { ParseToken::Absolute | ParseToken::Relative | ParseToken::All - | ParseToken::Key(_) => { - self.visit_token(node.token); + | ParseToken::Key(_) + | ParseToken::Range(_, _) + | ParseToken::Union(_) + | ParseToken::Number(_) + | ParseToken::Bool(_) => { + self.visit_token(&node.token); } ParseToken::In | ParseToken::Leaves => { - node.left.map(|n| self.visit(*n)); - self.visit_token(node.token); - node.right.map(|n| self.visit(*n)); - } - | ParseToken::Range(_, _) - | ParseToken::Union(_) - | ParseToken::Number(_) => { - self.visit_token(node.token); - } + match &node.left { + Some(n) => self.visit(&*n), + _ => {} + } - | ParseToken::Array => { - node.left.map(|n| self.visit(*n)); - self.visit_token(node.token); - node.right.map(|n| self.visit(*n)); - self.visit_token(ParseToken::ArrayEof); + self.visit_token(&node.token); + + match &node.right { + Some(n) => self.visit(&*n), + _ => {} + } + } + ParseToken::Array => { + match &node.left { + Some(n) => self.visit(&*n), + _ => {} + } + + self.visit_token(&node.token); + + match &node.right { + Some(n) => self.visit(&*n), + _ => {} + } + self.visit_token(&ParseToken::ArrayEof); } ParseToken::Filter(FilterToken::And) | ParseToken::Filter(FilterToken::Or) => { - node.left.map(|n| self.visit(*n)); - node.right.map(|n| self.visit(*n)); - self.visit_token(node.token); + match &node.left { + Some(n) => self.visit(&*n), + _ => {} + } + + match &node.right { + Some(n) => self.visit(&*n), + _ => {} + } + + self.visit_token(&node.token); } ParseToken::Filter(_) => { - node.left.map(|n| self.visit(*n)); + match &node.left { + Some(n) => self.visit(&*n), + _ => {} + } + self.end_term(); - node.right.map(|n| self.visit(*n)); + + match &node.right { + Some(n) => self.visit(&*n), + _ => {} + } + self.end_term(); - self.visit_token(node.token); + + self.visit_token(&node.token); } _ => {} } } - fn visit_token(&mut self, token: ParseToken); + fn visit_token(&mut self, token: &ParseToken); fn end_term(&mut self) {} } \ No newline at end of file diff --git a/src/parser/tokenizer.rs b/src/parser/tokenizer.rs index 9090a06..23a3ac7 100644 --- a/src/parser/tokenizer.rs +++ b/src/parser/tokenizer.rs @@ -3,29 +3,29 @@ use std::result::Result; use super::path_reader::{PathReader, ReaderError}; -const ABSOLUTE: &'static str = "$"; -const DOT: &'static str = "."; -const AT: &'static str = "@"; -const OPEN_ARRAY: &'static str = "["; -const CLOSE_ARRAY: &'static str = "]"; -const ASTERISK: &'static str = "*"; -const QUESTION: &'static str = "?"; -const COMMA: &'static str = ","; -const SPLIT: &'static str = ":"; -const OPEN_PARENTHESIS: &'static str = "("; -const CLOSE_PARENTHESIS: &'static str = ")"; -const KEY: &'static str = "Key"; -const DOUBLE_QUOTA: &'static str = "\""; -const SINGLE_QUOTA: &'static str = "'"; -const EQUAL: &'static str = "=="; -const GREATER_OR_EQUAL: &'static str = ">="; -const GREATER: &'static str = ">"; -const LITTLE: &'static str = "<"; -const LITTLE_OR_EQUAL: &'static str = "<="; -const NOT_EQUAL: &'static str = "!="; -const AND: &'static str = "&&"; -const OR: &'static str = "||"; -const WHITESPACE: &'static str = " "; +pub const ABSOLUTE: &'static str = "$"; +pub const DOT: &'static str = "."; +pub const AT: &'static str = "@"; +pub const OPEN_ARRAY: &'static str = "["; +pub const CLOSE_ARRAY: &'static str = "]"; +pub const ASTERISK: &'static str = "*"; +pub const QUESTION: &'static str = "?"; +pub const COMMA: &'static str = ","; +pub const SPLIT: &'static str = ":"; +pub const OPEN_PARENTHESIS: &'static str = "("; +pub const CLOSE_PARENTHESIS: &'static str = ")"; +pub const KEY: &'static str = "Key"; +pub const DOUBLE_QUOTA: &'static str = "\""; +pub const SINGLE_QUOTA: &'static str = "'"; +pub const EQUAL: &'static str = "=="; +pub const GREATER_OR_EQUAL: &'static str = ">="; +pub const GREATER: &'static str = ">"; +pub const LITTLE: &'static str = "<"; +pub const LITTLE_OR_EQUAL: &'static str = "<="; +pub const NOT_EQUAL: &'static str = "!="; +pub const AND: &'static str = "&&"; +pub const OR: &'static str = "||"; +pub const WHITESPACE: &'static str = " "; const CH_DOLLA: char = '$'; const CH_DOT: char = '.'; @@ -91,6 +91,10 @@ impl Token { self.to_simple() == other.to_simple() } + pub fn simple_eq(&self, str_token: &str) -> bool { + self.to_simple() == str_token + } + fn to_simple(&self) -> &'static str { match self { Token::Absolute(_) => ABSOLUTE, @@ -275,7 +279,7 @@ impl<'a> Tokenizer<'a> { } } -pub struct PreloadedTokenizer<'a> { +pub struct TokenReader<'a> { origin_input: &'a str, err: TokenError, err_pos: usize, @@ -283,7 +287,7 @@ pub struct PreloadedTokenizer<'a> { curr_pos: Option, } -impl<'a> PreloadedTokenizer<'a> { +impl<'a> TokenReader<'a> { pub fn new(input: &'a str) -> Self { let mut tokenizer = Tokenizer::new(input); let mut tokens = vec![]; @@ -293,7 +297,7 @@ impl<'a> PreloadedTokenizer<'a> { tokens.insert(0, (tokenizer.current_pos(), t)); } Err(e) => { - return PreloadedTokenizer { + return TokenReader { origin_input: input.clone(), err: e, err_pos: tokenizer.current_pos(), @@ -305,6 +309,13 @@ impl<'a> PreloadedTokenizer<'a> { } } + pub fn peek_is(&self, simple_token: &str) -> bool { + match self.peek_token() { + Ok(t) => t.simple_eq(simple_token), + _ => false + } + } + pub fn peek_token(&self) -> Result<&Token, TokenError> { match self.tokens.last() { Some((_, t)) => { diff --git a/src/ref_value/de.rs b/src/ref_value/de.rs deleted file mode 100644 index a5765ca..0000000 --- a/src/ref_value/de.rs +++ /dev/null @@ -1,1058 +0,0 @@ -use std::borrow::Cow; -use std::fmt; -use std::ops::Deref; -use std::result::Result; -use std::vec; - -use indexmap::IndexMap; -use serde::{self, Deserialize, Deserializer}; -use serde::de::{DeserializeSeed, EnumAccess, IntoDeserializer, MapAccess, SeqAccess, VariantAccess, Visitor}; -use serde_json::Value; - -use super::model::*; -use super::serde_error::SerdeError; - -/// -/// see `serde_json/value/de.rs` -/// - -macro_rules! deserialize_prim_number { - ($method:ident) => { - fn $method(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self { - RefValue::Number(ref n) => { - match n.deserialize_any(visitor) { - Ok(v) => Ok(v), - Err(e) => Err(SerdeError::new(format!("{:?}", e))) - } - } - _ => Err(SerdeError::from_str("invalid type")), - } - } - } -} - -impl<'de> Deserialize<'de> for RefValue { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct RefValueVisitor {} - - impl<'de> Visitor<'de> for RefValueVisitor { - type Value = RefValue; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("any valid JSON value") - } - - #[inline] - fn visit_bool(self, v: bool) -> Result - where - E: serde::de::Error, { - Ok(RefValue::Bool(v)) - } - - #[inline] - fn visit_i64(self, v: i64) -> Result - where - E: serde::de::Error, { - Ok(RefValue::Number(v.into())) - } - - #[inline] - fn visit_u64(self, v: u64) -> Result - where - E: serde::de::Error, { - Ok(RefValue::Number(v.into())) - } - - #[inline] - fn visit_f64(self, v: f64) -> Result - where - E: serde::de::Error, { - let n: Value = v.into(); - if let Value::Number(n) = n { - Ok(RefValue::Number(n)) - } else { - unreachable!() - } - } - - #[inline] - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, { - self.visit_string(String::from(v)) - } - - #[inline] - fn visit_string(self, v: String) -> Result - where - E: serde::de::Error, { - Ok(RefValue::String(v)) - } - - #[inline] - fn visit_none(self) -> Result - where - E: serde::de::Error, { - Ok(RefValue::Null) - } - - #[inline] - fn visit_some(self, deserializer: D) -> Result where - D: Deserializer<'de>, { - Deserialize::deserialize(deserializer) - } - - #[inline] - fn visit_unit(self) -> Result - where - E: serde::de::Error, { - Ok(RefValue::Null) - } - - #[inline] - fn visit_seq(self, mut visitor: A) -> Result - where - A: SeqAccess<'de>, { - let mut vec = Vec::new(); - - while let Some(elem) = visitor.next_element()? { - let e: RefValue = elem; - let v: RefValueWrapper = e.into(); - vec.push(v); - } - - Ok(RefValue::Array(vec)) - } - - fn visit_map(self, mut visitor: A) -> Result - where - A: MapAccess<'de>, { - let mut values = IndexMap::new(); - match visitor.next_key() { - Ok(Some(first_key)) => { - let next: RefValue = visitor.next_value()?; - values.insert(first_key, next.into()); - while let Some((k, v)) = visitor.next_entry()? { - let value: RefValue = v; - values.insert(k, value.into()); - } - Ok(RefValue::Object(values)) - } - _ => Ok(RefValue::Object(IndexMap::new())), - } - } - } - - deserializer.deserialize_any(RefValueVisitor {}) - } -} - -fn visit_array<'de, V>(array: Vec, visitor: V) -> Result - where - V: Visitor<'de>, -{ - let mut deserializer = SeqDeserializer::new(array); - let seq = visitor.visit_seq(&mut deserializer)?; - let remaining = deserializer.iter.len(); - if remaining == 0 { - Ok(seq) - } else { - Err(SerdeError::from_str("fewer elements in array")) - } -} - -fn visit_object<'de, V>(object: IndexMap, visitor: V) -> Result - where - V: Visitor<'de>, -{ - let mut deserializer = MapDeserializer::new(object); - let map = visitor.visit_map(&mut deserializer)?; - let remaining = deserializer.iter.len(); - if remaining == 0 { - Ok(map) - } else { - Err(SerdeError::from_str("fewer elements in map")) - } -} - -fn to_vec(vec: &Vec) -> Vec { - vec.iter().map(|v| v.clone()).collect() -} - -fn to_map(object: &IndexMap) -> IndexMap { - let mut map = IndexMap::new(); - for (k, v) in object { - map.insert(k.to_string(), v.clone()); - } - map -} - -impl<'de> serde::Deserializer<'de> for RefValue { - type Error = SerdeError; - - #[inline] - fn deserialize_any(self, visitor: V) -> Result where - V: Visitor<'de> { - match self { - RefValue::Null => visitor.visit_unit(), - RefValue::Bool(v) => visitor.visit_bool(v), - RefValue::Number(n) => { - n.deserialize_any(visitor).map_err(|e| SerdeError::new(format!("{:?}", e))) - } - RefValue::String(v) => visitor.visit_string(v), - RefValue::Array(array) => visit_array(array, visitor), - RefValue::Object(object) => visit_object(object, visitor) - } - } - - deserialize_prim_number!(deserialize_i8); - deserialize_prim_number!(deserialize_i16); - deserialize_prim_number!(deserialize_i32); - deserialize_prim_number!(deserialize_i64); - deserialize_prim_number!(deserialize_u8); - deserialize_prim_number!(deserialize_u16); - deserialize_prim_number!(deserialize_u32); - deserialize_prim_number!(deserialize_u64); - deserialize_prim_number!(deserialize_f32); - deserialize_prim_number!(deserialize_f64); - - #[inline] - fn deserialize_option(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self { - RefValue::Null => visitor.visit_none(), - _ => visitor.visit_some(self), - } - } - - #[inline] - fn deserialize_enum( - self, - _name: &str, - _variants: &'static [&'static str], - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - let (variant, value) = match self { - RefValue::Object(value) => { - let mut iter = value.into_iter(); - let (variant, value) = match iter.next() { - Some(v) => v, - None => { - return Err(SerdeError::from_str("map with a single key")); - } - }; - if iter.next().is_some() { - return Err(SerdeError::from_str("map with a single key")); - } - (variant, Some(value)) - } - RefValue::String(variant) => (variant, None), - _ => { - return Err(SerdeError::from_str("string or map")); - } - }; - - visitor.visit_enum(EnumDeserializer { - variant: variant, - value: value, - }) - } - - #[inline] - fn deserialize_newtype_struct( - self, - name: &'static str, - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - let _ = name; - visitor.visit_newtype_struct(self) - } - - fn deserialize_bool(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self { - RefValue::Bool(v) => visitor.visit_bool(v), - _ => Err(SerdeError::from_str("invalid type: bool")), - } - } - - fn deserialize_char(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_string(visitor) - } - - fn deserialize_str(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_string(visitor) - } - - fn deserialize_string(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self { - RefValue::String(v) => visitor.visit_string(v), - _ => Err(SerdeError::from_str("invalid type: string")), - } - } - - fn deserialize_bytes(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_byte_buf(visitor) - } - - fn deserialize_byte_buf(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self { - RefValue::String(v) => visitor.visit_string(v), - RefValue::Array(v) => visit_array(v, visitor), - _ => Err(SerdeError::from_str("invalid type: string or array")), - } - } - - fn deserialize_unit(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self { - RefValue::Null => visitor.visit_unit(), - _ => Err(SerdeError::from_str("invalid type: null")), - } - } - - fn deserialize_unit_struct( - self, - _name: &'static str, - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - self.deserialize_unit(visitor) - } - - fn deserialize_seq(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self { - RefValue::Array(v) => visit_array(v, visitor), - _ => Err(SerdeError::from_str("invalid type: array")), - } - } - - fn deserialize_tuple(self, _len: usize, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_seq(visitor) - } - - fn deserialize_tuple_struct( - self, - _name: &'static str, - _len: usize, - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - self.deserialize_seq(visitor) - } - - fn deserialize_map(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self { - RefValue::Object(v) => visit_object(v, visitor), - _ => Err(SerdeError::from_str("invalid type: object")) - } - } - - fn deserialize_struct( - self, - _name: &'static str, - _fields: &'static [&'static str], - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - match self { - RefValue::Array(v) => visit_array(v, visitor), - RefValue::Object(v) => visit_object(v, visitor), - _ => Err(SerdeError::from_str("invalid type: array, object")) - } - } - - fn deserialize_identifier(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_string(visitor) - } - - fn deserialize_ignored_any(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - drop(self); - visitor.visit_unit() - } -} - -impl<'de> serde::Deserializer<'de> for &RefValue { - type Error = SerdeError; - - #[inline] - fn deserialize_any(self, visitor: V) -> Result where - V: Visitor<'de> { - match self { - RefValue::Null => visitor.visit_unit(), - RefValue::Bool(v) => visitor.visit_bool(*v), - RefValue::Number(n) => { - n.deserialize_any(visitor).map_err(|e| SerdeError::new(format!("{:?}", e))) - } - RefValue::String(v) => visitor.visit_string(v.to_string()), - RefValue::Array(array) => visit_array(to_vec(array), visitor), - RefValue::Object(object) => { - visit_object(to_map(object), visitor) - } - } - } - - deserialize_prim_number!(deserialize_i8); - deserialize_prim_number!(deserialize_i16); - deserialize_prim_number!(deserialize_i32); - deserialize_prim_number!(deserialize_i64); - deserialize_prim_number!(deserialize_u8); - deserialize_prim_number!(deserialize_u16); - deserialize_prim_number!(deserialize_u32); - deserialize_prim_number!(deserialize_u64); - deserialize_prim_number!(deserialize_f32); - deserialize_prim_number!(deserialize_f64); - - #[inline] - fn deserialize_option(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self { - RefValue::Null => visitor.visit_none(), - _ => visitor.visit_some(self), - } - } - - #[inline] - fn deserialize_enum( - self, - _name: &str, - _variants: &'static [&'static str], - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - let (variant, value) = match self { - RefValue::Object(value) => { - let mut iter = value.into_iter(); - let (variant, value) = match iter.next() { - Some(v) => v, - None => { - return Err(SerdeError::from_str("map with a single key")); - } - }; - if iter.next().is_some() { - return Err(SerdeError::from_str("map with a single key")); - } - (variant, Some(value)) - } - RefValue::String(variant) => (variant, None), - _ => { - return Err(SerdeError::from_str("string or map")); - } - }; - - visitor.visit_enum(EnumDeserializer { - variant: variant.to_string(), - value: match value { - Some(v) => Some(v.clone()), - _ => None - }, - }) - } - - #[inline] - fn deserialize_newtype_struct( - self, - name: &'static str, - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - let _ = name; - visitor.visit_newtype_struct(self) - } - - fn deserialize_bool(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self { - RefValue::Bool(v) => visitor.visit_bool(*v), - _ => Err(SerdeError::from_str("invalid type: bool")), - } - } - - fn deserialize_char(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_string(visitor) - } - - fn deserialize_str(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_string(visitor) - } - - fn deserialize_string(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self { - RefValue::String(v) => visitor.visit_string(v.to_string()), - _ => Err(SerdeError::from_str("invalid type: string")), - } - } - - fn deserialize_bytes(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_byte_buf(visitor) - } - - fn deserialize_byte_buf(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self { - RefValue::String(v) => visitor.visit_string(v.to_string()), - RefValue::Array(vec) => visit_array(to_vec(vec), visitor), - _ => Err(SerdeError::from_str("invalid type: string or array")), - } - } - - fn deserialize_unit(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self { - RefValue::Null => visitor.visit_unit(), - _ => Err(SerdeError::from_str("invalid type: null")), - } - } - - fn deserialize_unit_struct( - self, - _name: &'static str, - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - self.deserialize_unit(visitor) - } - - fn deserialize_seq(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self { - RefValue::Array(vec) => visit_array(to_vec(vec), visitor), - _ => Err(SerdeError::from_str("invalid type: array")), - } - } - - fn deserialize_tuple(self, _len: usize, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_seq(visitor) - } - - fn deserialize_tuple_struct( - self, - _name: &'static str, - _len: usize, - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - self.deserialize_seq(visitor) - } - - fn deserialize_map(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self { - RefValue::Object(object) => { - visit_object(to_map(object), visitor) - } - _ => Err(SerdeError::from_str("invalid type: object")) - } - } - - fn deserialize_struct( - self, - _name: &'static str, - _fields: &'static [&'static str], - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - match self { - RefValue::Array(vec) => visit_array(to_vec(vec), visitor), - RefValue::Object(object) => { - visit_object(to_map(object), visitor) - } - _ => Err(SerdeError::from_str("invalid type: array, object")) - } - } - - fn deserialize_identifier(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_string(visitor) - } - - fn deserialize_ignored_any(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - drop(self); - visitor.visit_unit() - } -} - - -struct SeqDeserializer { - iter: vec::IntoIter, -} - -impl SeqDeserializer { - fn new(vec: Vec) -> Self { - SeqDeserializer { - iter: vec.into_iter(), - } - } -} - -impl<'de> serde::Deserializer<'de> for SeqDeserializer { - type Error = SerdeError; - - #[inline] - fn deserialize_any(mut self, visitor: V) -> Result - where - V: Visitor<'de>, - { - let len = self.iter.len(); - if len == 0 { - visitor.visit_unit() - } else { - let ret = visitor.visit_seq(&mut self)?; - let remaining = self.iter.len(); - if remaining == 0 { - Ok(ret) - } else { - Err(SerdeError::from_str("fewer elements in array")) - } - } - } - - forward_to_deserialize_any! { - bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string - bytes byte_buf option unit unit_struct newtype_struct seq tuple - tuple_struct map struct enum identifier ignored_any - } -} - -impl<'de> SeqAccess<'de> for SeqDeserializer { - type Error = SerdeError; - - fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> - where - T: serde::de::DeserializeSeed<'de>, - { - match self.iter.next() { - Some(value) => seed.deserialize(value.deref()).map(Some), - None => Ok(None), - } - } - - fn size_hint(&self) -> Option { - match self.iter.size_hint() { - (lower, Some(upper)) if lower == upper => Some(upper), - _ => None, - } - } -} - -struct MapDeserializer { - iter: as IntoIterator>::IntoIter, - value: Option, -} - -impl MapDeserializer { - fn new(map: IndexMap) -> Self { - MapDeserializer { - iter: map.into_iter(), - value: None, - } - } -} - -impl<'de> MapAccess<'de> for MapDeserializer { - type Error = SerdeError; - - fn next_key_seed(&mut self, seed: T) -> Result, Self::Error> - where - T: DeserializeSeed<'de>, - { - match self.iter.next() { - Some((key, value)) => { - self.value = Some(value); - let key_de = MapKeyDeserializer { - key: Cow::Owned(key), - }; - seed.deserialize(key_de).map(Some) - } - None => Ok(None), - } - } - - fn next_value_seed(&mut self, seed: T) -> Result - where - T: DeserializeSeed<'de>, - { - match self.value.take() { - Some(value) => seed.deserialize(value.deref()), - None => Err(serde::de::Error::custom("value is missing")), - } - } - - fn size_hint(&self) -> Option { - match self.iter.size_hint() { - (lower, Some(upper)) if lower == upper => Some(upper), - _ => None, - } - } -} - -impl<'de> serde::Deserializer<'de> for MapDeserializer { - type Error = SerdeError; - - #[inline] - fn deserialize_any(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - visitor.visit_map(self) - } - - forward_to_deserialize_any! { - bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string - bytes byte_buf option unit unit_struct newtype_struct seq tuple - tuple_struct map struct enum identifier ignored_any - } -} - -struct MapKeyDeserializer<'de> { - key: Cow<'de, str>, -} - -macro_rules! deserialize_integer_key { - ($method:ident => $visit:ident) => { - fn $method(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match (self.key.parse(), self.key) { - (Ok(integer), _) => visitor.$visit(integer), - (Err(_), Cow::Borrowed(s)) => visitor.visit_borrowed_str(s), - (Err(_), Cow::Owned(s)) => visitor.visit_string(s), - } - } - } -} - -impl<'de> serde::Deserializer<'de> for MapKeyDeserializer<'de> { - type Error = SerdeError; - - fn deserialize_any(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - BorrowedCowStrDeserializer::new(self.key).deserialize_any(visitor) - } - - deserialize_integer_key!(deserialize_i8 => visit_i8); - deserialize_integer_key!(deserialize_i16 => visit_i16); - deserialize_integer_key!(deserialize_i32 => visit_i32); - deserialize_integer_key!(deserialize_i64 => visit_i64); - deserialize_integer_key!(deserialize_u8 => visit_u8); - deserialize_integer_key!(deserialize_u16 => visit_u16); - deserialize_integer_key!(deserialize_u32 => visit_u32); - deserialize_integer_key!(deserialize_u64 => visit_u64); - - serde_if_integer128! { - deserialize_integer_key!(deserialize_i128 => visit_i128); - deserialize_integer_key!(deserialize_u128 => visit_u128); - } - - #[inline] - fn deserialize_option(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - // Map keys cannot be null. - visitor.visit_some(self) - } - - #[inline] - fn deserialize_newtype_struct( - self, - _name: &'static str, - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - visitor.visit_newtype_struct(self) - } - - fn deserialize_enum( - self, - name: &'static str, - variants: &'static [&'static str], - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - self.key - .into_deserializer() - .deserialize_enum(name, variants, visitor) - } - - forward_to_deserialize_any! { - bool f32 f64 char str string bytes byte_buf unit unit_struct seq tuple - tuple_struct map struct identifier ignored_any - } -} - -struct BorrowedCowStrDeserializer<'de> { - value: Cow<'de, str>, -} - -impl<'de> BorrowedCowStrDeserializer<'de> { - fn new(value: Cow<'de, str>) -> Self { - BorrowedCowStrDeserializer { value: value } - } -} - -impl<'de> serde::Deserializer<'de> for BorrowedCowStrDeserializer<'de> { - type Error = SerdeError; - - fn deserialize_any(self, visitor: V) -> Result - where - V: serde::de::Visitor<'de>, - { - match self.value { - Cow::Borrowed(string) => visitor.visit_borrowed_str(string), - Cow::Owned(string) => visitor.visit_string(string), - } - } - - fn deserialize_enum( - self, - _name: &str, - _variants: &'static [&'static str], - visitor: V, - ) -> Result - where - V: serde::de::Visitor<'de>, - { - visitor.visit_enum(self) - } - - forward_to_deserialize_any! { - bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string - bytes byte_buf option unit unit_struct newtype_struct seq tuple - tuple_struct map struct identifier ignored_any - } -} - -impl<'de> serde::de::EnumAccess<'de> for BorrowedCowStrDeserializer<'de> { - type Error = SerdeError; - type Variant = UnitOnly; - - fn variant_seed(self, seed: T) -> Result<(T::Value, Self::Variant), Self::Error> - where - T: serde::de::DeserializeSeed<'de>, - { - let value = seed.deserialize(self)?; - Ok((value, UnitOnly)) - } -} - -struct UnitOnly; - -impl<'de> serde::de::VariantAccess<'de> for UnitOnly { - type Error = SerdeError; - - fn unit_variant(self) -> Result<(), Self::Error> { - Ok(()) - } - - fn newtype_variant_seed(self, _seed: T) -> Result - where - T: serde::de::DeserializeSeed<'de>, - { - Err(SerdeError::from_str("newtype variant")) - } - - fn tuple_variant(self, _len: usize, _visitor: V) -> Result - where - V: serde::de::Visitor<'de>, - { - Err(SerdeError::from_str("tuple variant")) - } - - fn struct_variant( - self, - _fields: &'static [&'static str], - _visitor: V, - ) -> Result - where - V: serde::de::Visitor<'de>, - { - Err(SerdeError::from_str("struct variant")) - } -} - -struct EnumDeserializer { - variant: String, - value: Option, -} - -impl<'de> EnumAccess<'de> for EnumDeserializer { - type Error = SerdeError; - type Variant = VariantDeserializer; - - fn variant_seed(self, seed: V) -> Result<(V::Value, VariantDeserializer), Self::Error> - where - V: DeserializeSeed<'de>, - { - let variant = self.variant.into_deserializer(); - let visitor = VariantDeserializer { value: self.value }; - seed.deserialize(variant).map(|v| (v, visitor)) - } -} - -struct VariantDeserializer { - value: Option, -} - -impl<'de> VariantAccess<'de> for VariantDeserializer { - type Error = SerdeError; - - fn unit_variant(self) -> Result<(), Self::Error> { - match self.value { - Some(value) => Deserialize::deserialize(value.deref()), - None => Ok(()), - } - } - - fn newtype_variant_seed(self, seed: T) -> Result - where - T: DeserializeSeed<'de>, - { - match self.value { - Some(value) => seed.deserialize(value.deref()), - None => Err(SerdeError::from_str("newtype variant")), - } - } - - fn tuple_variant(self, _len: usize, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self.value { - Some(ref_value) => { - match ref_value.deref() { - RefValue::Array(vec) => { - serde::Deserializer::deserialize_any(SeqDeserializer::new(to_vec(vec)), visitor) - } - _ => Err(SerdeError::from_str("tuple variant")) - } - } - None => Err(SerdeError::from_str("tuple variant")), - } - } - - fn struct_variant( - self, - _fields: &'static [&'static str], - visitor: V, - ) -> Result - where - V: Visitor<'de>, - { - match self.value { - Some(ref_value) => { - match ref_value.deref() { - RefValue::Object(vec) => { - serde::Deserializer::deserialize_any(MapDeserializer::new(to_map(vec)), visitor) - } - _ => Err(SerdeError::from_str("struct variant")) - } - } - _ => Err(SerdeError::from_str("struct variant")), - } - } -} \ No newline at end of file diff --git a/src/ref_value/mod.rs b/src/ref_value/mod.rs deleted file mode 100644 index 05dec5f..0000000 --- a/src/ref_value/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod model; -pub mod de; -pub mod ser; -pub mod serde_error; \ No newline at end of file diff --git a/src/ref_value/model.rs b/src/ref_value/model.rs deleted file mode 100644 index 694dd9d..0000000 --- a/src/ref_value/model.rs +++ /dev/null @@ -1,293 +0,0 @@ -use std::hash::{Hash, Hasher}; -use std::ops::Deref; -use std::sync::Arc; - -use indexmap::map::IndexMap; -use serde::ser::Serialize; -use serde_json::{Number, Value}; -use std::collections::hash_map::DefaultHasher; - -type TypeRefValue = Arc>; - -#[derive(Debug)] -pub struct RefValueWrapper { - data: TypeRefValue -} - -impl PartialEq for RefValueWrapper { - fn eq(&self, other: &RefValueWrapper) -> bool { - Arc::ptr_eq(&self.data, &other.data) - } -} - -impl Eq for RefValueWrapper {} - -impl Deref for RefValueWrapper { - type Target = RefValue; - - fn deref(&self) -> &Self::Target { - &(**self.data) - } -} - -impl Hash for RefValueWrapper { - fn hash(&self, state: &mut H) { - self.deref().hash(state) - } -} - -impl Clone for RefValueWrapper { - fn clone(&self) -> Self { - RefValueWrapper { - data: self.data.clone() - } - } -} - -/// -/// serde_json::Value 참고 -/// -pub trait RefIndex { - fn index_into<'v>(&self, v: &'v RefValue) -> Option<&'v RefValueWrapper>; - fn index_into_mut<'v>(&self, v: &'v mut RefValue) -> Option<&'v mut RefValueWrapper>; - fn index_or_insert<'v>(&self, v: &'v mut RefValue) -> &'v mut RefValueWrapper; -} - -impl RefIndex for usize { - fn index_into<'v>(&self, v: &'v RefValue) -> Option<&'v RefValueWrapper> { - match *v { - RefValue::Array(ref vec) => vec.get(*self), - _ => None, - } - } - fn index_into_mut<'v>(&self, v: &'v mut RefValue) -> Option<&'v mut RefValueWrapper> { - match *v { - RefValue::Array(ref mut vec) => vec.get_mut(*self), - _ => None, - } - } - fn index_or_insert<'v>(&self, v: &'v mut RefValue) -> &'v mut RefValueWrapper { - match *v { - RefValue::Array(ref mut vec) => { - let len = vec.len(); - vec.get_mut(*self).unwrap_or_else(|| { - panic!( - "cannot access index {} of JSON array of length {}", - self, len - ) - }) - } - _ => panic!("cannot access index {} of JSON {:?}", self, v), - } - } -} - -impl RefIndex for str { - fn index_into<'v>(&self, v: &'v RefValue) -> Option<&'v RefValueWrapper> { - match *v { - RefValue::Object(ref map) => map.get(self), - _ => None, - } - } - fn index_into_mut<'v>(&self, v: &'v mut RefValue) -> Option<&'v mut RefValueWrapper> { - match *v { - RefValue::Object(ref mut map) => map.get_mut(self), - _ => None, - } - } - fn index_or_insert<'v>(&self, v: &'v mut RefValue) -> &'v mut RefValueWrapper { - if let RefValue::Null = *v { - *v = RefValue::Object(IndexMap::new()); - } - match *v { - RefValue::Object(ref mut map) => { - map.entry(self.to_owned()).or_insert(RefValue::Null.into()) - } - _ => panic!("cannot access key {:?} in JSON {:?}", self, v), - } - } -} - -impl RefIndex for String { - fn index_into<'v>(&self, v: &'v RefValue) -> Option<&'v RefValueWrapper> { - self[..].index_into(v) - } - fn index_into_mut<'v>(&self, v: &'v mut RefValue) -> Option<&'v mut RefValueWrapper> { - self[..].index_into_mut(v) - } - fn index_or_insert<'v>(&self, v: &'v mut RefValue) -> &'v mut RefValueWrapper { - self[..].index_or_insert(v) - } -} - -#[derive(Debug)] -pub enum RefValue { - Null, - Bool(bool), - Number(Number), - String(String), - Array(Vec), - Object(IndexMap), -} - -impl PartialEq for RefValue { - fn eq(&self, other: &RefValue) -> bool { - let mut hasher1 = DefaultHasher::new(); - let mut hasher2 = DefaultHasher::new(); - - self.hash(&mut hasher1); - other.hash(&mut hasher2); - - hasher1.finish() == hasher2.finish() - } -} - -static REF_VALUE_NULL: &'static str = "$jsonpath::ref_value::model::RefValue::Null"; - -impl Hash for RefValue { - fn hash(&self, state: &mut H) { - match self { - RefValue::Null => { - REF_VALUE_NULL.hash(state) - } - RefValue::Bool(b) => { - b.hash(state) - } - RefValue::Number(n) => { - if n.is_f64() { - n.as_f64().unwrap().to_string().hash(state) - } else if n.is_i64() { - n.as_i64().unwrap().hash(state); - } else { - n.as_u64().unwrap().hash(state); - } - } - RefValue::String(s) => { - s.hash(state) - } - RefValue::Object(map) => { - for (k, v) in map { - k.hash(state); - v.hash(state); - } - } - RefValue::Array(v) => { - for i in v { - i.hash(state); - } - } - } - } -} - -impl Eq for RefValue {} - -impl RefValue { - pub fn get(&self, index: I) -> Option<&RefValueWrapper> { - index.index_into(self) - } - - pub fn is_object(&self) -> bool { - self.as_object().is_some() - } - - pub fn as_object(&self) -> Option<&IndexMap> { - match *self { - RefValue::Object(ref map) => Some(map), - _ => None, - } - } - - pub fn is_array(&self) -> bool { - self.as_array().is_some() - } - - pub fn as_array(&self) -> Option<&Vec> { - match *self { - RefValue::Array(ref array) => Some(&*array), - _ => None, - } - } - - pub fn is_string(&self) -> bool { - self.as_str().is_some() - } - - pub fn as_str(&self) -> Option<&str> { - match *self { - RefValue::String(ref s) => Some(s), - _ => None, - } - } - - pub fn is_number(&self) -> bool { - match *self { - RefValue::Number(_) => true, - _ => false, - } - } - - pub fn as_number(&self) -> Option { - match *self { - RefValue::Number(ref n) => Some(n.clone()), - _ => None, - } - } - - pub fn is_boolean(&self) -> bool { - self.as_bool().is_some() - } - - pub fn as_bool(&self) -> Option { - match *self { - RefValue::Bool(b) => Some(b), - _ => None, - } - } - - pub fn is_null(&self) -> bool { - self.as_null().is_some() - } - - pub fn as_null(&self) -> Option<()> { - match *self { - RefValue::Null => Some(()), - _ => None, - } - } -} - -impl Into for RefValue { - fn into(self) -> RefValueWrapper { - RefValueWrapper { - data: Arc::new(Box::new(self)) - } - } -} - -impl Into for &Value { - fn into(self) -> RefValue { - match self.serialize(super::ser::Serializer) { - Ok(v) => v, - Err(e) => panic!("Error Value into RefValue: {:?}", e) - } - } -} - -impl Into for &Value { - fn into(self) -> RefValueWrapper { - match self.serialize(super::ser::Serializer) { - Ok(v) => v.into(), - Err(e) => panic!("Error Value into RefValue: {:?}", e) - } - } -} - -impl Into for &RefValueWrapper { - fn into(self) -> Value { - match serde_json::to_value(self.deref()) { - Ok(v) => v, - Err(e) => panic!("Error RefValueWrapper into Value: {:?}", e) - } - } -} \ No newline at end of file diff --git a/src/ref_value/ser.rs b/src/ref_value/ser.rs deleted file mode 100644 index c4b65fb..0000000 --- a/src/ref_value/ser.rs +++ /dev/null @@ -1,624 +0,0 @@ -use std::result::Result; - -use indexmap::IndexMap; -use serde::{self, Serialize}; -use serde::ser::Impossible; - -use ref_value::model::{RefValue, RefValueWrapper}; - -use super::serde_error::SerdeError; - -/// -/// see `serde_json/value/ser.rs` -/// -impl Serialize for RefValue { - #[inline] - fn serialize(&self, serializer: S) -> Result - where - S: ::serde::Serializer, - { - match *self { - RefValue::Null => serializer.serialize_unit(), - RefValue::Bool(b) => serializer.serialize_bool(b), - RefValue::Number(ref n) => n.serialize(serializer), - RefValue::String(ref s) => serializer.serialize_str(s), - RefValue::Array(ref v) => { - use std::ops::Deref; - let v: Vec<&RefValue> = v.iter().map(|v| v.deref()).collect(); - v.serialize(serializer) - } - RefValue::Object(ref m) => { - use serde::ser::SerializeMap; - use std::ops::Deref; - let mut map = try!(serializer.serialize_map(Some(m.len()))); - for (k, v) in m { - try!(map.serialize_key(k)); - try!(map.serialize_value(v.deref())); - } - map.end() - } - } - } -} - -pub struct Serializer; - -impl serde::Serializer for Serializer { - type Ok = RefValue; - type Error = SerdeError; - - type SerializeSeq = SerializeVec; - type SerializeTuple = SerializeVec; - type SerializeTupleStruct = SerializeVec; - type SerializeTupleVariant = SerializeTupleVariant; - type SerializeMap = SerializeMap; - type SerializeStruct = SerializeMap; - type SerializeStructVariant = SerializeStructVariant; - - #[inline] - fn serialize_bool(self, value: bool) -> Result { - Ok(RefValue::Bool(value)) - } - - #[inline] - fn serialize_i8(self, value: i8) -> Result { - self.serialize_i64(value as i64) - } - - #[inline] - fn serialize_i16(self, value: i16) -> Result { - self.serialize_i64(value as i64) - } - - #[inline] - fn serialize_i32(self, value: i32) -> Result { - self.serialize_i64(value as i64) - } - - fn serialize_i64(self, value: i64) -> Result { - Ok(RefValue::Number(value.into())) - } - - #[inline] - fn serialize_u8(self, value: u8) -> Result { - self.serialize_u64(value as u64) - } - - #[inline] - fn serialize_u16(self, value: u16) -> Result { - self.serialize_u64(value as u64) - } - - #[inline] - fn serialize_u32(self, value: u32) -> Result { - self.serialize_u64(value as u64) - } - - #[inline] - fn serialize_u64(self, value: u64) -> Result { - Ok(RefValue::Number(value.into())) - } - - #[inline] - fn serialize_f32(self, value: f32) -> Result { - self.serialize_f64(value as f64) - } - - #[inline] - fn serialize_f64(self, value: f64) -> Result { - Ok(serde_json::Number::from_f64(value).map_or(RefValue::Null, RefValue::Number)) - } - - #[inline] - fn serialize_char(self, value: char) -> Result { - let mut s = String::new(); - s.push(value); - self.serialize_str(&s) - } - - #[inline] - fn serialize_str(self, value: &str) -> Result { - Ok(RefValue::String(value.to_owned())) - } - - fn serialize_bytes(self, value: &[u8]) -> Result { - let vec = value.iter().map(|&b| RefValue::Number(b.into()).into()).collect(); - Ok(RefValue::Array(vec)) - } - - #[inline] - fn serialize_unit(self) -> Result { - Ok(RefValue::Null) - } - - #[inline] - fn serialize_unit_struct(self, _name: &'static str) -> Result { - self.serialize_unit() - } - - #[inline] - fn serialize_unit_variant( - self, - _name: &'static str, - _variant_index: u32, - variant: &'static str, - ) -> Result { - self.serialize_str(variant) - } - - #[inline] - fn serialize_newtype_struct( - self, - _name: &'static str, - value: &T, - ) -> Result - where - T: Serialize, - { - value.serialize(self) - } - - fn serialize_newtype_variant( - self, - _name: &'static str, - _variant_index: u32, - variant: &'static str, - value: &T, - ) -> Result - where - T: Serialize, - { - let mut values: IndexMap = IndexMap::new(); - values.insert(String::from(variant), { - value.serialize(Serializer)?.into() - }); - Ok(RefValue::Object(values)) - } - - #[inline] - fn serialize_none(self) -> Result { - self.serialize_unit() - } - - #[inline] - fn serialize_some(self, value: &T) -> Result - where - T: Serialize, - { - value.serialize(self) - } - - fn serialize_seq(self, len: Option) -> Result { - Ok(SerializeVec { - vec: Vec::with_capacity(len.unwrap_or(0)), - }) - } - - fn serialize_tuple(self, len: usize) -> Result { - self.serialize_seq(Some(len)) - } - - fn serialize_tuple_struct( - self, - _name: &'static str, - len: usize, - ) -> Result { - self.serialize_seq(Some(len)) - } - - fn serialize_tuple_variant( - self, - _name: &'static str, - _variant_index: u32, - variant: &'static str, - len: usize, - ) -> Result { - Ok(SerializeTupleVariant { - name: String::from(variant), - vec: Vec::with_capacity(len), - }) - } - - fn serialize_map(self, _len: Option) -> Result { - Ok(SerializeMap::Map { - map: IndexMap::new(), - next_key: None, - }) - } - - fn serialize_struct( - self, - name: &'static str, - len: usize, - ) -> Result { - match name { - _ => self.serialize_map(Some(len)), - } - } - - fn serialize_struct_variant( - self, - _name: &'static str, - _variant_index: u32, - variant: &'static str, - _len: usize, - ) -> Result { - Ok(SerializeStructVariant { - name: String::from(variant), - map: IndexMap::new(), - }) - } -} - -pub struct SerializeVec { - vec: Vec, -} - -pub struct SerializeTupleVariant { - name: String, - vec: Vec, -} - -pub enum SerializeMap { - Map { - map: IndexMap, - next_key: Option, - }, -} - -pub struct SerializeStructVariant { - name: String, - map: IndexMap, -} - -impl serde::ser::SerializeSeq for SerializeVec { - type Ok = RefValue; - type Error = SerdeError; - - fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> - where - T: Serialize, - { - self.vec.push({ - value.serialize(Serializer)?.into() - }); - Ok(()) - } - - fn end(self) -> Result { - Ok(RefValue::Array(self.vec)) - } -} - -impl serde::ser::SerializeTuple for SerializeVec { - type Ok = RefValue; - type Error = SerdeError; - - fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> - where - T: Serialize, - { - serde::ser::SerializeSeq::serialize_element(self, value) - } - - fn end(self) -> Result { - serde::ser::SerializeSeq::end(self) - } -} - -impl serde::ser::SerializeTupleStruct for SerializeVec { - type Ok = RefValue; - type Error = SerdeError; - - fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> - where - T: Serialize, - { - serde::ser::SerializeSeq::serialize_element(self, value) - } - - fn end(self) -> Result { - serde::ser::SerializeSeq::end(self) - } -} - -impl serde::ser::SerializeTupleVariant for SerializeTupleVariant { - type Ok = RefValue; - type Error = SerdeError; - - fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> - where - T: Serialize, - { - self.vec.push({ - let a: RefValue = value.serialize(Serializer)?; - a.into() - }); - Ok(()) - } - - fn end(self) -> Result { - let mut object: IndexMap = IndexMap::new(); - - object.insert(self.name, RefValue::Array(self.vec).into()); - - Ok(RefValue::Object(object)) - } -} - -impl serde::ser::SerializeMap for SerializeMap { - type Ok = RefValue; - type Error = SerdeError; - - fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> - where - T: Serialize, - { - match *self { - SerializeMap::Map { - ref mut next_key, .. - } => { - *next_key = Some(key.serialize(MapKeySerializer)?); - Ok(()) - } - } - } - - fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> - where - T: Serialize, - { - match *self { - SerializeMap::Map { - ref mut map, - ref mut next_key, - } => { - let key = next_key.take(); - // Panic because this indicates a bug in the program rather than an - // expected failure. - let key = key.expect("serialize_value called before serialize_key"); - map.insert(key, { - let a: RefValue = value.serialize(Serializer)?; - a.into() - }); - Ok(()) - } - } - } - - fn end(self) -> Result { - match self { - SerializeMap::Map { map, .. } => Ok(RefValue::Object(map)), - } - } -} - -struct MapKeySerializer; - -fn key_must_be_a_string() -> SerdeError { - SerdeError::from_str("key must be string") -} - -impl serde::Serializer for MapKeySerializer { - type Ok = String; - type Error = SerdeError; - - type SerializeSeq = Impossible; - type SerializeTuple = Impossible; - type SerializeTupleStruct = Impossible; - type SerializeTupleVariant = Impossible; - type SerializeMap = Impossible; - type SerializeStruct = Impossible; - type SerializeStructVariant = Impossible; - - #[inline] - fn serialize_unit_variant( - self, - _name: &'static str, - _variant_index: u32, - variant: &'static str, - ) -> Result { - Ok(variant.to_owned()) - } - - #[inline] - fn serialize_newtype_struct( - self, - _name: &'static str, - value: &T, - ) -> Result - where - T: Serialize, - { - value.serialize(self) - } - - fn serialize_bool(self, _value: bool) -> Result { - Err(key_must_be_a_string()) - } - - fn serialize_i8(self, value: i8) -> Result { - Ok(value.to_string()) - } - - fn serialize_i16(self, value: i16) -> Result { - Ok(value.to_string()) - } - - fn serialize_i32(self, value: i32) -> Result { - Ok(value.to_string()) - } - - fn serialize_i64(self, value: i64) -> Result { - Ok(value.to_string()) - } - - fn serialize_u8(self, value: u8) -> Result { - Ok(value.to_string()) - } - - fn serialize_u16(self, value: u16) -> Result { - Ok(value.to_string()) - } - - fn serialize_u32(self, value: u32) -> Result { - Ok(value.to_string()) - } - - fn serialize_u64(self, value: u64) -> Result { - Ok(value.to_string()) - } - - fn serialize_f32(self, _value: f32) -> Result { - Err(key_must_be_a_string()) - } - - fn serialize_f64(self, _value: f64) -> Result { - Err(key_must_be_a_string()) - } - - #[inline] - fn serialize_char(self, value: char) -> Result { - Ok({ - let mut s = String::new(); - s.push(value); - s - }) - } - - #[inline] - fn serialize_str(self, value: &str) -> Result { - Ok(value.to_owned()) - } - - fn serialize_bytes(self, _value: &[u8]) -> Result { - Err(key_must_be_a_string()) - } - - fn serialize_unit(self) -> Result { - Err(key_must_be_a_string()) - } - - fn serialize_unit_struct(self, _name: &'static str) -> Result { - Err(key_must_be_a_string()) - } - - fn serialize_newtype_variant( - self, - _name: &'static str, - _variant_index: u32, - _variant: &'static str, - _value: &T, - ) -> Result - where - T: Serialize, - { - Err(key_must_be_a_string()) - } - - fn serialize_none(self) -> Result { - Err(key_must_be_a_string()) - } - - fn serialize_some(self, _value: &T) -> Result - where - T: Serialize, - { - Err(key_must_be_a_string()) - } - - fn serialize_seq(self, _len: Option) -> Result { - Err(key_must_be_a_string()) - } - - fn serialize_tuple(self, _len: usize) -> Result { - Err(key_must_be_a_string()) - } - - fn serialize_tuple_struct( - self, - _name: &'static str, - _len: usize, - ) -> Result { - Err(key_must_be_a_string()) - } - - fn serialize_tuple_variant( - self, - _name: &'static str, - _variant_index: u32, - _variant: &'static str, - _len: usize, - ) -> Result { - Err(key_must_be_a_string()) - } - - fn serialize_map(self, _len: Option) -> Result { - Err(key_must_be_a_string()) - } - - fn serialize_struct( - self, - _name: &'static str, - _len: usize, - ) -> Result { - Err(key_must_be_a_string()) - } - - fn serialize_struct_variant( - self, - _name: &'static str, - _variant_index: u32, - _variant: &'static str, - _len: usize, - ) -> Result { - Err(key_must_be_a_string()) - } -} - -impl serde::ser::SerializeStruct for SerializeMap { - type Ok = RefValue; - type Error = SerdeError; - - fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> - where - T: Serialize, - { - match *self { - SerializeMap::Map { .. } => { - serde::ser::SerializeMap::serialize_key(self, key)?; - serde::ser::SerializeMap::serialize_value(self, value) - } - } - } - - fn end(self) -> Result { - match self { - SerializeMap::Map { .. } => serde::ser::SerializeMap::end(self), - } - } -} - -impl serde::ser::SerializeStructVariant for SerializeStructVariant { - type Ok = RefValue; - type Error = SerdeError; - - fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> - where - T: Serialize, - { - self.map.insert(String::from(key), { - let a: RefValue = value.serialize(Serializer)?; - a.into() - }); - Ok(()) - } - - fn end(self) -> Result { - let mut object: IndexMap = IndexMap::new(); - - object.insert(self.name, RefValue::Object(self.map).into()); - - Ok(RefValue::Object(object)) - } -} diff --git a/src/ref_value/serde_error.rs b/src/ref_value/serde_error.rs deleted file mode 100644 index 7deb7f1..0000000 --- a/src/ref_value/serde_error.rs +++ /dev/null @@ -1,38 +0,0 @@ -use std::fmt; - -#[derive(Debug)] -pub struct SerdeError { - msg: String, -} - -impl<'a> SerdeError { - pub fn new(msg: String) -> Self { - SerdeError { msg: msg } - } - - pub fn from_str(msg: &str) -> Self { - SerdeError { msg: msg.to_string() } - } -} - -impl serde::de::Error for SerdeError { - #[cold] - fn custom(msg: T) -> SerdeError { - SerdeError { msg: msg.to_string() } - } -} - -impl serde::ser::Error for SerdeError { - #[cold] - fn custom(msg: T) -> SerdeError { - SerdeError { msg: msg.to_string() } - } -} - -impl std::error::Error for SerdeError {} - -impl fmt::Display for SerdeError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.msg) - } -} \ No newline at end of file diff --git a/src/select/mod.rs b/src/select/mod.rs index 66fd576..f2b19ea 100644 --- a/src/select/mod.rs +++ b/src/select/mod.rs @@ -1,239 +1,1150 @@ -use std::{fmt, result}; -use std::ops::Deref; +use std::collections::HashSet; -use serde_json::Value; +use array_tool::vec::{Intersect, Union}; +use indexmap::IndexMap; +use serde_json::{Number, Value}; -use super::filter::value_filter::*; -use super::parser::parser::*; -use super::ref_value::model::*; +use parser::parser::*; -/// Utility. Functions like jsonpath::selector or jsonpath::compile are also implemented using this structure. -/// -/// ```rust -/// extern crate jsonpath_lib as jsonpath; -/// extern crate serde; -/// extern crate serde_json; -/// -/// use serde::{Deserialize, Serialize}; -/// use serde_json::Value; -/// -/// use jsonpath::Selector; -/// -/// #[derive(Serialize, Deserialize, PartialEq, Debug)] -/// struct Person { -/// name: String, -/// age: Option, -/// phone: String, -/// } -/// -/// fn input_str() -> &'static str { -/// r#"[ -/// { -/// "name": "이름1", -/// "age": 40, -/// "phone": "+33 12341234" -/// }, -/// { -/// "name": "이름2", -/// "age": 42, -/// "phone": "++44 12341234" -/// } -/// ]"# -/// } -/// -/// fn input_json() -> Value { -/// serde_json::from_str(input_str()).unwrap() -/// } -/// -/// fn input_person() -> Vec { -/// serde_json::from_str(input_str()).unwrap() -/// } -/// -/// -/// let mut selector = Selector::new(); -/// -/// let result = selector -/// .path("$..[?(@.age > 40)]").unwrap() -/// .value_from_str(input_str()).unwrap() -/// .select_as_value().unwrap(); -/// assert_eq!(input_json()[1], result[0]); -/// -/// let result = selector.select_as_str().unwrap(); -/// assert_eq!(serde_json::to_string(&vec![&input_json()[1].clone()]).unwrap(), result); -/// -/// let result = selector.select_as::>().unwrap(); -/// assert_eq!(input_person()[1], result[0]); -/// -/// let _ = selector.path("$..[?(@.age == 40)]"); -/// -/// let result = selector.select_as_value().unwrap(); -/// assert_eq!(input_json()[0], result[0]); -/// -/// let result = selector.select_as_str().unwrap(); -/// assert_eq!(serde_json::to_string(&vec![&input_json()[0].clone()]).unwrap(), result); -/// -/// let result = selector.select_as::>().unwrap(); -/// assert_eq!(input_person()[0], result[0]); -/// -/// selector.map(|v| { -/// let r = match v { -/// Value::Array(mut vec) => { -/// for mut v in &mut vec { -/// v.as_object_mut().unwrap().remove("age"); -/// } -/// Value::Array(vec) -/// } -/// _ => Value::Null -/// }; -/// Some(r) -/// }); -/// assert_eq!( -/// serde_json::from_str::(r#"[{ "name": "이름1", "phone": "+33 12341234"}]"#).unwrap(), -/// selector.get().unwrap()); -/// -/// selector.value_from_str(input_str()).unwrap() -/// .map_as(|mut v: Vec| { -/// let mut p = v.pop().unwrap(); -/// p.name = "name1".to_string(); -/// p.age = None; -/// Some(vec![p]) -/// }); -/// assert_eq!( -/// vec![Person { name: "name1".to_string(), age: None, phone: "+33 12341234".to_string() }], -/// selector.get_as::>().unwrap()); -/// ``` -#[derive(Debug)] -pub struct Selector { - pub(crate) node: Option, - pub(crate) value: Option, +fn to_f64(n: &Number) -> f64 { + if n.is_i64() { + n.as_i64().unwrap() as f64 + } else if n.is_f64() { + n.as_f64().unwrap() + } else { + n.as_u64().unwrap() as f64 + } } -impl Selector { - pub fn new() -> Self { - Selector { node: None, value: None } +trait Cmp { + fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool; + + fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool; + + fn cmp_string(&self, v1: &String, v2: &String) -> bool; + + fn cmp_json<'a>(&self, v1: &Vec<&'a Value>, v2: &Vec<&'a Value>) -> Vec<&'a Value>; + + fn default(&self) -> bool { false } +} + +struct CmpEq; + +impl Cmp for CmpEq { + fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { + v1 == v2 } - pub fn path(&mut self, path: &str) -> result::Result<&mut Self, String> { - let mut parser = Parser::new(path); - self.node = Some(parser.compile()?); - Ok(self) + fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { + v1 == v2 } - pub fn value(&mut self, value: &Value) -> result::Result<&mut Self, String> { - self.value = Some(value.into()); - Ok(self) + fn cmp_string(&self, v1: &String, v2: &String) -> bool { + v1 == v2 } - pub fn value_from(&mut self, serializable: &impl serde::ser::Serialize) -> result::Result<&mut Self, String> { - let ref_value: RefValue = serializable - .serialize(super::ref_value::ser::Serializer) - .map_err(|e| e.to_string())?; - self.value = Some(ref_value.into()); - Ok(self) + fn cmp_json<'a>(&self, v1: &Vec<&'a Value>, v2: &Vec<&'a Value>) -> Vec<&'a Value> { + v1.intersect(v2.to_vec()) + } +} + +struct CmpNe; + +impl Cmp for CmpNe { + fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { + v1 != v2 } - pub fn value_from_str(&mut self, json_str: &str) -> result::Result<&mut Self, String> { - let value = serde_json::from_str(json_str) - .map_err(|e| e.to_string())?; - self.value(&value) + fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { + v1 != v2 } - fn jf(&self) -> result::Result { - match &self.value { - Some(v) => Ok(JsonValueFilter::new_from_value(v.clone())), - _ => return Err(SelectorErrorMessage::EmptyValue.to_string()) + fn cmp_string(&self, v1: &String, v2: &String) -> bool { + v1 != v2 + } + + fn cmp_json<'a>(&self, v1: &Vec<&'a Value>, v2: &Vec<&'a Value>) -> Vec<&'a Value> { + v1.intersect_if(v2.to_vec(), |a, b| a != b) + } +} + +struct CmpGt; + +impl Cmp for CmpGt { + fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { + v1 > v2 + } + + fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { + v1 > v2 + } + + fn cmp_string(&self, v1: &String, v2: &String) -> bool { + v1 > v2 + } + + fn cmp_json<'a>(&self, _: &Vec<&'a Value>, _: &Vec<&'a Value>) -> Vec<&'a Value> { + Vec::new() + } +} + +struct CmpGe; + +impl Cmp for CmpGe { + fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { + v1 >= v2 + } + + fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { + v1 >= v2 + } + + fn cmp_string(&self, v1: &String, v2: &String) -> bool { + v1 >= v2 + } + + fn cmp_json<'a>(&self, _: &Vec<&'a Value>, _: &Vec<&'a Value>) -> Vec<&'a Value> { + Vec::new() + } +} + +struct CmpLt; + +impl Cmp for CmpLt { + fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { + v1 < v2 + } + + fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { + v1 < v2 + } + + fn cmp_string(&self, v1: &String, v2: &String) -> bool { + v1 < v2 + } + + fn cmp_json<'a>(&self, _: &Vec<&'a Value>, _: &Vec<&'a Value>) -> Vec<&'a Value> { + Vec::new() + } +} + +struct CmpLe; + +impl Cmp for CmpLe { + fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { + v1 <= v2 + } + + fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { + v1 <= v2 + } + + fn cmp_string(&self, v1: &String, v2: &String) -> bool { + v1 <= v2 + } + + fn cmp_json<'a>(&self, _: &Vec<&'a Value>, _: &Vec<&'a Value>) -> Vec<&'a Value> { + Vec::new() + } +} + +struct CmpAnd; + +impl Cmp for CmpAnd { + fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { + *v1 && *v2 + } + + fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { + v1 > &0_f64 && v2 > &0_f64 + } + + fn cmp_string(&self, v1: &String, v2: &String) -> bool { + !v1.is_empty() && !v2.is_empty() + } + + fn cmp_json<'a>(&self, v1: &Vec<&'a Value>, v2: &Vec<&'a Value>) -> Vec<&'a Value> { + v1.intersect(v2.to_vec()) + } +} + +struct CmpOr; + +impl Cmp for CmpOr { + fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { + *v1 || *v2 + } + + fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { + v1 > &0_f64 || v2 > &0_f64 + } + + fn cmp_string(&self, v1: &String, v2: &String) -> bool { + !v1.is_empty() || !v2.is_empty() + } + + fn cmp_json<'a>(&self, v1: &Vec<&'a Value>, v2: &Vec<&'a Value>) -> Vec<&'a Value> { + v1.union(v2.to_vec()) + } +} + +#[derive(Debug)] +enum ExprTerm<'a> { + String(String), + Number(Number), + Bool(bool), + Json(Option, Vec<&'a Value>), +} + +impl<'a> ExprTerm<'a> { + fn is_string(&self) -> bool { + match &self { + ExprTerm::String(_) => true, + _ => false } } - fn select(&self) -> result::Result { - let mut jf = self.jf()?; + fn is_number(&self) -> bool { + match &self { + ExprTerm::Number(_) => true, + _ => false + } + } - match &self.node { - Some(node) => { - jf.visit(node.clone()); - Ok(jf.take_value()) + fn is_bool(&self) -> bool { + match &self { + ExprTerm::Bool(_) => true, + _ => false + } + } + + fn is_json(&self) -> bool { + match &self { + ExprTerm::Json(_, _) => true, + _ => false + } + } + + fn cmp(&self, other: &Self, cmp_fn: &C1, reverse_cmp_fn: &C2) -> ExprTerm<'a> { + match &self { + ExprTerm::String(s1) => match &other { + ExprTerm::String(s2) => ExprTerm::Bool(cmp_fn.cmp_string(s1, s2)), + ExprTerm::Json(_, _) => { + other.cmp(&self, reverse_cmp_fn, cmp_fn) + } + _ => ExprTerm::Bool(cmp_fn.default()) } - _ => Err(SelectorErrorMessage::EmptyPath.to_string()) + ExprTerm::Number(n1) => match &other { + ExprTerm::Number(n2) => ExprTerm::Bool(cmp_fn.cmp_f64(&to_f64(n1), &to_f64(n2))), + ExprTerm::Json(_, _) => { + other.cmp(&self, reverse_cmp_fn, cmp_fn) + } + _ => ExprTerm::Bool(cmp_fn.default()) + } + ExprTerm::Bool(b1) => match &other { + ExprTerm::Bool(b2) => ExprTerm::Bool(cmp_fn.cmp_bool(b1, b2)), + ExprTerm::Json(_, _) => { + other.cmp(&self, reverse_cmp_fn, cmp_fn) + } + _ => ExprTerm::Bool(cmp_fn.default()) + } + ExprTerm::Json(fk1, vec1) if other.is_string() => { + let s2 = if let ExprTerm::String(s2) = &other { s2 } else { unreachable!() }; + + let ret: Vec<&Value> = vec1.iter().filter(|v1| { + match v1 { + Value::String(s1) => cmp_fn.cmp_string(s1, s2), + Value::Object(map1) => { + if let Some(FilterKey::String(k)) = fk1 { + if let Some(Value::String(s1)) = map1.get(k) { + return cmp_fn.cmp_string(s1, s2); + } + } + cmp_fn.default() + } + _ => cmp_fn.default() + } + }).map(|v| *v).collect(); + + if ret.is_empty() { ExprTerm::Bool(cmp_fn.default()) } else { ExprTerm::Json(None, ret) } + } + ExprTerm::Json(fk1, vec1) if other.is_number() => { + let n2 = if let ExprTerm::Number(n2) = &other { n2 } else { unreachable!() }; + let ret: Vec<&Value> = vec1.iter().filter(|v1| { + match v1 { + Value::Number(n1) => cmp_fn.cmp_f64(&to_f64(n1), &to_f64(n2)), + Value::Object(map1) => { + if let Some(FilterKey::String(k)) = fk1 { + if let Some(Value::Number(n1)) = map1.get(k) { + return cmp_fn.cmp_f64(&to_f64(n1), &to_f64(n2)); + } + } + cmp_fn.default() + } + _ => cmp_fn.default() + } + }).map(|v| *v).collect(); + + if ret.is_empty() { ExprTerm::Bool(cmp_fn.default()) } else { ExprTerm::Json(None, ret) } + } + ExprTerm::Json(fk1, vec1) if other.is_bool() => { + let b2 = if let ExprTerm::Bool(b2) = &other { b2 } else { unreachable!() }; + let ret: Vec<&Value> = vec1.iter().filter(|v1| { + match v1 { + Value::Bool(b1) => cmp_fn.cmp_bool(b1, b2), + Value::Object(map1) => { + if let Some(FilterKey::String(k)) = fk1 { + if let Some(Value::Bool(b1)) = map1.get(k) { + return cmp_fn.cmp_bool(b1, b2); + } + } + cmp_fn.default() + } + _ => cmp_fn.default() + } + }).map(|v| *v).collect(); + + if ret.is_empty() { ExprTerm::Bool(cmp_fn.default()) } else { ExprTerm::Json(None, ret) } + } + ExprTerm::Json(_, vec1) if other.is_json() => { + match &other { + ExprTerm::Json(_, vec2) => { + let vec = cmp_fn.cmp_json(vec1, vec2); + if vec.is_empty() { ExprTerm::Bool(cmp_fn.default()) } else { ExprTerm::Json(None, vec) } + } + _ => unreachable!() + } + } + _ => unreachable!() } } - #[deprecated(since = "0.1.13", note = "Please use the select_as_str function instead")] - pub fn select_to_str(&self) -> result::Result { - self.select_as_str() + fn eq(&self, other: &Self, ret: &mut Option>) { + debug!("eq - {:?} : {:?}", &self, &other); + let _ = ret.take(); + let tmp = self.cmp(other, &CmpEq, &CmpEq); + debug!("eq = {:?}", tmp); + *ret = Some(tmp); } - #[deprecated(since = "0.1.13", note = "Please use the select_as_value function instead")] - pub fn select_to_value(&self) -> result::Result { - self.select_as_value() + fn ne(&self, other: &Self, ret: &mut Option>) { + debug!("ne - {:?} : {:?}", &self, &other); + let _ = ret.take(); + let tmp = self.cmp(other, &CmpNe, &CmpNe); + debug!("ne = {:?}", tmp); + *ret = Some(tmp); } - #[deprecated(since = "0.1.13", note = "Please use the select_as function instead")] - pub fn select_to(&self) -> result::Result { - self.select_as() + fn gt(&self, other: &Self, ret: &mut Option>) { + debug!("gt - {:?} : {:?}", &self, &other); + let _ = ret.take(); + let tmp = self.cmp(other, &CmpGt, &CmpLt); + debug!("gt = {:?}", tmp); + *ret = Some(tmp); } - pub fn select_as_str(&self) -> result::Result { - serde_json::to_string(self.select()?.deref()).map_err(|e| e.to_string()) + fn ge(&self, other: &Self, ret: &mut Option>) { + debug!("ge - {:?} : {:?}", &self, &other); + let _ = ret.take(); + let tmp = self.cmp(other, &CmpGe, &CmpLe); + debug!("ge = {:?}", tmp); + *ret = Some(tmp); } - pub fn select_as_value(&self) -> result::Result { - Ok((&self.select()?).into()) + fn lt(&self, other: &Self, ret: &mut Option>) { + debug!("lt - {:?} : {:?}", &self, &other); + let _ = ret.take(); + let tmp = self.cmp(other, &CmpLt, &CmpGt); + debug!("lt = {:?}", tmp); + *ret = Some(tmp); } - pub fn select_as(&self) -> result::Result { - T::deserialize(self.select()?.deref()).map_err(|e| e.to_string()) + fn le(&self, other: &Self, ret: &mut Option>) { + debug!("le - {:?} : {:?}", &self, &other); + let _ = ret.take(); + let tmp = self.cmp(other, &CmpLe, &CmpGe); + debug!("le = {:?}", tmp); + *ret = Some(tmp); } - pub fn map(&mut self, func: F) -> result::Result<&mut Self, String> - where F: FnOnce(Value) -> Option - { - self.value = func((&self.select()?).into()).map(|ref v| v.into()); - Ok(self) + fn and(&self, other: &Self, ret: &mut Option>) { + debug!("and - {:?} : {:?}", &self, &other); + let _ = ret.take(); + let tmp = self.cmp(other, &CmpAnd, &CmpAnd); + debug!("and = {:?}", tmp); + *ret = Some(tmp); } - pub fn map_as(&mut self, func: F) -> result::Result<&mut Self, String> - where F: FnOnce(D) -> Option, - D: serde::de::DeserializeOwned, - S: serde::ser::Serialize - { - let ret = func(D::deserialize(self.select()?.deref()).map_err(|e| e.to_string())?) - .map(|ref ser| ser.serialize(super::ref_value::ser::Serializer)); + fn or(&self, other: &Self, ret: &mut Option>) { + debug!("or - {:?} : {:?}", &self, &other); + let _ = ret.take(); + let tmp = self.cmp(other, &CmpOr, &CmpOr); + debug!("or = {:?}", tmp); + *ret = Some(tmp); + } +} - self.value = match ret { - Some(ret) => match ret { - Ok(v) => Some(v.into()), - Err(e) => return Err(e.to_string()) +impl<'a> Into> for &Vec<&'a Value> { + fn into(self) -> ExprTerm<'a> { + if self.len() == 1 { + match &self[0] { + Value::Number(v) => return ExprTerm::Number(v.clone()), + Value::String(v) => return ExprTerm::String(v.clone()), + Value::Bool(v) => return ExprTerm::Bool(*v), + _ => {} + } + } + + ExprTerm::Json(None, self.to_vec()) + } +} + +fn walk_all_with_str<'a>(vec: &Vec<&'a Value>, tmp: &mut Vec<&'a Value>, key: &str, is_filter: bool) { + if is_filter { + walk(vec, tmp, &|v| match v { + Value::Object(map) if map.contains_key(key) => { + Some(vec![v]) } _ => None - }; + }); + } else { + walk(vec, tmp, &|v| match v { + Value::Object(map) => match map.get(key) { + Some(v) => Some(vec![v]), + _ => None + } + _ => None + }); + } +} + +fn walk_all<'a>(vec: &Vec<&'a Value>, tmp: &mut Vec<&'a Value>) { + walk(vec, tmp, &|v| match v { + Value::Array(vec) => { + Some(vec.iter().collect()) + } + Value::Object(map) => { + let mut tmp = Vec::new(); + for (_, v) in map { + tmp.push(v); + } + Some(tmp) + } + _ => None + }); +} + +fn walk<'a, F>(vec: &Vec<&'a Value>, tmp: &mut Vec<&'a Value>, fun: &F) + where F: Fn(&Value) -> Option> +{ + fn _walk<'a, F>(v: &'a Value, tmp: &mut Vec<&'a Value>, fun: &F) + where F: Fn(&Value) -> Option> + { + if let Some(mut ret) = fun(v) { + tmp.append(&mut ret); + } + + match v { + Value::Array(vec) => { + for v in vec { + _walk(v, tmp, fun); + } + } + Value::Object(map) => { + for (_, v) in map { + _walk(&v, tmp, fun); + } + } + _ => {} + } + } + + for v in vec { + _walk(v, tmp, fun); + } +} + +fn abs_index(n: &isize, len: usize) -> usize { + if n < &0_isize { + (n + len as isize) as usize + } else { + *n as usize + } +} + +#[derive(Debug)] +enum FilterKey { + String(String), + All, +} + +#[derive(Debug)] +pub enum JsonPathError { + EmptyPath, + EmptyValue, + Path(String), + Serde(String), +} + +#[derive(Debug)] +pub struct Selector<'a, 'b> { + node: Option, + node_ref: Option<&'b Node>, + value: Option<&'a Value>, + tokens: Vec, + terms: Vec>>, + current: Option>, + selectors: Vec>, +} + +impl<'a, 'b> Selector<'a, 'b> { + pub fn new() -> Self { + Selector { + node: None, + node_ref: None, + value: None, + tokens: Vec::new(), + terms: Vec::new(), + current: None, + selectors: Vec::new(), + } + } + + pub fn str_path(&mut self, path: &str) -> Result<&mut Self, JsonPathError> { + debug!("path : {}", path); + + if self.node_ref.is_some() { + self.node_ref.take(); + } + + self.node = Some(Parser::compile(path).map_err(|e| JsonPathError::Path(e))?); Ok(self) } - pub fn get(&self) -> result::Result { - match &self.value { - Some(value) => Ok(value.into()), - _ => Err(SelectorErrorMessage::EmptyValue.to_string()) + pub fn node_ref(&self) -> Option<&Node> { + if let Some(node) = &self.node { + Some(node) + } else { + None } } - pub fn get_as(&self) -> result::Result { - match &self.value { - Some(value) => T::deserialize(value.deref()).map_err(|e| e.to_string()), - _ => Err(SelectorErrorMessage::EmptyValue.to_string()) + pub fn compiled_path(&mut self, node: &'b Node) -> &mut Self { + if self.node.is_some() { + self.node.take(); + } + + self.node_ref = Some(node); + self + } + + pub fn reset_value(&mut self) -> &mut Self { + self.current = None; + self + } + + pub fn value(&mut self, v: &'a Value) -> &mut Self { + self.value = Some(v); + self + } + + fn _select(&mut self) -> Result<(), JsonPathError> { + if self.node_ref.is_some() { + let node_ref = self.node_ref.take().unwrap(); + self.visit(node_ref); + return Ok(()); + } + + if self.node.is_none() { + return Err(JsonPathError::EmptyPath); + } + + let node = self.node.take().unwrap(); + self.visit(&node); + self.node = Some(node); + + Ok(()) + } + + pub fn select_as(&mut self) -> Result, JsonPathError> { + self._select()?; + + match &self.current { + Some(vec) => { + let mut ret = Vec::new(); + for v in vec { + match T::deserialize(*v) { + Ok(v) => ret.push(v), + Err(e) => return Err(JsonPathError::Serde(e.to_string())) + } + } + Ok(ret) + } + _ => Err(JsonPathError::EmptyValue) + } + } + + pub fn select_as_str(&mut self) -> Result { + self._select()?; + + match &self.current { + Some(r) => { + Ok(serde_json::to_string(r) + .map_err(|e| JsonPathError::Serde(e.to_string()))?) + } + _ => Err(JsonPathError::EmptyValue) + } + } + + pub fn select(&mut self) -> Result, JsonPathError> { + self._select()?; + + match &self.current { + Some(r) => Ok(r.to_vec()), + _ => Err(JsonPathError::EmptyValue) + } + } + + fn new_filter_context(&mut self) { + self.terms.push(None); + debug!("new_filter_context: {:?}", self.terms); + } + + fn in_filter, &mut Vec<&'a Value>) -> FilterKey>(&mut self, fun: F) { + match self.terms.pop() { + Some(peek) => match peek { + Some(v) => { + debug!("in_filter 1.: {:?}", v); + + match v { + ExprTerm::Json(_, vec) => { + let mut tmp = Vec::new(); + let filter_key = fun(&vec, &mut tmp); + self.terms.push(Some(ExprTerm::Json(Some(filter_key), tmp))); + } + _ => unreachable!() + }; + } + _ => { + debug!("in_filter 2.: {:?}", &self.current); + + if let Some(current) = &self.current { + let mut tmp = Vec::new(); + let filter_key = fun(current, &mut tmp); + self.terms.push(Some(ExprTerm::Json(Some(filter_key), tmp))); + } + } + }, + _ => {} + } + } + + fn all_in_filter_with_str(&mut self, key: &str) { + self.in_filter(|vec, tmp| { + walk_all_with_str(&vec, tmp, key, true); + FilterKey::All + }); + + debug!("all_in_filter_with_str : {}, {:?}", key, self.terms); + } + + fn next_in_filter_with_str(&mut self, key: &str) { + fn _collect<'a>(v: &'a Value, tmp: &mut Vec<&'a Value>, key: &str, visited: &mut HashSet<*const Value>) { + match v { + Value::Object(map) => if map.contains_key(key) { + let ptr = v as *const Value; + if !visited.contains(&ptr) { + visited.insert(ptr); + tmp.push(v) + } + }, + Value::Array(vec) => for v in vec { + _collect(v, tmp, key, visited); + } + _ => {} + } + } + + self.in_filter(|vec, tmp| { + let mut visited = HashSet::new(); + for v in vec { + _collect(v, tmp, key, &mut visited); + } + FilterKey::String(key.to_owned()) + }); + + debug!("next_in_filter_with_str : {}, {:?}", key, self.terms); + } + + fn next_from_current_with_num(&mut self, index: f64) { + if let Some(current) = self.current.take() { + let mut tmp = Vec::new(); + for c in current { + if let Value::Array(vec) = c { + let index = abs_index(&(index as isize), vec.len()); + if let Some(v) = c.get(index) { + tmp.push(v); + } + } + } + self.current = Some(tmp); + } + + debug!("next_from_current_with_num : {:?}, {:?}", &index, self.current); + } + + fn next_from_current_with_str(&mut self, key: &str) { + fn _collect<'a>(v: &'a Value, tmp: &mut Vec<&'a Value>, key: &str, visited: &mut HashSet<*const Value>) { + match v { + Value::Object(map) => { + if let Some(v) = map.get(key) { + let ptr = v as *const Value; + if !visited.contains(&ptr) { + visited.insert(ptr); + tmp.push(v) + } + } + } + Value::Array(vec) => for v in vec { + _collect(v, tmp, key, visited); + } + _ => {} + } + } + + if let Some(current) = self.current.take() { + let mut tmp = Vec::new(); + let mut visited = HashSet::new(); + for c in current { + _collect(c, &mut tmp, key, &mut visited); + } + self.current = Some(tmp); + } + + debug!("next_from_current_with_str : {}, {:?}", key, self.current); + } + + fn next_all_from_current(&mut self) { + fn _collect<'a>(v: &'a Value, tmp: &mut Vec<&'a Value>) { + match v { + Value::Object(map) => { + for (_, v) in map { + tmp.push(v) + } + } + Value::Array(vec) => for v in vec { + _collect(v, tmp); + } + _ => {} + } + } + + if let Some(current) = self.current.take() { + let mut tmp = Vec::new(); + for c in current { + _collect(c, &mut tmp); + } + self.current = Some(tmp); + } + + debug!("next_all_from_current : {:?}", self.current); + } + + fn all_from_current(&mut self) { + if let Some(current) = self.current.take() { + let mut tmp = Vec::new(); + walk_all(¤t, &mut tmp); + self.current = Some(tmp); + } + debug!("all_from_current: {:?}", self.current); + } + + fn all_from_current_with_str(&mut self, key: &str) { + if let Some(current) = self.current.take() { + let mut tmp = Vec::new(); + walk_all_with_str(¤t, &mut tmp, key, false); + self.current = Some(tmp); + } + debug!("all_from_current_with_str: {}, {:?}", key, self.current); + } +} + +impl<'a, 'b> NodeVisitor for Selector<'a, 'b> { + fn visit_token(&mut self, token: &ParseToken) { + debug!("token: {:?}, stack: {:?}", token, self.tokens); + + if !self.selectors.is_empty() { + match token { + ParseToken::Absolute | ParseToken::Relative | ParseToken::Filter(_) => { + let selector = self.selectors.pop().unwrap(); + + if let Some(current) = &selector.current { + let term = current.into(); + + if let Some(s) = self.selectors.last_mut() { + s.terms.push(Some(term)); + } else { + self.terms.push(Some(term)); + } + } else { + unreachable!() + } + } + _ => {} + } + } + + if let Some(selector) = self.selectors.last_mut() { + selector.visit_token(token); + return; + } + + match token { + ParseToken::Absolute => { + if self.current.is_some() { + let mut selector = Selector::new(); + + if let Some(value) = self.value { + selector.value = Some(value); + selector.current = Some(vec![value]); + self.selectors.push(selector); + } + return; + } + + match &self.value { + Some(v) => self.current = Some(vec![v]), + _ => {} + } + } + ParseToken::Relative => { + self.new_filter_context(); + } + ParseToken::In | ParseToken::Leaves => { + self.tokens.push(token.clone()); + } + ParseToken::Array => { + if let Some(ParseToken::Leaves) = self.tokens.last() { + self.tokens.pop(); + self.all_from_current(); + } + + self.tokens.push(token.clone()); + } + ParseToken::ArrayEof => { + if let Some(Some(e)) = self.terms.pop() { + match e { + ExprTerm::Number(n) => { + self.next_from_current_with_num(to_f64(&n)); + } + ExprTerm::String(key) => { + self.next_from_current_with_str(&key); + } + ExprTerm::Json(_, v) => { + if v.is_empty() { + self.current = Some(vec![&Value::Null]); + } else { + self.current = Some(v); + } + } + ExprTerm::Bool(false) => { + self.current = Some(vec![&Value::Null]); + } + _ => {} + } + } + + self.tokens.pop(); + } + ParseToken::All => { + match self.tokens.last() { + Some(ParseToken::Leaves) => { + self.tokens.pop(); + self.all_from_current(); + } + Some(ParseToken::In) => { + self.tokens.pop(); + self.next_all_from_current(); + } + _ => {} + } + } + ParseToken::Bool(b) => { + self.terms.push(Some(ExprTerm::Bool(*b))); + } + ParseToken::Key(key) => { + if let Some(ParseToken::Array) = self.tokens.last() { + self.terms.push(Some(ExprTerm::String(key.clone()))); + return; + } + + match self.tokens.pop() { + Some(t) => { + if self.terms.is_empty() { + match t { + ParseToken::Leaves => { + self.all_from_current_with_str(key.as_str()) + } + ParseToken::In => { + self.next_from_current_with_str(key.as_str()) + } + _ => {} + } + } else { + match t { + ParseToken::Leaves => { + self.all_in_filter_with_str(key.as_str()); + } + ParseToken::In => { + self.next_in_filter_with_str(key.as_str()); + } + _ => {} + } + } + } + _ => {} + } + } + ParseToken::Number(v) => { + self.terms.push(Some(ExprTerm::Number(Number::from_f64(*v).unwrap()))); + } + ParseToken::Filter(ref ft) => { + if let Some(Some(ref right)) = self.terms.pop() { + if let Some(Some(left)) = self.terms.pop() { + let mut ret = None; + match ft { + FilterToken::Equal => left.eq(right, &mut ret), + FilterToken::NotEqual => left.ne(right, &mut ret), + FilterToken::Greater => left.gt(right, &mut ret), + FilterToken::GreaterOrEqual => left.ge(right, &mut ret), + FilterToken::Little => left.lt(right, &mut ret), + FilterToken::LittleOrEqual => left.le(right, &mut ret), + FilterToken::And => left.and(right, &mut ret), + FilterToken::Or => left.or(right, &mut ret), + }; + + if let Some(e) = ret { + self.terms.push(Some(e)); + } + } else { + unreachable!() + } + } else { + unreachable!() + } + } + ParseToken::Range(from, to) => { + if !self.terms.is_empty() { + unimplemented!("range syntax in filter"); + } + + if let Some(ParseToken::Array) = self.tokens.pop() { + let mut tmp = Vec::new(); + if let Some(current) = &self.current { + for v in current { + if let Value::Array(vec) = v { + let from = if let Some(from) = from { + abs_index(from, vec.len()) + } else { + 0 + }; + + let to = if let Some(to) = to { + abs_index(to, vec.len()) + } else { + vec.len() + }; + + for i in from..to { + if let Some(v) = vec.get(i) { + tmp.push(v); + } + } + } + } + } + self.current = Some(tmp); + } else { + unreachable!(); + } + } + ParseToken::Union(indices) => { + if !self.terms.is_empty() { + unimplemented!("union syntax in filter"); + } + + if let Some(ParseToken::Array) = self.tokens.pop() { + let mut tmp = Vec::new(); + if let Some(current) = &self.current { + for v in current { + if let Value::Array(vec) = v { + for i in indices { + if let Some(v) = vec.get(abs_index(i, vec.len())) { + tmp.push(v); + } + } + } + } + } + + self.current = Some(tmp); + } else { + unreachable!(); + } + } + ParseToken::Eof => { + debug!("visit_token eof"); + } } } } -enum SelectorErrorMessage { - EmptyValue, - EmptyPath, +pub struct SelectorMut { + path: Option, + value: Option, } -impl fmt::Display for SelectorErrorMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - SelectorErrorMessage::EmptyValue => write!(f, "Empty value"), - SelectorErrorMessage::EmptyPath => write!(f, "Empty path"), +fn replace_value Value>(tokens: Vec, value: &mut Value, fun: &mut F) { + let mut target = value; + + for (i, token) in tokens.iter().enumerate() { + let target_once = target; + let is_last = i == tokens.len() - 1; + let target_opt = match *target_once { + Value::Object(ref mut map) => { + if is_last { + let v = if let Some(v) = map.get(token) { + fun(v) + } else { + return; + }; + + map.insert(token.clone(), v); + return; + } + map.get_mut(token) + } + Value::Array(ref mut vec) => { + if let Ok(x) = token.parse::() { + if is_last { + let v = { fun(&vec[x]) }; + vec[x] = v; + return; + } + vec.get_mut(x) + } else { + None + } + } + _ => None, + }; + + if let Some(t) = target_opt { + target = t; + } else { + break; } } } + +impl SelectorMut { + pub fn new() -> Self { + SelectorMut { path: None, value: None } + } + + pub fn str_path(&mut self, path: &str) -> Result<&mut Self, JsonPathError> { + self.path = Some(Parser::compile(path).map_err(|e| JsonPathError::Path(e))?); + Ok(self) + } + + pub fn value(&mut self, value: Value) -> &mut Self { + self.value = Some(value); + self + } + + pub fn take(&mut self) -> Option { + self.value.take() + } + + fn compute_paths(&self, mut result: Vec<&Value>) -> Vec> { + fn _walk(origin: &Value, target: &mut Vec<&Value>, tokens: &mut Vec, visited: &mut IndexMap<*const Value, Vec>) -> bool { + trace!("{:?}, {:?}", target, tokens); + + if target.is_empty() { + return true; + } + + target.retain(|t| { + if std::ptr::eq(origin, *t) { + visited.insert(*t, tokens.to_vec()); + false + } else { + true + } + }); + + match origin { + Value::Array(vec) => for (i, v) in vec.iter().enumerate() { + tokens.push(i.to_string()); + if _walk(v, target, tokens, visited) { + return true; + } + tokens.pop(); + }, + Value::Object(map) => for (k, v) in map { + tokens.push(k.clone()); + if _walk(v, target, tokens, visited) { + return true; + } + tokens.pop(); + } + _ => {} + } + + return false; + } + + let mut visited = IndexMap::new(); + + if let Some(origin) = &self.value { + let mut tokens = Vec::new(); + _walk(origin, &mut result, &mut tokens, &mut visited); + } + + visited.iter().map(|(_, v)| v.to_vec()).collect() + } + + pub fn delete(&mut self) -> Result<&mut Self, JsonPathError> { + self.replace_with(&mut |_| Value::Null) + } + + fn select(&self) -> Result, JsonPathError> { + if let Some(node) = &self.path { + let mut selector = Selector::new(); + selector.compiled_path(&node); + + if let Some(value) = &self.value { + selector.value(value); + } + + Ok(selector.select()?) + } else { + Err(JsonPathError::EmptyPath) + } + } + + pub fn replace_with Value>(&mut self, fun: &mut F) -> Result<&mut Self, JsonPathError> { + let paths = { + let result = self.select()?; + self.compute_paths(result) + }; + + if let Some(ref mut value) = &mut self.value { + for tokens in paths { + replace_value(tokens, value, fun); + } + } + + Ok(self) + } +} \ No newline at end of file diff --git a/tests/common.rs b/tests/common.rs new file mode 100644 index 0000000..a2884f7 --- /dev/null +++ b/tests/common.rs @@ -0,0 +1,45 @@ +extern crate env_logger; +extern crate jsonpath_lib as jsonpath; +extern crate serde_json; + +use std::io::Read; + +use serde_json::Value; + +use self::jsonpath::Selector; + +#[allow(dead_code)] +pub fn setup() { + let _ = env_logger::try_init(); +} + +#[allow(dead_code)] +pub fn read_json(path: &str) -> Value { + let mut f = std::fs::File::open(path).unwrap(); + let mut contents = String::new(); + f.read_to_string(&mut contents).unwrap(); + serde_json::from_str(&contents).unwrap() +} + +#[allow(dead_code)] +pub fn read_contents(path: &str) -> String { + let mut f = std::fs::File::open(path).unwrap(); + let mut contents = String::new(); + f.read_to_string(&mut contents).unwrap(); + contents +} + +#[allow(dead_code)] +pub fn select_and_then_compare<'a>(path: &str, json: Value, target: Value) { + let mut s = Selector::new(); + let _ = s.str_path(path); + let _ = s.value(&json); + let result = serde_json::to_value(s.select().unwrap()).unwrap(); + assert_eq!(result, target, "{}", path); +} + +#[allow(dead_code)] +pub fn compare_result<'a>(result: Vec<&Value>, target: Value) { + let result = serde_json::to_value(result).unwrap(); + assert_eq!(result, target); +} diff --git a/tests/filter.rs b/tests/filter.rs index b47e241..101ec8e 100644 --- a/tests/filter.rs +++ b/tests/filter.rs @@ -1,222 +1,140 @@ -extern crate core; -extern crate env_logger; -extern crate jsonpath_lib as jsonpath; #[macro_use] extern crate serde_json; -use std::io::Read; - use serde_json::Value; -use jsonpath::filter::value_filter::{JsonValueFilter, ValueFilter}; -use jsonpath::parser::parser::Parser; +use common::{read_json, select_and_then_compare, setup}; -fn setup() { - let _ = env_logger::try_init(); -} - -fn new_value_filter(file: &str) -> ValueFilter { - let string = read_json(file); - let json: Value = serde_json::from_str(string.as_str()).unwrap(); - ValueFilter::new((&json).into(), false, false) -} - -fn do_filter(path: &str, file: &str) -> JsonValueFilter { - let string = read_json(file); - let mut jf = JsonValueFilter::new(string.as_str()).unwrap(); - let mut parser = Parser::new(path); - parser.parse(&mut jf).unwrap(); - jf -} - -fn read_json(path: &str) -> String { - let mut f = std::fs::File::open(path).unwrap(); - let mut contents = String::new(); - f.read_to_string(&mut contents).unwrap(); - contents -} - -#[test] -fn step_in() { - setup(); - - let mut jf = new_value_filter("./benches/data_obj.json"); - { - let current = jf.step_in_str("friends"); - assert_eq!(current.is_array(), true); - } - - let mut jf = new_value_filter("./benches/data_array.json"); - { - let current = jf.step_in_num(&1.0); - assert_eq!(current.get_val().is_object(), true); - } - { - let current = jf.step_in_str("friends"); - assert_eq!(current.is_array(), true); - } - let mut jf = new_value_filter("./benches/data_obj.json"); - { - jf.step_in_str("school"); - jf.step_in_str("friends"); - jf.step_in_all(); - let current = jf.step_in_str("name"); - let friends = json!([ - "Millicent Norman", - "Vincent Cannon", - "Gray Berry" - ]); - - assert_eq!(friends, current.into_value()); - } - let mut jf = new_value_filter("./benches/data_obj.json"); - { - let current = jf.step_leaves_str("name"); - let names = json!([ - "Leonor Herman", - "Millicent Norman", - "Vincent Cannon", - "Gray Berry", - "Vincent Cannon", - "Gray Berry" - ]); - assert_eq!(names, current.into_value()); - } -} +mod common; #[test] fn array() { setup(); - let friends = json!([ - {"id": 1, "name": "Vincent Cannon" }, - {"id": 2, "name": "Gray Berry"} - ]); + select_and_then_compare("$.school.friends[1, 2]", read_json("./benches/data_obj.json"), json!([ + {"id": 1, "name": "Vincent Cannon" }, + {"id": 2, "name": "Gray Berry"} + ])); - let jf = do_filter("$.school.friends[1, 2]", "./benches/data_obj.json"); - assert_eq!(friends, jf.into_value()); + select_and_then_compare("$.school.friends[1: ]", read_json("./benches/data_obj.json"), json!([ + {"id": 1, "name": "Vincent Cannon" }, + {"id": 2, "name": "Gray Berry"} + ])); - let jf = do_filter("$.school.friends[1:]", "./benches/data_obj.json"); - assert_eq!(friends, jf.into_value()); + select_and_then_compare("$.school.friends[:-2]", read_json("./benches/data_obj.json"), json!([ + {"id": 0, "name": "Millicent Norman"} + ])); - let jf = do_filter("$.school.friends[:-2]", "./benches/data_obj.json"); - let friends = json!([ - {"id": 0, "name": "Millicent Norman"} - ]); - assert_eq!(friends, jf.into_value()); + select_and_then_compare("$..friends[2].name", read_json("./benches/data_obj.json"), json!([ + "Gray Berry", "Gray Berry" + ])); - let jf = do_filter("$..friends[2].name", "./benches/data_obj.json"); - let friends = json!(["Gray Berry", "Gray Berry"]); - assert_eq!(friends, jf.into_value()); + select_and_then_compare("$..friends[*].name", read_json("./benches/data_obj.json"), json!([ + "Vincent Cannon","Gray Berry","Millicent Norman","Vincent Cannon","Gray Berry" + ])); - let jf = do_filter("$..friends[*].name", "./benches/data_obj.json"); - let friends = json!(["Vincent Cannon","Gray Berry","Millicent Norman","Vincent Cannon","Gray Berry"]); - assert_eq!(friends, jf.into_value()); + select_and_then_compare("$['school']['friends'][*].['name']", read_json("./benches/data_obj.json"), json!([ + "Millicent Norman","Vincent Cannon","Gray Berry" + ])); - let jf = do_filter("$['school']['friends'][*].['name']", "./benches/data_obj.json"); - let friends = json!(["Millicent Norman","Vincent Cannon","Gray Berry"]); - assert_eq!(friends, jf.into_value()); - - let jf = do_filter("$['school']['friends'][0].['name']", "./benches/data_obj.json"); - let friends = json!("Millicent Norman"); - assert_eq!(friends, jf.into_value()); + select_and_then_compare("$['school']['friends'][0].['name']", read_json("./benches/data_obj.json"), json!([ + "Millicent Norman" + ])); } #[test] fn return_type() { setup(); - let friends = json!({ - "friends": [ - {"id": 0, "name": "Millicent Norman"}, - {"id": 1, "name": "Vincent Cannon" }, - {"id": 2, "name": "Gray Berry"} - ] - }); - - let jf = do_filter("$.school", "./benches/data_obj.json"); - assert_eq!(friends, jf.into_value()); - - let jf = do_filter("$.school[?(@.friends[0])]", "./benches/data_obj.json"); - assert_eq!(friends, jf.into_value()); - - let jf = do_filter("$.school[?(@.friends[10])]", "./benches/data_obj.json"); - assert_eq!(Value::Null, jf.into_value()); - - let jf = do_filter("$.school[?(1==1)]", "./benches/data_obj.json"); - assert_eq!(friends, jf.into_value()); - - let jf = do_filter("$.school.friends[?(1==1)]", "./benches/data_obj.json"); - let friends = json!([ + select_and_then_compare("$.school", read_json("./benches/data_obj.json"), json!([{ + "friends": [ {"id": 0, "name": "Millicent Norman"}, {"id": 1, "name": "Vincent Cannon" }, {"id": 2, "name": "Gray Berry"} - ]); - assert_eq!(friends, jf.into_value()); + ] + }])); + + select_and_then_compare("$.school[?(@.friends[0])]", read_json("./benches/data_obj.json"), json!([{ + "friends": [ + {"id": 0, "name": "Millicent Norman"}, + {"id": 1, "name": "Vincent Cannon" }, + {"id": 2, "name": "Gray Berry"} + ] + }])); + + select_and_then_compare("$.school[?(@.friends[10])]", read_json("./benches/data_obj.json"), json!([{ + "friends": [ + {"id": 0, "name": "Millicent Norman"}, + {"id": 1, "name": "Vincent Cannon" }, + {"id": 2, "name": "Gray Berry"} + ] + }])); + + select_and_then_compare("$.school[?(1==1)]", read_json("./benches/data_obj.json"), json!([{ + "friends": [ + {"id": 0, "name": "Millicent Norman"}, + {"id": 1, "name": "Vincent Cannon" }, + {"id": 2, "name": "Gray Berry"} + ] + }])); + + select_and_then_compare("$.school.friends[?(1==1)]", read_json("./benches/data_obj.json"), json!([[ + {"id": 0, "name": "Millicent Norman"}, + {"id": 1, "name": "Vincent Cannon" }, + {"id": 2, "name": "Gray Berry"} + ]])); } #[test] fn op_default() { setup(); - let jf = do_filter("$.school[?(@.friends == @.friends)]", "./benches/data_obj.json"); - let friends = json!({ + select_and_then_compare("$.school[?(@.friends == @.friends)]", read_json("./benches/data_obj.json"), json!([{ "friends": [ {"id": 0, "name": "Millicent Norman"}, {"id": 1, "name": "Vincent Cannon" }, {"id": 2, "name": "Gray Berry"} ] - }); - assert_eq!(friends, jf.into_value()); + }])); - let jf = do_filter("$.friends[?(@.name)]", "./benches/data_obj.json"); - let friends = json!([ - { "id" : 1, "name" : "Vincent Cannon" }, - { "id" : 2, "name" : "Gray Berry" } - ]); - assert_eq!(friends, jf.into_value()); + select_and_then_compare("$.friends[?(@.name)]", read_json("./benches/data_obj.json"), json!([ + { "id" : 1, "name" : "Vincent Cannon" }, + { "id" : 2, "name" : "Gray Berry" } + ])); - let jf = do_filter("$.friends[?(@.id >= 2)]", "./benches/data_obj.json"); - let friends = json!([ - { "id" : 2, "name" : "Gray Berry" } - ]); - assert_eq!(friends, jf.into_value()); + select_and_then_compare("$.friends[?(@.id >= 2)]", read_json("./benches/data_obj.json"), json!([ + { "id" : 2, "name" : "Gray Berry" } + ])); - let jf = do_filter("$.friends[?(@.id >= 2 || @.id == 1)]", "./benches/data_obj.json"); - let friends = json!([ - { "id" : 2, "name" : "Gray Berry" }, - { "id" : 1, "name" : "Vincent Cannon" } - ]); - assert_eq!(friends, jf.into_value()); + select_and_then_compare("$.friends[?(@.id >= 2 || @.id == 1)]", read_json("./benches/data_obj.json"), json!([ + { "id" : 2, "name" : "Gray Berry" }, + { "id" : 1, "name" : "Vincent Cannon" } + ])); - let jf = do_filter("$.friends[?( (@.id >= 2 || @.id == 1) && @.id == 0)]", "./benches/data_obj.json"); - assert_eq!(Value::Null, jf.into_value()); + select_and_then_compare("$.friends[?( (@.id >= 2 || @.id == 1) && @.id == 0)]", read_json("./benches/data_obj.json"), json!([ + Value::Null + ])); - let jf = do_filter("$..friends[?(@.id == $.index)].id", "./benches/data_obj.json"); - let friends = json!([0, 0]); - assert_eq!(friends, jf.into_value()); + select_and_then_compare("$..friends[?(@.id == $.index)].id", read_json("./benches/data_obj.json"), json!([ + 0, 0 + ])); - let jf = do_filter("$..book[?($.store.bicycle.price < @.price)].price", "./benches/example.json"); - let friends = json!([22.99]); - assert_eq!(friends, jf.into_value()); + select_and_then_compare("$..book[?($.store.bicycle.price < @.price)].price", read_json("./benches/example.json"), json!([ + 22.99 + ])); - let jf = do_filter("$..book[?( (@.price == 12.99 || @.category == 'reference') && @.price > 10)].price", "./benches/example.json"); - let friends = json!([12.99]); - assert_eq!(friends, jf.into_value()); + select_and_then_compare("$..book[?( (@.price == 12.99 || @.category == 'reference') && @.price > 10)].price", read_json("./benches/example.json"), json!([ + 12.99 + ])); - let ref value = json!([ + select_and_then_compare("$..[?(@.age > 40)]", json!([ { "name": "이름1", "age": 40, "phone": "+33 12341234" }, { "name": "이름2", "age": 42, "phone": "++44 12341234" } - ]); - let mut jf = JsonValueFilter::new_from_value(value.into()); - let mut parser = Parser::new("$..[?(@.age > 40)]"); - parser.parse(&mut jf).unwrap(); - let friends = json!([ - { "name" : "이름2", "age" : 42, "phone" : "++44 12341234" } - ]); - assert_eq!(friends, jf.into_value()); + ]), json!([ + { "name" : "이름2", "age" : 42, "phone" : "++44 12341234" } + ])); - let ref value = json!({ + select_and_then_compare("$..[?(@.age >= 30)]", json!({ "school": { "friends": [ {"name": "친구1", "age": 20}, @@ -226,247 +144,208 @@ fn op_default() { "friends": [ {"name": "친구3", "age": 30}, {"name": "친구4"} - ]}); - let mut jf = JsonValueFilter::new_from_value(value.into()); - let mut parser = Parser::new("$..[?(@.age >= 30)]"); - parser.parse(&mut jf).unwrap(); - let friends = json!([{ "name" : "친구3", "age" : 30 }]); - assert_eq!(friends, jf.into_value()); + ]}), json!([ + { "name" : "친구3", "age" : 30 } + ])); } #[test] fn op_number() { setup(); - let json = json!({ "a": 1 }); - let ret = jsonpath::select(&json, "$.[?(@.a == 1)]").unwrap(); - assert_eq!(json, ret); - let ret = jsonpath::select(&json, "$.[?(@.a != 2)]").unwrap(); - assert_eq!(json, ret); - let ret = jsonpath::select(&json, "$.[?(@.a < 2)]").unwrap(); - assert_eq!(json, ret); - let ret = jsonpath::select(&json, "$.[?(@.a <= 1)]").unwrap(); - assert_eq!(json, ret); - let ret = jsonpath::select(&json, "$.[?(@.a > 0)]").unwrap(); - assert_eq!(json, ret); - let ret = jsonpath::select(&json, "$.[?(@.a >= 0)]").unwrap(); - assert_eq!(json, ret); + select_and_then_compare("$.[?(@.a == 1)]", json!({ "a": 1 }), json!([{ "a": 1 }])); + select_and_then_compare("$.[?(@.a != 2)]", json!({ "a": 1 }), json!([{ "a": 1 }])); + select_and_then_compare("$.[?(@.a < 2)]", json!({ "a": 1 }), json!([{ "a": 1 }])); + select_and_then_compare("$.[?(@.a <= 1)]", json!({ "a": 1 }), json!([{ "a": 1 }])); + select_and_then_compare("$.[?(@.a > 0)]", json!({ "a": 1 }), json!([{ "a": 1 }])); + select_and_then_compare("$.[?(@.a >= 0)]", json!({ "a": 1 }), json!([{ "a": 1 }])); } #[test] fn op_string() { setup(); - let json = json!({ "a": "b" }); - let ret = jsonpath::select(&json, r#"$.[?(@.a == "b")]"#).unwrap(); - assert_eq!(json!({ "a": "b" }), ret); - let ret = jsonpath::select(&json, r#"$.[?(@.a != "c")]"#).unwrap(); - assert_eq!(json!({ "a": "b" }), ret); - let ret = jsonpath::select(&json, r#"$.[?(@.a < "b")]"#).unwrap(); - assert_eq!(Value::Null, ret); - let ret = jsonpath::select(&json, r#"$.[?(@.a <= "b")]"#).unwrap(); - assert_eq!(json!({ "a": "b" }), ret); - let ret = jsonpath::select(&json, r#"$.[?(@.a > "b")]"#).unwrap(); - assert_eq!(Value::Null, ret); - let ret = jsonpath::select(&json, r#"$.[?(@.a >= "b")]"#).unwrap(); - assert_eq!(json!({ "a": "b" }), ret); + select_and_then_compare(r#"$.[?(@.a == "b")]"#, json!({ "a": "b" }), json!([{ "a": "b" }])); + select_and_then_compare(r#"$.[?(@.a != "c")]"#, json!({ "a": "b" }), json!([{ "a": "b" }])); + select_and_then_compare(r#"$.[?(@.a < "b")]"#, json!({ "a": "b" }), json!([Value::Null])); + select_and_then_compare(r#"$.[?(@.a <= "b")]"#, json!({ "a": "b" }), json!([{ "a": "b" }])); + select_and_then_compare(r#"$.[?(@.a > "b")]"#, json!({ "a": "b" }), json!([Value::Null])); + select_and_then_compare(r#"$.[?(@.a >= "b")]"#, json!({ "a": "b" }), json!([{ "a": "b" }])); } #[test] fn op_object() { setup(); - let json = json!({ - "a": { "1": 1 }, - "b": { "2": 2 }, - "c": { "1": 1 }, - }); - let ret = jsonpath::select(&json, r#"$.[?(@.a == @.c)]"#).unwrap(); - assert_eq!(json, ret); - let ret = jsonpath::select(&json, r#"$.[?(@.a != @.c)]"#).unwrap(); - assert_eq!(Value::Null, ret); - let ret = jsonpath::select(&json, r#"$.[?(@.a < @.c)]"#).unwrap(); - assert_eq!(Value::Null, ret); - let ret = jsonpath::select(&json, r#"$.[?(@.a <= @.c)]"#).unwrap(); - assert_eq!(Value::Null, ret); - let ret = jsonpath::select(&json, r#"$.[?(@.a > @.c)]"#).unwrap(); - assert_eq!(Value::Null, ret); - let ret = jsonpath::select(&json, r#"$.[?(@.a >= @.c)]"#).unwrap(); - assert_eq!(Value::Null, ret); + select_and_then_compare(r#"$.[?(@.a == @.c)]"#, + json!({"a": { "1": 1 }, "b": { "2": 2 }, "c": { "1": 1 }}), + json!([{"a": { "1": 1 }, "b": { "2": 2 }, "c": { "1": 1 }}])); + select_and_then_compare(r#"$.[?(@.a != @.c)]"#, + json!({"a": { "1": 1 }, "b": { "2": 2 }, "c": { "1": 1 }}), + json!([Value::Null])); + select_and_then_compare(r#"$.[?(@.a < @.c)]"#, + json!({"a": { "1": 1 }, "b": { "2": 2 }, "c": { "1": 1 }}), + json!([Value::Null])); + select_and_then_compare(r#"$.[?(@.a <= @.c)]"#, + json!({"a": { "1": 1 }, "b": { "2": 2 }, "c": { "1": 1 }}), + json!([Value::Null])); + select_and_then_compare(r#"$.[?(@.a > @.c)]"#, + json!({"a": { "1": 1 }, "b": { "2": 2 }, "c": { "1": 1 }}), + json!([Value::Null])); + select_and_then_compare(r#"$.[?(@.a >= @.c)]"#, + json!({"a": { "1": 1 }, "b": { "2": 2 }, "c": { "1": 1 }}), + json!([Value::Null])); } #[test] fn op_complex() { setup(); - let json = json!({ "a": { "b": 1 } }); - let ret = jsonpath::select(&json, r#"$.[?(1 == @.a)]"#).unwrap(); - assert_eq!(Value::Null, ret); - let ret = jsonpath::select(&json, r#"$.[?("1" != @.a)]"#).unwrap(); - assert_eq!(Value::Null, ret); - let ret = jsonpath::select(&json, r#"$.[?(@.a <= 1)]"#).unwrap(); - assert_eq!(Value::Null, ret); - let ret = jsonpath::select(&json, r#"$.[?(@.a > "1")]"#).unwrap(); - assert_eq!(Value::Null, ret); + select_and_then_compare(r#"$.[?(1 == @.a)]"#, json!({ "a": { "b": 1 } }), json!([Value::Null])); + select_and_then_compare(r#"$.[?("1" != @.a)]"#, json!({ "a": { "b": 1 } }), json!([Value::Null])); + select_and_then_compare(r#"$.[?(@.a <= 1)]"#, json!({ "a": { "b": 1 } }), json!([Value::Null])); + select_and_then_compare(r#"$.[?(@.a > "1")]"#, json!({ "a": { "b": 1 } }), json!([Value::Null])); } #[test] fn example() { setup(); - let jf = do_filter("$.store.book[*].author", "./benches/example.json"); - let ret = json!(["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]); - assert_eq!(ret, jf.into_value()); + select_and_then_compare(r#"$.store.book[*].author"#, read_json("./benches/example.json"), json!([ + "Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien" + ])); - let jf = do_filter("$..author", "./benches/example.json"); - assert_eq!(ret, jf.into_value()); + select_and_then_compare(r#"$..author"#, read_json("./benches/example.json"), json!([ + "Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien" + ])); - let jf = do_filter("$.store.*", "./benches/example.json"); - let ret = json!([ - [ + select_and_then_compare(r#"$.store.*"#, read_json("./benches/example.json"), json!([ + [ {"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} ], {"color" : "red","price" : 19.95}, - ]); - assert_eq!(ret, jf.into_value()); + ])); - let jf = do_filter("$.store..price", "./benches/example.json"); - let ret = json!([8.95, 12.99, 8.99, 22.99, 19.95]); - assert_eq!(ret, jf.into_value()); + select_and_then_compare(r#"$.store..price"#, read_json("./benches/example.json"), json!([ + 8.95, 12.99, 8.99, 22.99, 19.95 + ])); - let jf = do_filter("$..book[2]", "./benches/example.json"); - let ret = json!([{ + select_and_then_compare(r#"$..book[2]"#, read_json("./benches/example.json"), json!([ + { + "category" : "fiction", + "author" : "Herman Melville", + "title" : "Moby Dick", + "isbn" : "0-553-21311-3", + "price" : 8.99 + } + ])); + + select_and_then_compare(r#"$..book[-2]"#, read_json("./benches/example.json"), json!([ + { "category" : "fiction", "author" : "Herman Melville", "title" : "Moby Dick", "isbn" : "0-553-21311-3", "price" : 8.99 - }]); - assert_eq!(ret, jf.into_value()); + } + ])); - let jf = do_filter("$..book[-2]", "./benches/example.json"); - let ret = json!([{ + select_and_then_compare(r#"$..book[0, 1]"#, read_json("./benches/example.json"), json!([ + { + "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 + } + ])); + + select_and_then_compare(r#"$..book[:2]"#, read_json("./benches/example.json"), json!([ + { + "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 + } + ])); + + select_and_then_compare(r#"$..book[2:]"#, read_json("./benches/example.json"), json!([ + { "category" : "fiction", "author" : "Herman Melville", "title" : "Moby Dick", "isbn" : "0-553-21311-3", "price" : 8.99 - }]); - assert_eq!(ret, jf.into_value()); + }, + { + "category" : "fiction", + "author" : "J. R. R. Tolkien", + "title" : "The Lord of the Rings", + "isbn" : "0-395-19395-8", + "price" : 22.99 + } + ])); - let jf = do_filter("$..book[0,1]", "./benches/example.json"); - let ret = json!([ - { - "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 - } - ]); - assert_eq!(ret, jf.into_value()); + select_and_then_compare(r#"$..book[?(@.isbn)]"#, read_json("./benches/example.json"), json!([ + { + "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 + } + ])); - let jf = do_filter("$..book[:2]", "./benches/example.json"); - let ret = json!([ - { - "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 - } - ]); - assert_eq!(ret, jf.into_value()); + select_and_then_compare(r#"$.store.book[?(@.price < 10)]"#, read_json("./benches/example.json"), json!([ + { + "category" : "reference", + "author" : "Nigel Rees", + "title" : "Sayings of the Century", + "price" : 8.95 + }, + { + "category" : "fiction", + "author" : "Herman Melville", + "title" : "Moby Dick", + "isbn" : "0-553-21311-3", + "price" : 8.99 + } + ])); - let jf = do_filter("$..book[2:]", "./benches/example.json"); - let ret = json!([ - { - "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 - } - ]); - assert_eq!(ret, jf.into_value()); - - let jf = do_filter("$..book[?(@.isbn)]", "./benches/example.json"); - let ret = json!([ - { - "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 - } - ]); - assert_eq!(ret, jf.into_value()); - - let jf = do_filter("$.store.book[?(@.price < 10)]", "./benches/example.json"); - let ret = json!([ - { - "category" : "reference", - "author" : "Nigel Rees", - "title" : "Sayings of the Century", - "price" : 8.95 - }, - { - "category" : "fiction", - "author" : "Herman Melville", - "title" : "Moby Dick", - "isbn" : "0-553-21311-3", - "price" : 8.99 - } - ]); - assert_eq!(ret, jf.into_value()); - - let jf = do_filter("$..*", "./benches/example.json"); - let json: Value = serde_json::from_str(read_json("./benches/giveme_every_thing_result.json").as_str()).unwrap(); - assert_eq!(json, jf.into_value()); + select_and_then_compare(r#"$..*"#, read_json("./benches/example.json"), + read_json("./benches/giveme_every_thing_result.json")); } #[test] fn filer_same_obj() { setup(); - let mut jf = JsonValueFilter::new(r#" - { + select_and_then_compare(r#"$..[?(@.a == 1)]"#, json!({ "a": 1, "b" : {"a": 1}, "c" : {"a": 1} - } - "#).unwrap(); - let mut parser = Parser::new("$..[?(@.a == 1)]"); - parser.parse(&mut jf).unwrap(); - let ret = jf.into_value(); - assert_eq!(ret, json!([ + }), json!([ {"a": 1}, {"a": 1} ])); diff --git a/tests/lib.rs b/tests/lib.rs index 6fa8fb0..3ac8f24 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,31 +1,19 @@ -extern crate env_logger; extern crate jsonpath_lib as jsonpath; -extern crate log; extern crate serde; #[macro_use] extern crate serde_json; -use std::io::Read; - -use serde::{Deserialize, Serialize}; +use serde::Deserialize; use serde_json::Value; -fn read_json(path: &str) -> Value { - let mut f = std::fs::File::open(path).unwrap(); - let mut contents = String::new(); - f.read_to_string(&mut contents).unwrap(); - serde_json::from_str(contents.as_str()).unwrap() -} +use common::{compare_result, read_contents, read_json, setup}; -fn read_contents(path: &str) -> String { - let mut f = std::fs::File::open(path).unwrap(); - let mut contents = String::new(); - f.read_to_string(&mut contents).unwrap(); - contents -} +mod common; #[test] fn compile() { + setup(); + let mut template = jsonpath::compile("$..friends[2]"); let json_obj = read_json("./benches/data_obj.json"); let json = template(&json_obj).unwrap(); @@ -33,7 +21,7 @@ fn compile() { {"id": 2,"name": "Gray Berry"}, {"id": 2,"name": "Gray Berry"} ]); - assert_eq!(json, ret); + compare_result(json, ret); let json_obj = read_json("./benches/data_array.json"); let json = template(&json_obj).unwrap(); @@ -41,11 +29,13 @@ fn compile() { {"id": 2,"name": "Gray Berry"}, {"id": 2,"name": "Rosetta Erickson"} ]); - assert_eq!(json, ret); + compare_result(json, ret); } #[test] fn selector() { + setup(); + let json_obj = read_json("./benches/data_obj.json"); let mut reader = jsonpath::selector(&json_obj); let json = reader("$..friends[2]").unwrap(); @@ -53,27 +43,26 @@ fn selector() { {"id": 2,"name": "Gray Berry"}, {"id": 2,"name": "Gray Berry"} ]); - assert_eq!(json, ret); + compare_result(json, ret); let json = reader("$..friends[0]").unwrap(); let ret = json!([ {"id": 0}, {"id": 0,"name": "Millicent Norman"} ]); - assert_eq!(json, ret); + compare_result(json, ret); } #[test] fn selector_as() { - let json_obj = read_json("./benches/data_obj.json"); - let mut selector = jsonpath::selector_as::>(&json_obj); - - #[derive(Serialize, Deserialize, PartialEq, Debug)] + #[derive(Deserialize, PartialEq, Debug)] struct Friend { id: u8, name: Option, } + let json_obj = read_json("./benches/data_obj.json"); + let mut selector = jsonpath::selector_as::(&json_obj); let json = selector("$..friends[2]").unwrap(); let ret = vec!( @@ -101,7 +90,7 @@ fn select() { "isbn" : "0-553-21311-3", "price" : 8.99 }]); - assert_eq!(json, ret); + compare_result(json, ret); } #[test] @@ -121,14 +110,14 @@ fn select_str() { #[test] fn test_to_struct() { - #[derive(Serialize, Deserialize, PartialEq, Debug)] + #[derive(Deserialize, PartialEq, Debug)] struct Person { name: String, age: u8, phones: Vec, } - let ret: Person = jsonpath::select_as(r#" + let ret: Vec = jsonpath::select_as(r#" { "person": { @@ -148,5 +137,5 @@ fn test_to_struct() { phones: vec!["+44 1234567".to_string(), "+44 2345678".to_string()], }; - assert_eq!(person, ret); + assert_eq!(vec![person], ret); } \ No newline at end of file diff --git a/tests/mutable.rs b/tests/mutable.rs new file mode 100644 index 0000000..b61fa07 --- /dev/null +++ b/tests/mutable.rs @@ -0,0 +1,40 @@ +extern crate jsonpath_lib as jsonpath; +#[macro_use] +extern crate serde_json; + +use common::{read_json, setup}; +use jsonpath::{SelectorMut, Selector}; +use serde_json::Value; + +mod common; + +#[test] +fn selector_mut() { + setup(); + + let mut selector_mut = SelectorMut::new(); + + let mut nums = Vec::new(); + let result = selector_mut + .str_path(r#"$.store..price"#).unwrap() + .value(read_json("./benches/example.json")) + .replace_with(&mut |v| { + match v { + Value::Number(n) => { + nums.push(n.as_f64().unwrap()); + } + _ => {} + } + Value::String("a".to_string()) + }).unwrap() + .take().unwrap(); + + assert_eq!(nums, vec![8.95_f64, 12.99_f64, 8.99_f64, 22.99_f64, 19.95_f64]); + + let mut selector = Selector::new(); + let result = selector.str_path(r#"$.store..price"#).unwrap() + .value(&result) + .select().unwrap(); + + assert_eq!(vec![&json!("a"), &json!("a"), &json!("a"), &json!("a"), &json!("a")], result); +} \ No newline at end of file diff --git a/tests/parser.rs b/tests/parser.rs deleted file mode 100644 index b0973fb..0000000 --- a/tests/parser.rs +++ /dev/null @@ -1,312 +0,0 @@ -extern crate env_logger; -extern crate jsonpath_lib as jsonpath; - -use std::result; -use jsonpath::parser::parser::{Parser, ParseToken, NodeVisitor, FilterToken}; - -struct NodeVisitorTestImpl<'a> { - input: &'a str, - stack: Vec, -} - -impl<'a> NodeVisitorTestImpl<'a> { - fn new(input: &'a str) -> Self { - NodeVisitorTestImpl { input, stack: Vec::new() } - } - - fn visit(&mut self) -> result::Result, String> { - let mut parser = Parser::new(self.input); - parser.parse(self)?; - Ok(self.stack.split_off(0)) - } -} - -impl<'a> NodeVisitor for NodeVisitorTestImpl<'a> { - fn visit_token(&mut self, token: ParseToken) { - self.stack.push(token); - } -} - -fn setup() { - let _ = env_logger::try_init(); -} - -fn run(input: &str) -> result::Result, String> { - let mut interpreter = NodeVisitorTestImpl::new(input); - interpreter.visit() -} - -#[test] -fn parse_path() { - setup(); - - assert_eq!(run("$.aa"), Ok(vec![ - ParseToken::Absolute, - ParseToken::In, - ParseToken::Key("aa".to_owned()) - ])); - - assert_eq!(run("$.00.a"), Ok(vec![ - ParseToken::Absolute, - ParseToken::In, - ParseToken::Key("00".to_owned()), - ParseToken::In, - ParseToken::Key("a".to_owned()) - ])); - - assert_eq!(run("$.00.韓창.seok"), Ok(vec![ - ParseToken::Absolute, - ParseToken::In, - ParseToken::Key("00".to_owned()), - ParseToken::In, - ParseToken::Key("韓창".to_owned()), - ParseToken::In, - ParseToken::Key("seok".to_owned()) - ])); - - assert_eq!(run("$.*"), Ok(vec![ - ParseToken::Absolute, - ParseToken::In, - ParseToken::All - ])); - - assert_eq!(run("$..*"), Ok(vec![ - ParseToken::Absolute, - ParseToken::Leaves, - ParseToken::All - ])); - - assert_eq!(run("$..[0]"), Ok(vec![ - ParseToken::Absolute, - ParseToken::Leaves, - ParseToken::Array, - ParseToken::Number(0.0), - ParseToken::ArrayEof - ])); - - match run("$.") { - Ok(_) => panic!(), - _ => {} - } - - match run("$..") { - Ok(_) => panic!(), - _ => {} - } - - match run("$. a") { - Ok(_) => panic!(), - _ => {} - } -} - -#[test] -fn parse_array_sytax() { - setup(); - - assert_eq!(run("$.book[?(@.isbn)]"), Ok(vec![ - ParseToken::Absolute, - ParseToken::In, - ParseToken::Key("book".to_string()), - ParseToken::Array, - ParseToken::Relative, - ParseToken::In, - ParseToken::Key("isbn".to_string()), - ParseToken::ArrayEof - ])); - - // - // Array도 컨텍스트 In으로 간주 할거라서 중첩되면 하나만 - // - assert_eq!(run("$.[*]"), Ok(vec![ - ParseToken::Absolute, - ParseToken::Array, - ParseToken::All, - ParseToken::ArrayEof - ])); - - assert_eq!(run("$.a[*]"), Ok(vec![ - ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), - ParseToken::Array, - ParseToken::All, - ParseToken::ArrayEof - ])); - - assert_eq!(run("$.a[*].가"), Ok(vec![ - ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), - ParseToken::Array, - ParseToken::All, - ParseToken::ArrayEof, - ParseToken::In, ParseToken::Key("가".to_owned()) - ])); - - assert_eq!(run("$.a[0][1]"), Ok(vec![ - ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), - ParseToken::Array, - ParseToken::Number(0_f64), - ParseToken::ArrayEof, - ParseToken::Array, - ParseToken::Number(1_f64), - ParseToken::ArrayEof - ])); - - assert_eq!(run("$.a[1,2]"), Ok(vec![ - ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), - ParseToken::Array, - ParseToken::Union(vec![1, 2]), - ParseToken::ArrayEof - ])); - - assert_eq!(run("$.a[10:]"), Ok(vec![ - ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), - ParseToken::Array, - ParseToken::Range(Some(10), None), - ParseToken::ArrayEof - ])); - - assert_eq!(run("$.a[:11]"), Ok(vec![ - ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), - ParseToken::Array, - ParseToken::Range(None, Some(11)), - ParseToken::ArrayEof - ])); - - assert_eq!(run("$.a[-12:13]"), Ok(vec![ - ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), - ParseToken::Array, - ParseToken::Range(Some(-12), Some(13)), - ParseToken::ArrayEof - ])); - - assert_eq!(run("$.a[?(1>2)]"), Ok(vec![ - ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), - ParseToken::Array, - ParseToken::Number(1_f64), ParseToken::Number(2_f64), ParseToken::Filter(FilterToken::Greater), - ParseToken::ArrayEof - ])); - - assert_eq!(run("$.a[?($.b>3)]"), Ok(vec![ - ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), - ParseToken::Array, - ParseToken::Absolute, ParseToken::In, ParseToken::Key("b".to_owned()), ParseToken::Number(3_f64), ParseToken::Filter(FilterToken::Greater), - ParseToken::ArrayEof - ])); - - assert_eq!(run("$[?($.c>@.d && 1==2)]"), Ok(vec![ - ParseToken::Absolute, - ParseToken::Array, - ParseToken::Absolute, ParseToken::In, ParseToken::Key("c".to_owned()), - ParseToken::Relative, ParseToken::In, ParseToken::Key("d".to_owned()), - ParseToken::Filter(FilterToken::Greater), - ParseToken::Number(1_f64), ParseToken::Number(2_f64), ParseToken::Filter(FilterToken::Equal), - ParseToken::Filter(FilterToken::And), - ParseToken::ArrayEof - ])); - - assert_eq!(run("$[?($.c>@.d&&(1==2||3>=4))]"), Ok(vec![ - ParseToken::Absolute, - ParseToken::Array, - ParseToken::Absolute, ParseToken::In, ParseToken::Key("c".to_owned()), - ParseToken::Relative, ParseToken::In, ParseToken::Key("d".to_owned()), - ParseToken::Filter(FilterToken::Greater), - ParseToken::Number(1_f64), ParseToken::Number(2_f64), ParseToken::Filter(FilterToken::Equal), - ParseToken::Number(3_f64), ParseToken::Number(4_f64), ParseToken::Filter(FilterToken::GreaterOrEqual), - ParseToken::Filter(FilterToken::Or), - ParseToken::Filter(FilterToken::And), - ParseToken::ArrayEof - ])); - - assert_eq!(run("$[?(@.a<@.b)]"), Ok(vec![ - ParseToken::Absolute, - ParseToken::Array, - ParseToken::Relative, ParseToken::In, ParseToken::Key("a".to_owned()), - ParseToken::Relative, ParseToken::In, ParseToken::Key("b".to_owned()), - ParseToken::Filter(FilterToken::Little), - ParseToken::ArrayEof - ])); - - assert_eq!(run("$[*][*][*]"), Ok(vec![ - ParseToken::Absolute, - ParseToken::Array, - ParseToken::All, - ParseToken::ArrayEof, - ParseToken::Array, - ParseToken::All, - ParseToken::ArrayEof, - ParseToken::Array, - ParseToken::All, - ParseToken::ArrayEof - ])); - - assert_eq!(run("$['a']['bb']"), Ok(vec![ - ParseToken::Absolute, - ParseToken::Array, - ParseToken::Key("a".to_string()), - ParseToken::ArrayEof, - ParseToken::Array, - ParseToken::Key("bb".to_string()), - ParseToken::ArrayEof - ])); - - match run("$[") { - Ok(_) => panic!(), - _ => {} - } - - match run("$[]") { - Ok(_) => panic!(), - _ => {} - } - - match run("$[a]") { - Ok(_) => panic!(), - _ => {} - } - - match run("$[?($.a)]") { - Ok(_) => panic!(), - _ => {} - } - - match run("$[?(@.a > @.b]") { - Ok(_) => panic!(), - _ => {} - } - - match run("$[?(@.a < @.b&&(@.c < @.d)]") { - Ok(_) => panic!(), - _ => {} - } -} - -#[test] -fn parse_array_float() { - setup(); - - assert_eq!(run("$[?(1.1<2.1)]"), Ok(vec![ - ParseToken::Absolute, - ParseToken::Array, - ParseToken::Number(1.1), ParseToken::Number(2.1), ParseToken::Filter(FilterToken::Little), - ParseToken::ArrayEof - ])); - - match run("$[1.1]") { - Ok(_) => panic!(), - _ => {} - } - - match run("$[?(1.1<.2)]") { - Ok(_) => panic!(), - _ => {} - } - - match run("$[?(1.1<2.)]") { - Ok(_) => panic!(), - _ => {} - } - - match run("$[?(1.1<2.a)]") { - Ok(_) => panic!(), - _ => {} - } -} \ No newline at end of file diff --git a/tests/readme.rs b/tests/readme.rs index 4243fa5..ef98673 100644 --- a/tests/readme.rs +++ b/tests/readme.rs @@ -3,14 +3,137 @@ extern crate serde; #[macro_use] extern crate serde_json; -use serde::{Deserialize, Serialize}; +use serde::Deserialize; use serde_json::Value; -use jsonpath::Selector; +use jsonpath::{Selector, SelectorMut}; + +mod common; + +#[test] +fn readme() { + let json_obj = json!({ + "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 + } + }, + "expensive": 10 + }); + + let mut selector = jsonpath::selector(&json_obj); + + assert_eq!(selector("$.store.book[*].author").unwrap(), + vec![ + "Nigel Rees", "Evelyn Waugh", "Herman Melville", "J. R. R. Tolkien" + ]); + + assert_eq!(selector("$..author").unwrap(), + vec![ + "Nigel Rees", "Evelyn Waugh", "Herman Melville", "J. R. R. Tolkien" + ]); + + assert_eq!(selector("$.store.*").unwrap(), + vec![ + &json!([ + { "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 } + ]), + &json!({ "color": "red", "price": 19.95 }) + ]); + + assert_eq!(selector("$.store..price").unwrap(), + vec![ + 8.95, 12.99, 8.99, 22.99, 19.95 + ]); + + assert_eq!(selector("$..book[2]").unwrap(), + vec![ + &json!({ + "category" : "fiction", + "author" : "Herman Melville", + "title" : "Moby Dick", + "isbn" : "0-553-21311-3", + "price" : 8.99 + }) + ]); + + assert_eq!(selector("$..book[-2]").unwrap(), + vec![ + &json!({ + "category" : "fiction", + "author" : "Herman Melville", + "title" : "Moby Dick", + "isbn" : "0-553-21311-3", + "price" : 8.99 + }) + ]); + + assert_eq!(selector("$..book[0,1]").unwrap(), + vec![ + &json!({"category" : "reference","author" : "Nigel Rees","title" : "Sayings of the Century","price" : 8.95}), + &json!({"category" : "fiction","author" : "Evelyn Waugh","title" : "Sword of Honour","price" : 12.99}) + ]); + + assert_eq!(selector("$..book[:2]").unwrap(), + vec![ + &json!({"category" : "reference","author" : "Nigel Rees","title" : "Sayings of the Century","price" : 8.95}), + &json!({"category" : "fiction","author" : "Evelyn Waugh","title" : "Sword of Honour","price" : 12.99}) + ]); + + assert_eq!(selector("$..book[:2]").unwrap(), + vec![ + &json!({"category" : "reference","author" : "Nigel Rees","title" : "Sayings of the Century","price" : 8.95}), + &json!({"category" : "fiction","author" : "Evelyn Waugh","title" : "Sword of Honour","price" : 12.99}) + ]); + + assert_eq!(selector("$..book[?(@.isbn)]").unwrap(), + vec![ + &json!({"category" : "fiction","author" : "Herman Melville","title" : "Moby Dick","isbn" : "0-553-21311-3","price" : 8.99}), + &json!({"category" : "fiction","author" : "J. R. R. Tolkien","title" : "The Lord of the Rings","isbn" : "0-395-19395-8","price" : 22.99}) + ]); + + assert_eq!(selector("$.store.book[?(@.price < 10)]").unwrap(), + vec![ + &json!({"category" : "reference","author" : "Nigel Rees","title" : "Sayings of the Century","price" : 8.95}), + &json!({"category" : "fiction","author" : "Herman Melville","title" : "Moby Dick","isbn" : "0-553-21311-3","price" : 8.99}) + ]); +} #[test] fn readme_selector() { - #[derive(Serialize, Deserialize, PartialEq, Debug)] + #[derive(Deserialize, PartialEq, Debug)] struct Friend { name: String, age: Option, @@ -31,43 +154,60 @@ fn readme_selector() { let mut selector = Selector::new(); let result = selector - .path("$..[?(@.age >= 30)]").unwrap() -// .value_from_str(&serde_json::to_string(&json_obj).unwrap() /*&str*/).unwrap() -// .value_from(&json_obj /*&impl serde::ser::Serialize*/).unwrap() - .value(&json_obj /*serde_json::value::Value*/).unwrap() - .select_as_value().unwrap(); + .str_path("$..[?(@.age >= 30)]").unwrap() + .value(&json_obj) + .select().unwrap(); - assert_eq!(json!([{"name": "친구3", "age": 30}]), result); + assert_eq!(vec![&json!({"name": "친구3", "age": 30})], result); let result = selector.select_as_str().unwrap(); assert_eq!(r#"[{"name":"친구3","age":30}]"#, result); - let result = selector.select_as::>().unwrap(); + let result = selector.select_as::().unwrap(); assert_eq!(vec![Friend { name: "친구3".to_string(), age: Some(30) }], result); +} - let _ = selector.map(|v| { - let r = match v { - Value::Array(mut vec) => { - for v in &mut vec { - v.as_object_mut().unwrap().remove("age"); - } - Value::Array(vec) - } - _ => Value::Null - }; - Some(r) - }); - assert_eq!(json!([{ "name": "친구3"}]), selector.get().unwrap()); +#[test] +fn readme_selector_mut() { + let json_obj = json!({ + "school": { + "friends": [ + {"name": "친구1", "age": 20}, + {"name": "친구2", "age": 20} + ] + }, + "friends": [ + {"name": "친구3", "age": 30}, + {"name": "친구4"} + ]}); - let _ = selector.value(&json_obj).unwrap() - .map_as(|mut v: Vec| { - let mut f = v.pop().unwrap(); - f.name = "friend3".to_string(); - f.age = None; - Some(vec![f]) - }); - assert_eq!(vec![Friend { name: "friend3".to_string(), age: None }], - selector.get_as::>().unwrap()); + let mut selector_mut = SelectorMut::new(); + + let result = selector_mut + .str_path("$..[?(@.age == 20)].age").unwrap() + .value(json_obj) + .replace_with(&mut |v| { + let age = if let Value::Number(n) = v { + n.as_u64().unwrap() * 2 + } else { + 0 + }; + + json!(age) + }).unwrap() + .take().unwrap(); + + assert_eq!(result, json!({ + "school": { + "friends": [ + {"name": "친구1", "age": 40}, + {"name": "친구2", "age": 40} + ] + }, + "friends": [ + {"name": "친구3", "age": 30}, + {"name": "친구4"} + ]})); } #[test] @@ -86,11 +226,10 @@ fn readme_select() { let json = jsonpath::select(&json_obj, "$..friends[0]").unwrap(); - let ret = json!([ - {"name": "친구3", "age": 30}, - {"name": "친구1", "age": 20} + assert_eq!(json, vec![ + &json!({"name": "친구3", "age": 30}), + &json!({"name": "친구1", "age": 20}) ]); - assert_eq!(json, ret); } #[test] @@ -122,7 +261,7 @@ fn readme_select_as() { phones: Vec, } - let ret: Person = jsonpath::select_as(r#" + let ret: Vec = jsonpath::select_as(r#" { "person": { @@ -142,12 +281,12 @@ fn readme_select_as() { phones: vec!["+44 1234567".to_string(), "+44 2345678".to_string()], }; - assert_eq!(person, ret); + assert_eq!(ret[0], person); } #[test] fn readme_compile() { - let mut template = jsonpath::compile("$..friends[0]"); + let mut first_firend = jsonpath::compile("$..friends[0]"); let json_obj = json!({ "school": { @@ -161,14 +300,12 @@ fn readme_compile() { {"name": "친구4"} ]}); - let json = template(&json_obj).unwrap(); + let json = first_firend(&json_obj).unwrap(); - let ret = json!([ - {"name": "친구3", "age": 30}, - {"name": "친구1", "age": 20} + assert_eq!(json, vec![ + &json!({"name": "친구3", "age": 30}), + &json!({"name": "친구1", "age": 20}) ]); - - assert_eq!(json, ret); } #[test] @@ -189,21 +326,17 @@ fn readme_selector_fn() { let json = selector("$..friends[0]").unwrap(); - let ret = json!([ - {"name": "친구3", "age": 30}, - {"name": "친구1", "age": 20} + assert_eq!(json, vec![ + &json!({"name": "친구3", "age": 30}), + &json!({"name": "친구1", "age": 20}) ]); - assert_eq!(json, ret); - let json = selector("$..friends[1]").unwrap(); - let ret = json!([ - {"name": "친구4"}, - {"name": "친구2", "age": 20} + assert_eq!(json, vec![ + &json!({"name": "친구4"}), + &json!({"name": "친구2", "age": 20}) ]); - - assert_eq!(json, ret); } #[test] @@ -220,13 +353,13 @@ fn readme_selector_as() { {"name": "친구4"} ]}); - #[derive(Serialize, Deserialize, PartialEq, Debug)] + #[derive(Deserialize, PartialEq, Debug)] struct Friend { name: String, age: Option, } - let mut selector = jsonpath::selector_as::>(&json_obj); + let mut selector = jsonpath::selector_as::(&json_obj); let json = selector("$..friends[0]").unwrap(); @@ -244,4 +377,91 @@ fn readme_selector_as() { ); assert_eq!(json, ret); +} + + +#[test] +fn readme_delete() { + let json_obj = json!({ + "school": { + "friends": [ + {"name": "친구1", "age": 20}, + {"name": "친구2", "age": 20} + ] + }, + "friends": [ + {"name": "친구3", "age": 30}, + {"name": "친구4"} + ]}); + + let ret = jsonpath::delete(json_obj, "$..[?(20 == @.age)]").unwrap(); + + assert_eq!(ret, json!({ + "school": { + "friends": [ + null, + null + ] + }, + "friends": [ + {"name": "친구3", "age": 30}, + {"name": "친구4"} + ]})); +} + +#[test] +fn readme_delete2() { + let json_obj = common::read_json("./benches/example.json"); + + let ret = jsonpath::delete(json_obj, "$.store.book").unwrap(); + + println!("{:?}", ret); + + assert_eq!(ret, json!({ + "store": { + "book": null, + "bicycle": { + "color": "red", + "price": 19.95 + } + }, + "expensive": 10 + })); +} + +#[test] +fn readme_replace_with() { + let json_obj = json!({ + "school": { + "friends": [ + {"name": "친구1", "age": 20}, + {"name": "친구2", "age": 20} + ] + }, + "friends": [ + {"name": "친구3", "age": 30}, + {"name": "친구4"} + ]}); + + let result = jsonpath::replace_with(json_obj, "$..[?(@.age == 20)].age", &mut |v| { + let age = if let Value::Number(n) = v { + n.as_u64().unwrap() * 2 + } else { + 0 + }; + + json!(age) + }).unwrap(); + + assert_eq!(result, json!({ + "school": { + "friends": [ + {"name": "친구1", "age": 40}, + {"name": "친구2", "age": 40} + ] + }, + "friends": [ + {"name": "친구3", "age": 30}, + {"name": "친구4"} + ]})); } \ No newline at end of file diff --git a/tests/selector.rs b/tests/selector.rs deleted file mode 100644 index 464761d..0000000 --- a/tests/selector.rs +++ /dev/null @@ -1,209 +0,0 @@ -extern crate jsonpath_lib as jsonpath; -extern crate serde; -#[macro_use] -extern crate serde_json; - -use serde::{Deserialize, Serialize}; -use serde_json::Value; - -use jsonpath::Selector; - -#[derive(Serialize, Deserialize, PartialEq, Debug)] -struct Person { - name: String, - age: u8, - phone: String, -} - -fn input_str() -> &'static str { - r#"[ - { - "name": "이름1", - "age": 40, - "phone": "+33 12341234" - }, - { - "name": "이름2", - "age": 42, - "phone": "++44 12341234" - }, - { - "name": "이름3", - "age": 50, - "phone": "++55 111111" - }, - { - "name": "이름4", - "age": 51, - "phone": "++55 12341234" - } - ]"# -} - -fn input_json() -> Value { - serde_json::from_str(input_str()).unwrap() -} - -fn input_person() -> Vec { - serde_json::from_str(input_str()).unwrap() -} - -#[test] -fn selector_value_from() { - let result = Selector::new() - .path("$..[?(@.age > 40)]").unwrap() - .value_from(&input_person()).unwrap() - .select_as::>().unwrap(); - assert_eq!(input_person()[1], result[0]); -} - -#[test] -fn selector_value() { - let result = Selector::new() - .path("$..[?(@.age > 40)]").unwrap() - .value((&input_json()).into()).unwrap() - .select_as_value().unwrap(); - assert_eq!(input_json()[1], result[0]); -} - -#[test] -fn selector_value_from_str() { - let result = Selector::new() - .path("$..[?(@.age > 40)]").unwrap() - .value_from_str(input_str()).unwrap() - .select_as_value().unwrap(); - assert_eq!(input_json()[1], result[0]); -} - -#[test] -fn selector_select_to() { - let mut selector = Selector::new(); - - let result = selector - .path("$..[?(@.age > 40)]").unwrap() - .value_from_str(input_str()).unwrap() - .select_as_value().unwrap(); - assert_eq!(input_json()[1], result[0]); - - let result = selector.select_as_str().unwrap(); - let value: Value = serde_json::from_str(&result).unwrap(); - assert_eq!(input_json()[1], value[0]); - - let result = selector.select_as::>().unwrap(); - assert_eq!(input_person()[1], result[0]); - - let _ = selector.path("$..[?(@.age == 40)]"); - - let result = selector.select_as_value().unwrap(); - assert_eq!(input_json()[0], result[0]); - - let result = selector.select_as_str().unwrap(); - assert_eq!(serde_json::to_string(&vec![&input_json()[0].clone()]).unwrap(), result); - - let result = selector.select_as::>().unwrap(); - assert_eq!(input_person()[0], result[0]); -} - -fn _remove_name(v: Value) -> Option { - let r = match v { - Value::Array(mut vec) => { - for v in &mut vec { - v.as_object_mut().unwrap().remove("name"); - } - Value::Array(vec) - } - _ => Value::Null - }; - Some(r) -} - -fn _change_phone_number(v: Value) -> Option { - let r = match v { - Value::Array(mut vec) => { - let mut v = vec.pop().unwrap(); - v.as_object_mut().unwrap() - .insert("phone".to_string(), Value::String("1234".to_string())); - v - } - _ => Value::Null - }; - Some(r) -} - -fn _rejuvenate(mut vec: Vec) -> Option> { - for p in &mut vec { - p.age = p.age - 10; - } - Some(vec) -} - -#[test] -fn selector_map_basic() { - let mut selector = Selector::new(); - - let result = selector - .path("$..[?(@.age > 40)]").unwrap() - .value_from_str(input_str()).unwrap() - .map(_remove_name).unwrap() - .get().unwrap(); - - assert_eq!(result, json!([ - {"phone": "++44 12341234", "age": 42}, - {"phone": "++55 111111", "age": 50}, - {"phone": "++55 12341234", "age": 51}, - ])); -} - -#[test] -fn selector_map() { - let mut selector = Selector::new(); - - let result = selector - .path("$..[?(@.age > 40)]").unwrap() - .value_from_str(input_str()).unwrap() - .map(_remove_name).unwrap() - .path("$..[?(@.age == 50)]").unwrap() - .map(_change_phone_number).unwrap() - .get().unwrap(); - - assert_eq!(result, json!({ - "phone": "1234", - "age": 50, - })); -} - -#[test] -fn selector_map_as_basic() { - let mut selector = Selector::new(); - - let result = selector - .path("$..[?(@.age > 40)]").unwrap() - .value_from_str(input_str()).unwrap() - .map_as(_rejuvenate).unwrap() - .get().unwrap(); - - assert_eq!(result, json!([ - {"name": "이름2", "phone": "++44 12341234", "age": 32}, - {"name": "이름3", "phone": "++55 111111", "age": 40}, - {"name": "이름4", "phone": "++55 12341234", "age": 41}, - ])); -} - -#[test] -fn selector_map_as() { - let mut selector = Selector::new(); - - let result = selector - .path("$..[?(@.age > 40)]").unwrap() - .value_from_str(input_str()).unwrap() - .map_as(_rejuvenate).unwrap() - .path("$..[?(@.age == 40)]").unwrap() - .map(_change_phone_number).unwrap() - .get().unwrap(); - - assert_eq!(result, json!({ - "name": "이름3", - "phone": "1234", - "age": 40, - })); -} diff --git a/tests/serde.rs b/tests/serde.rs deleted file mode 100644 index 5723ac0..0000000 --- a/tests/serde.rs +++ /dev/null @@ -1,39 +0,0 @@ -extern crate jsonpath_lib as jsonpath; -extern crate serde; -extern crate serde_json; - -use std::io::Read; - -use serde_json::Value; -use jsonpath::ref_value::model::{RefValue, RefValueWrapper}; - -fn read_json(path: &str) -> String { - let mut f = std::fs::File::open(path).unwrap(); - let mut contents = String::new(); - f.read_to_string(&mut contents).unwrap(); - contents -} - -#[test] -fn de() { - let json_str = read_json("./benches/example.json"); - // RefValue -> Value - let ref_value: RefValue = serde_json::from_str(json_str.as_str()).unwrap(); - let ref value_wrapper: RefValueWrapper = ref_value.into(); - let value: Value = value_wrapper.into(); - - // Value - let json: Value = serde_json::from_str(json_str.as_str()).unwrap(); - assert_eq!(value, json); -} - -#[test] -fn ser() { - let json_str = read_json("./benches/example.json"); - let ref_value: RefValue = serde_json::from_str(json_str.as_str()).unwrap(); - let ref_value_str = serde_json::to_string(&ref_value).unwrap(); - - let json: Value = serde_json::from_str(json_str.as_str()).unwrap(); - let json_str = serde_json::to_string(&json).unwrap(); - assert_eq!(ref_value_str, json_str); -} \ No newline at end of file diff --git a/tests/tokenizer.rs b/tests/tokenizer.rs deleted file mode 100644 index dbd14f8..0000000 --- a/tests/tokenizer.rs +++ /dev/null @@ -1,194 +0,0 @@ -extern crate jsonpath_lib as jsonpath; - -use jsonpath::parser::tokenizer::{Tokenizer, Token, TokenError, PreloadedTokenizer}; - -fn collect_token(input: &str) -> (Vec, Option) { - let mut tokenizer = Tokenizer::new(input); - let mut vec = vec![]; - loop { - match tokenizer.next_token() { - Ok(t) => vec.push(t), - Err(e) => return (vec, Some(e)), - } - } -} - -fn run(input: &str, expected: (Vec, Option)) { - let (vec, err) = collect_token(input.clone()); - assert_eq!((vec, err), expected, "\"{}\"", input); -} - -#[test] -fn peek() { - let mut tokenizer = PreloadedTokenizer::new("$.a"); - match tokenizer.next_token() { - Ok(t) => assert_eq!(Token::Absolute(0), t), - _ => panic!() - } - - match tokenizer.peek_token() { - Ok(t) => assert_eq!(&Token::Dot(1), t), - _ => panic!() - } - - match tokenizer.peek_token() { - Ok(t) => assert_eq!(&Token::Dot(1), t), - _ => panic!() - } - - match tokenizer.next_token() { - Ok(t) => assert_eq!(Token::Dot(1), t), - _ => panic!() - } -} - -#[test] -fn token() { - run("$.01.a", - ( - vec![ - Token::Absolute(0), - Token::Dot(1), - Token::Key(2, "01".to_string()), - Token::Dot(4), - Token::Key(5, "a".to_string()) - ] - , Some(TokenError::Eof) - )); - - run("$. []", - ( - vec![ - Token::Absolute(0), - Token::Dot(1), - Token::Whitespace(2, 2), - Token::OpenArray(5), - Token::CloseArray(6) - ] - , Some(TokenError::Eof) - )); - - run("$..", - ( - vec![ - Token::Absolute(0), - Token::Dot(1), - Token::Dot(2), - ] - , Some(TokenError::Eof) - )); - - run("$..ab", - ( - vec![ - Token::Absolute(0), - Token::Dot(1), - Token::Dot(2), - Token::Key(3, "ab".to_string()) - ] - , Some(TokenError::Eof) - )); - - run("$..가 [", - ( - vec![ - Token::Absolute(0), - Token::Dot(1), - Token::Dot(2), - Token::Key(3, "가".to_string()), - Token::Whitespace(6, 0), - Token::OpenArray(7), - ] - , Some(TokenError::Eof) - )); - - run("[-1, 2 ]", - ( - vec![ - Token::OpenArray(0), - Token::Key(1, "-1".to_string()), - Token::Comma(3), - Token::Whitespace(4, 0), - Token::Key(5, "2".to_string()), - Token::Whitespace(6, 0), - Token::CloseArray(7), - ] - , Some(TokenError::Eof) - )); - - run("[ 1 2 , 3 \"abc\" : -10 ]", - ( - vec![ - Token::OpenArray(0), - Token::Whitespace(1, 0), - Token::Key(2, "1".to_string()), - Token::Whitespace(3, 0), - Token::Key(4, "2".to_string()), - Token::Whitespace(5, 0), - Token::Comma(6), - Token::Whitespace(7, 0), - Token::Key(8, "3".to_string()), - Token::Whitespace(9, 0), - Token::DoubleQuoted(10, "abc".to_string()), - Token::Whitespace(15, 0), - Token::Split(16), - Token::Whitespace(17, 0), - Token::Key(18, "-10".to_string()), - Token::Whitespace(21, 0), - Token::CloseArray(22), - ] - , Some(TokenError::Eof) - )); - - run("?(@.a가 <41.01)", - ( - vec![ - Token::Question(0), - Token::OpenParenthesis(1), - Token::At(2), - Token::Dot(3), - Token::Key(4, "a가".to_string()), - Token::Whitespace(8, 0), - Token::Little(9), - Token::Key(10, "41".to_string()), - Token::Dot(12), - Token::Key(13, "01".to_string()), - Token::CloseParenthesis(15), - ] - , Some(TokenError::Eof) - )); - - run("?(@.a <4a.01)", - ( - vec![ - Token::Question(0), - Token::OpenParenthesis(1), - Token::At(2), - Token::Dot(3), - Token::Key(4, "a".to_string()), - Token::Whitespace(5, 0), - Token::Little(6), - Token::Key(7, "4a".to_string()), - Token::Dot(9), - Token::Key(10, "01".to_string()), - Token::CloseParenthesis(12), - ] - , Some(TokenError::Eof) - )); - - run("?($.c>@.d)", ( - vec![ - Token::Question(0), - Token::OpenParenthesis(1), - Token::Absolute(2), - Token::Dot(3), - Token::Key(4, "c".to_string()), - Token::Greater(5), - Token::At(6), - Token::Dot(7), - Token::Key(8, "d".to_string()), - Token::CloseParenthesis(9) - ] - , Some(TokenError::Eof) - )); -} \ No newline at end of file diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index fa4bd9d..dc13ab0 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jsonpath-wasm" -version = "0.1.3" +version = "0.2.0" authors = ["Changseok Han "] description = "It is Webassembly version of jsonpath_lib that is JsonPath engine written in Rust - Demo: https://freestrings.github.io/jsonpath" keywords = ["jsonpath", "json", "webassembly", "parsing", "rust"] diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index a495d24..25abf1c 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -1,28 +1,15 @@ extern crate cfg_if; extern crate js_sys; extern crate jsonpath_lib as jsonpath; -#[macro_use] -extern crate lazy_static; -extern crate serde; extern crate serde_json; extern crate wasm_bindgen; -extern crate web_sys; - -use std::collections::HashMap; -use std::ops::Deref; -use std::result; -use std::result::Result; -use std::sync::Mutex; use cfg_if::cfg_if; -use jsonpath::filter::value_filter::JsonValueFilter; -use jsonpath::parser::parser::{Node, NodeVisitor, Parser}; -use jsonpath::ref_value::model::{RefValue, RefValueWrapper}; +use jsonpath::{JsonPathError, Parser}; use jsonpath::Selector as _Selector; +use jsonpath::SelectorMut as _SelectorMut; use serde_json::Value; -use wasm_bindgen::*; use wasm_bindgen::prelude::*; -use web_sys::console; cfg_if! { if #[cfg(feature = "wee_alloc")] { @@ -42,14 +29,14 @@ cfg_if! { } } -fn filter_ref_value(json: RefValueWrapper, node: Node) -> JsValue { - let mut jf = JsonValueFilter::new_from_value(json); - jf.visit(node); - let taken = &jf.take_value(); - match JsValue::from_serde(taken.deref()) { - Ok(js_value) => js_value, - Err(e) => JsValue::from_str(&format!("Json deserialize error: {:?}", e)) - } +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(js_namespace = console)] + fn error(s: &str); +} + +macro_rules! console_error { + ($($t:tt)*) => (error(&format_args!($($t)*).to_string())) } fn into_serde_json(js_value: &JsValue) -> Result @@ -68,67 +55,55 @@ fn into_serde_json(js_value: &JsValue) -> Result } } -fn into_ref_value(js_value: &JsValue, node: Node) -> JsValue { - let result: result::Result = into_serde_json::(js_value); - match result { - Ok(json) => filter_ref_value(json.into(), node), - Err(e) => JsValue::from_str(&format!("Json serialize error: {}", e)) - } -} - -fn get_ref_value(js_value: JsValue, node: Node) -> JsValue { - match js_value.as_f64() { - Some(val) => { - match CACHE_JSON.lock().unwrap().get(&(val as usize)) { - Some(json) => filter_ref_value(json.clone(), node), - _ => JsValue::from_str("Invalid pointer") +fn replace_fun(v: &Value, fun: &js_sys::Function) -> Value { + match JsValue::from_serde(v) { + Ok(js_v) => { + match fun.call1(&JsValue::NULL, &js_v) { + Ok(result) => { + match into_serde_json(&result) { + Ok(json) => json, + Err(e) => { + console_error!("replace_with - closure returned a invalid JSON: {:?}", e); + Value::Null + } + } + } + Err(e) => { + console_error!("replace_with - fail to call closure: {:?}", e); + Value::Null + } } } - _ => into_ref_value(&js_value, node) - } -} - -lazy_static! { - static ref CACHE_JSON: Mutex> = Mutex::new(HashMap::new()); - static ref CACHE_JSON_IDX: Mutex = Mutex::new(0); -} - -#[wasm_bindgen(js_name = allocJson)] -pub extern fn alloc_json(js_value: JsValue) -> usize { - let result: result::Result = into_serde_json(&js_value); - match result { - Ok(json) => { - let mut map = CACHE_JSON.lock().unwrap(); - if map.len() >= std::u8::MAX as usize { - return 0; - } - - let mut idx = CACHE_JSON_IDX.lock().unwrap(); - *idx += 1; - map.insert(*idx, json.into()); - *idx - } Err(e) => { - console::error_1(&e.into()); - 0 + console_error!("replace_with - invalid JSON object: {:?}", e); + Value::Null } } } -#[wasm_bindgen(js_name = deallocJson)] -pub extern fn dealloc_json(ptr: usize) -> bool { - let mut map = CACHE_JSON.lock().unwrap(); - map.remove(&ptr).is_some() -} - #[wasm_bindgen] pub fn compile(path: &str) -> JsValue { - let mut parser = Parser::new(path); - let node = parser.compile(); + let node = Parser::compile(path); + let cb = Closure::wrap(Box::new(move |js_value: JsValue| { + let json = match into_serde_json(&js_value) { + Ok(json) => json, + Err(e) => return JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e))) + }; + + let mut selector = _Selector::new(); + match &node { - Ok(node) => get_ref_value(js_value, node.clone()), - Err(e) => JsValue::from_str(&format!("Json path error: {:?}", e)) + Ok(node) => selector.compiled_path(node), + Err(e) => return JsValue::from_str(&format!("{:?}", JsonPathError::Path(e.clone()))) + }; + + match selector.value(&json).select() { + Ok(ret) => match JsValue::from_serde(&ret) { + Ok(ret) => ret, + Err(e) => JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e.to_string()))) + }, + Err(e) => JsValue::from_str(&format!("{:?}", e)) } }) as Box JsValue>); @@ -139,26 +114,25 @@ pub fn compile(path: &str) -> JsValue { #[wasm_bindgen] pub fn selector(js_value: JsValue) -> JsValue { - let json = match js_value.as_f64() { - Some(val) => { - match CACHE_JSON.lock().unwrap().get(&(val as usize)) { - Some(json) => json.clone(), - _ => return JsValue::from_str("Invalid pointer") - } - } - _ => { - match into_serde_json::(&js_value) { - Ok(json) => json.into(), - Err(e) => return JsValue::from_str(e.as_str()) - } - } + let json: Value = match JsValue::into_serde(&js_value) { + Ok(json) => json, + Err(e) => return JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e.to_string()))) }; let cb = Closure::wrap(Box::new(move |path: String| { - let mut parser = Parser::new(path.as_str()); - match parser.compile() { - Ok(node) => filter_ref_value(json.clone(), node), - Err(e) => return JsValue::from_str(e.as_str()) + match Parser::compile(path.as_str()) { + Ok(node) => { + let mut selector = _Selector::new(); + let _ = selector.compiled_path(&node); + match selector.value(&json).select() { + Ok(ret) => match JsValue::from_serde(&ret) { + Ok(ret) => ret, + Err(e) => JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e.to_string()))) + }, + Err(e) => JsValue::from_str(&format!("{:?}", e)) + } + } + Err(e) => return JsValue::from_str(&format!("{:?}", JsonPathError::Path(e))) } }) as Box JsValue>); @@ -169,10 +143,108 @@ pub fn selector(js_value: JsValue) -> JsValue { #[wasm_bindgen] pub fn select(js_value: JsValue, path: &str) -> JsValue { - let mut parser = Parser::new(path); - match parser.compile() { - Ok(node) => get_ref_value(js_value, node), - Err(e) => return JsValue::from_str(e.as_str()) + let json = match into_serde_json(&js_value) { + Ok(json) => json, + Err(e) => return JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e))) + }; + + match jsonpath::select(&json, path) { + Ok(ret) => match JsValue::from_serde(&ret) { + Ok(ret) => ret, + Err(e) => JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e.to_string()))) + }, + Err(e) => JsValue::from_str(&format!("{:?}", e)) + } +} + +#[wasm_bindgen(catch, js_name = "deleteValue")] +pub fn delete(js_value: JsValue, path: &str) -> JsValue { + let json = match into_serde_json(&js_value) { + Ok(json) => json, + Err(e) => return JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e))) + }; + + match jsonpath::delete(json, path) { + Ok(ret) => { + match JsValue::from_serde(&ret) { + Ok(ret) => ret, + Err(e) => JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e.to_string()))) + } + } + Err(e) => JsValue::from_str(&format!("{:?}", e)) + } +} + +#[wasm_bindgen(catch, js_name = "replaceWith")] +pub fn replace_with(js_value: JsValue, path: &str, fun: js_sys::Function) -> JsValue { + let json = match into_serde_json(&js_value) { + Ok(json) => json, + Err(e) => return JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e))) + }; + + match jsonpath::replace_with(json, path, &mut |v| replace_fun(v, &fun)) { + Ok(ret) => match JsValue::from_serde(&ret) { + Ok(ret) => ret, + Err(e) => JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e.to_string()))) + }, + Err(e) => JsValue::from_str(&format!("{:?}", e)) + } +} + +/// +/// `wasm_bindgen` 제약으로 builder-pattern을 구사 할 수 없다. +/// lifetime 제약으로 Selector를 사용 할 수 없다. +/// +#[wasm_bindgen] +pub struct Selector { + path: Option, + value: Option, +} + +#[wasm_bindgen] +impl Selector { + #[wasm_bindgen(constructor)] + pub fn new() -> Self { + Selector { path: None, value: None } + } + + #[wasm_bindgen(catch)] + pub fn path(&mut self, path: &str) -> Result<(), JsValue> { + self.path = Some(path.to_string()); + Ok(()) + } + + #[wasm_bindgen(catch)] + pub fn value(&mut self, value: JsValue) -> Result<(), JsValue> { + let json = into_serde_json(&value) + .map_err(|e| JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e))))?; + self.value = Some(json); + Ok(()) + } + + #[wasm_bindgen(catch, js_name = select)] + pub fn select(&mut self) -> Result { + let mut selector = _Selector::new(); + + if let Some(path) = &self.path { + let _ = selector.str_path(&path).map_err(|e| JsValue::from_str(&format!("{:?}", e)))?; + } else { + return Err(JsValue::from_str(&format!("{:?}", JsonPathError::EmptyPath))); + } + + if let Some(value) = &self.value { + let _ = selector.value(value); + } else { + return Err(JsValue::from_str(&format!("{:?}", JsonPathError::EmptyValue))); + } + + match selector.select() { + Ok(ret) => match JsValue::from_serde(&ret) { + Ok(ret) => Ok(ret), + Err(e) => Err(JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e.to_string())))) + }, + Err(e) => Err(JsValue::from_str(&format!("{:?}", e))) + } } } @@ -180,104 +252,90 @@ pub fn select(js_value: JsValue, path: &str) -> JsValue { /// `wasm_bindgen` 제약으로 builder-pattern을 구사 할 수 없다. /// #[wasm_bindgen] -pub struct Selector { - selector: _Selector +pub struct SelectorMut { + path: Option, + value: Option, } #[wasm_bindgen] -impl Selector { +impl SelectorMut { #[wasm_bindgen(constructor)] pub fn new() -> Self { - Selector { selector: _Selector::new() } + SelectorMut { path: None, value: None } } #[wasm_bindgen(catch)] - pub fn path(&mut self, path: &str) -> result::Result<(), JsValue> { - let _ = self.selector.path(path)?; + pub fn path(&mut self, path: &str) -> Result<(), JsValue> { + self.path = Some(path.to_string()); Ok(()) } #[wasm_bindgen(catch)] - pub fn value(&mut self, value: JsValue) -> result::Result<(), JsValue> { - let ref ref_value = into_serde_json(&value)?; - let _ = self.selector.value(ref_value)?; + pub fn value(&mut self, value: JsValue) -> Result<(), JsValue> { + let json = into_serde_json(&value) + .map_err(|e| JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e))))?; + self.value = Some(json); Ok(()) } - #[wasm_bindgen(catch, js_name = selectToStr)] - pub fn select_to_str(&mut self) -> result::Result { - self.select_as_str() - } + #[wasm_bindgen(catch, js_name = "deleteValue")] + pub fn delete(&mut self) -> Result<(), JsValue> { + let mut selector = _SelectorMut::new(); - #[wasm_bindgen(catch, js_name = selectAsStr)] - pub fn select_as_str(&mut self) -> result::Result { - let json_str = self.selector.select_as_str()?; - Ok(JsValue::from_str(&json_str)) - } + if let Some(path) = &self.path { + let _ = selector.str_path(path); + } else { + return Err(JsValue::from_str(&format!("{:?}", JsonPathError::EmptyPath))); + }; - #[wasm_bindgen(catch, js_name = selectTo)] - pub fn select_to(&mut self) -> result::Result { - self.select_as() - } + if let Some(value) = self.value.take() { + selector.value(value); + } else { + return Err(JsValue::from_str(&format!("{:?}", JsonPathError::EmptyValue))); + }; - #[wasm_bindgen(catch, js_name = selectAs)] - pub fn select_as(&mut self) -> result::Result { - let ref_value = self.selector.select_as::() - .map_err(|e| JsValue::from_str(&e))?; - Ok(JsValue::from_serde(&ref_value) - .map_err(|e| JsValue::from_str(&e.to_string()))?) - } - - #[wasm_bindgen(catch)] - pub fn map(&mut self, func: JsValue) -> result::Result<(), JsValue> { - if !func.is_function() { - return Err(JsValue::from_str("Not a function argument")); - } - - let cb: &js_sys::Function = JsCast::unchecked_ref(func.as_ref()); - - self.selector.map(|v| { - let str_value = match JsValue::from_serde(&v) { - Ok(str_value) => str_value, - Err(e) => return { - console::error_1(&JsValue::from_str(&e.to_string())); - None - } - }; - - match cb.call1(&func, &str_value) { - Ok(ret) => { - match into_serde_json::(&ret) { - Ok(value) => Some(value), - Err(e) => { - console::error_1(&JsValue::from_str(&e.to_string())); - None - } - } - } - Err(e) => { - console::error_1(&e); - None - } + match selector.delete() { + Err(e) => Err(JsValue::from_str(&format!("{:?}", e))), + _ => { + self.value = selector.take(); + Ok(()) } - }).map_err(|e| JsValue::from_str(&e))?; + } + } - Ok(()) + #[wasm_bindgen(catch, js_name = replaceWith)] + pub fn replace_with(&mut self, fun: js_sys::Function) -> Result<(), JsValue> { + let mut selector = _SelectorMut::new(); + + if let Some(path) = &self.path { + let _ = selector.str_path(path); + } else { + return Err(JsValue::from_str(&format!("{:?}", JsonPathError::EmptyPath))); + }; + + if let Some(value) = self.value.take() { + selector.value(value); + } else { + return Err(JsValue::from_str(&format!("{:?}", JsonPathError::EmptyValue))); + }; + + match selector.replace_with(&mut |v| replace_fun(v, &fun)) { + Err(e) => Err(JsValue::from_str(&format!("{:?}", e))), + _ => { + self.value = selector.take(); + Ok(()) + } + } } #[wasm_bindgen(catch)] - pub fn get(&mut self) -> result::Result { - let v = self.selector.get().map_err(|e| JsValue::from_str(&e.to_string()))?; - JsValue::from_serde(&v).map_err(|e| JsValue::from_str(&e.to_string())) + pub fn take(&mut self) -> Result { + match self.value.take() { + Some(ret) => match JsValue::from_serde(&ret) { + Ok(ret) => Ok(ret), + Err(e) => Err(JsValue::from_str(&format!("{:?}", e))) + }, + None => Err(JsValue::from_str(&format!("{:?}", JsonPathError::EmptyValue))) + } } -} - -#[wasm_bindgen(catch)] -pub fn testa(js_value: JsValue, path: &str, iter: usize) -> result::Result<(), JsValue> { - for _ in 0..iter { - let mut parser = Parser::new(path); - let node = parser.compile().unwrap(); - into_ref_value(&js_value, node); - } - Ok(()) } \ No newline at end of file diff --git a/wasm/tests/test/index.spec.js b/wasm/tests/test/index.spec.js index f64d094..97d7dc7 100644 --- a/wasm/tests/test/index.spec.js +++ b/wasm/tests/test/index.spec.js @@ -1,365 +1,365 @@ const jsonpath = require('jsonpath-wasm'); let jsonObj = { - "store": { - "book": [ + 'store': { + 'book': [ { - "category": "reference", - "author": "Nigel Rees", - "title": "Sayings of the Century", - "price": 8.95 + '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': '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': '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 - } + '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 - } + 'bicycle': { + 'color': 'red', + 'price': 19.95, + }, }, - "expensive": 10 + 'expensive': 10, }; let list = { '$.store.book[*].author': [ - "Nigel Rees", - "Evelyn Waugh", - "Herman Melville", - "J. R. R. Tolkien" + 'Nigel Rees', + 'Evelyn Waugh', + 'Herman Melville', + 'J. R. R. Tolkien', ], - '$..author':[ - "Nigel Rees", - "Evelyn Waugh", - "Herman Melville", - "J. R. R. Tolkien" + '$..author': [ + 'Nigel Rees', + 'Evelyn Waugh', + 'Herman Melville', + 'J. R. R. Tolkien', ], '$.store.*': [ [ { - "category": "reference", - "author": "Nigel Rees", - "title": "Sayings of the Century", - "price": 8.95 + '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': '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': '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 - } + 'category': 'fiction', + 'author': 'J. R. R. Tolkien', + 'title': 'The Lord of the Rings', + 'isbn': '0-395-19395-8', + 'price': 22.99, + }, ], { - "color": "red", - "price": 19.95 - } + 'color': 'red', + 'price': 19.95, + }, ], - '$.store..price':[ + '$.store..price': [ 8.95, 12.99, 8.99, 22.99, - 19.95 + 19.95, ], '$..book[2]': [ { - "category": "fiction", - "author": "Herman Melville", - "title": "Moby Dick", - "isbn": "0-553-21311-3", - "price": 8.99 - } + 'category': 'fiction', + 'author': 'Herman Melville', + 'title': 'Moby Dick', + 'isbn': '0-553-21311-3', + 'price': 8.99, + }, ], '$..book[-2]': [ { - "category": "fiction", - "author": "Herman Melville", - "title": "Moby Dick", - "isbn": "0-553-21311-3", - "price": 8.99 - } + 'category': 'fiction', + 'author': 'Herman Melville', + 'title': 'Moby Dick', + 'isbn': '0-553-21311-3', + 'price': 8.99, + }, ], '$..book[0,1]': [ { - "category": "reference", - "author": "Nigel Rees", - "title": "Sayings of the Century", - "price": 8.95 + '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': 'Evelyn Waugh', + 'title': 'Sword of Honour', + 'price': 12.99, + }, ], '$..book[:2]': [ { - "category": "reference", - "author": "Nigel Rees", - "title": "Sayings of the Century", - "price": 8.95 + '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': 'Evelyn Waugh', + 'title': 'Sword of Honour', + 'price': 12.99, + }, ], '$..book[1:2]': [ { - "category": "fiction", - "author": "Evelyn Waugh", - "title": "Sword of Honour", - "price": 12.99 - } + 'category': 'fiction', + 'author': 'Evelyn Waugh', + 'title': 'Sword of Honour', + 'price': 12.99, + }, ], '$..book[-2:]': [ { - "category": "fiction", - "author": "Herman Melville", - "title": "Moby Dick", - "isbn": "0-553-21311-3", - "price": 8.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 - } + 'category': 'fiction', + 'author': 'J. R. R. Tolkien', + 'title': 'The Lord of the Rings', + 'isbn': '0-395-19395-8', + 'price': 22.99, + }, ], '$..book[2:]': [ { - "category": "fiction", - "author": "Herman Melville", - "title": "Moby Dick", - "isbn": "0-553-21311-3", - "price": 8.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 - } + 'category': 'fiction', + 'author': 'J. R. R. Tolkien', + 'title': 'The Lord of the Rings', + 'isbn': '0-395-19395-8', + 'price': 22.99, + }, ], '$..book[?(@.isbn)]': [ { - "category": "fiction", - "author": "Herman Melville", - "title": "Moby Dick", - "isbn": "0-553-21311-3", - "price": 8.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 - } + 'category': 'fiction', + 'author': 'J. R. R. Tolkien', + 'title': 'The Lord of the Rings', + 'isbn': '0-395-19395-8', + 'price': 22.99, + }, ], '$.store.book[?(@.price < 10)]': [ { - "category": "reference", - "author": "Nigel Rees", - "title": "Sayings of the Century", - "price": 8.95 + 'category': 'reference', + 'author': 'Nigel Rees', + 'title': 'Sayings of the Century', + 'price': 8.95, }, { - "category": "fiction", - "author": "Herman Melville", - "title": "Moby Dick", - "isbn": "0-553-21311-3", - "price": 8.99 - } + 'category': 'fiction', + 'author': 'Herman Melville', + 'title': 'Moby Dick', + 'isbn': '0-553-21311-3', + 'price': 8.99, + }, ], '$..*': [ { - "book": [ + 'book': [ { - "category": "reference", - "author": "Nigel Rees", - "title": "Sayings of the Century", - "price": 8.95 + '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': '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': '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 - } + '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 - } + 'bicycle': { + 'color': 'red', + 'price': 19.95, + }, }, 10, [ { - "category": "reference", - "author": "Nigel Rees", - "title": "Sayings of the Century", - "price": 8.95 + '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': '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': '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 - } + 'category': 'fiction', + 'author': 'J. R. R. Tolkien', + 'title': 'The Lord of the Rings', + 'isbn': '0-395-19395-8', + 'price': 22.99, + }, ], { - "color": "red", - "price": 19.95 + 'color': 'red', + 'price': 19.95, }, { - "category": "reference", - "author": "Nigel Rees", - "title": "Sayings of the Century", - "price": 8.95 + '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': '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': '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 + 'category': 'fiction', + 'author': 'J. R. R. Tolkien', + 'title': 'The Lord of the Rings', + 'isbn': '0-395-19395-8', + 'price': 22.99, }, - "reference", - "Nigel Rees", - "Sayings of the Century", + 'reference', + 'Nigel Rees', + 'Sayings of the Century', 8.95, - "fiction", - "Evelyn Waugh", - "Sword of Honour", + 'fiction', + 'Evelyn Waugh', + 'Sword of Honour', 12.99, - "fiction", - "Herman Melville", - "Moby Dick", - "0-553-21311-3", + 'fiction', + 'Herman Melville', + 'Moby Dick', + '0-553-21311-3', 8.99, - "fiction", - "J. R. R. Tolkien", - "The Lord of the Rings", - "0-395-19395-8", + 'fiction', + 'J. R. R. Tolkien', + 'The Lord of the Rings', + '0-395-19395-8', 22.99, - "red", - 19.95 + 'red', + 19.95, ], '$..book[ ?( (@.price < 13 || $.store.bicycle.price < @.price) && @.price <=10 ) ]': [ { - "category": "reference", - "author": "Nigel Rees", - "title": "Sayings of the Century", - "price": 8.95 + 'category': 'reference', + 'author': 'Nigel Rees', + 'title': 'Sayings of the Century', + 'price': 8.95, }, { - "category": "fiction", - "author": "Herman Melville", - "title": "Moby Dick", - "isbn": "0-553-21311-3", - "price": 8.99 - } - ] + 'category': 'fiction', + 'author': 'Herman Melville', + 'title': 'Moby Dick', + 'isbn': '0-553-21311-3', + 'price': 8.99, + }, + ], }; describe('compile test', () => { it('basic', (done) => { let template = jsonpath.compile('$.a'); let result = template({'a': 1}); - if (result === 1) { + if (result[0] === 1) { done(); } }); @@ -369,7 +369,7 @@ describe('selector test', () => { it('basic', (done) => { let selector = jsonpath.selector({'a': 1}); let result = selector('$.a'); - if (result === 1) { + if (result[0] === 1) { done(); } }); @@ -378,7 +378,7 @@ describe('selector test', () => { describe('select test', () => { it('basic', (done) => { let result = jsonpath.select({'a': 1}, '$.a'); - if (result === 1) { + if (result[0] === 1) { done(); } }); @@ -393,54 +393,132 @@ describe('filter test', () => { } } - for( var i in list ) { + for (var i in list) { it(i, (done) => { - run (done, i, list[i]); - }) + run(done, i, list[i]); + }); } it('object equal', (done) => { let selector = new jsonpath.Selector(); selector.path('$..[?(@.a == 1)]'); selector.value({ - "a": 1, - "b" : {"a": 1}, - "c" : {"a": 1} + 'a': 1, + 'b': {'a': 1}, + 'c': {'a': 1}, }); - let result = selector.selectAs(); - if (JSON.stringify(result) === JSON.stringify([ {"a": 1}, {"a": 1} ])) { + let result = selector.select(); + if (JSON.stringify(result) === JSON.stringify([{'a': 1}, {'a': 1}])) { + done(); + } + }); +}); + +describe('SelectorMut test', () => { + it('delete', (done) => { + let jsonObjNew = JSON.parse(JSON.stringify(jsonObj)); + let result = jsonpath.deleteValue(jsonObjNew, '$.store.book'); + if (JSON.stringify(result) === JSON.stringify({ + 'store': { + 'book': null, + 'bicycle': { + 'color': 'red', + 'price': 19.95, + }, + }, + 'expensive': 10, + })) { + done(); + } + }); + + it('replaceWith', (done) => { + let jsonObjNew = JSON.parse(JSON.stringify(jsonObj)); + let result = jsonpath.replaceWith(jsonObjNew, '$.store.book', (v) => { + let ret = v[0]; + ret.price = 9; + return ret; + }); + if (JSON.stringify(result) === JSON.stringify({ + 'store': { + 'book': { + 'category': 'reference', + 'author': 'Nigel Rees', + 'title': 'Sayings of the Century', + 'price': 9, + }, + 'bicycle': { + 'color': 'red', + 'price': 19.95, + }, + }, + 'expensive': 10, + })) { + done(); + } + }); + + it('SeletorMut delete', (done) => { + let jsonObjNew = JSON.parse(JSON.stringify(jsonObj)); + let selector = new jsonpath.SelectorMut(); + selector.path('$.store.book'); + selector.value(jsonObjNew); + selector.deleteValue(); + + let result = selector.take(); + if (JSON.stringify(result) === JSON.stringify({ + 'store': { + 'book': null, + 'bicycle': { + 'color': 'red', + 'price': 19.95, + }, + }, + 'expensive': 10, + })) { + done(); + } + }); + + it('SeletorMut replaceWith', (done) => { + let jsonObjNew = JSON.parse(JSON.stringify(jsonObj)); + let selector = new jsonpath.SelectorMut(); + selector.path('$.store.book'); + selector.value(jsonObjNew); + selector.replaceWith((v) => { + let ret = v[0]; + ret.price = 9; + return ret; + }); + + let result = selector.take(); + if (JSON.stringify(result) === JSON.stringify({ + 'store': { + 'book': { + 'category': 'reference', + 'author': 'Nigel Rees', + 'title': 'Sayings of the Century', + 'price': 9, + }, + 'bicycle': { + 'color': 'red', + 'price': 19.95, + }, + }, + 'expensive': 10, + })) { done(); } }); }); describe('Selector test', () => { - it('basic selectTo', (done) => { - let selector = new jsonpath.Selector(); - selector.path('$.a'); - selector.value({'a': 1}); - let result = selector.selectAs(); - if (result === 1) { - done(); - } - }); - - it('basic selectToStr', (done) => { - let selector = new jsonpath.Selector(); - selector.path('$.a'); - selector.value({'a': 1}); - let result = selector.selectToStr(); - if (result === '1') { - done(); - } - }); - it('select', (done) => { let selector = new jsonpath.Selector(); selector.value(jsonObj); - for(var i in list) { + for (var i in list) { selector.path(i); - if(JSON.stringify(list[i]) !== selector.selectAsStr()) { + if (JSON.stringify(list[i]) !== JSON.stringify(selector.select())) { throw `fail: ${i}`; } } @@ -451,16 +529,16 @@ describe('Selector test', () => { describe('README test', () => { it('jsonpath.Selector', (done) => { let jsonObj = { - "school": { - "friends": [ - {"name": "친구1", "age": 20}, - {"name": "친구2", "age": 20} - ] + 'school': { + 'friends': [ + {'name': '친구1', 'age': 20}, + {'name': '친구2', 'age': 20}, + ], }, - "friends": [ - {"name": "친구3", "age": 30}, - {"name": "친구4"} - ] + 'friends': [ + {'name': '친구3', 'age': 30}, + {'name': '친구4'}, + ], }; let selector = new jsonpath.Selector(); @@ -468,50 +546,87 @@ describe('README test', () => { { selector.path('$..[?(@.age >= 30)]'); - let jsonObj = selector.selectAs(); - let resultObj = [{"name": "친구3", "age": 30}]; - if(JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) { + let jsonObj = selector.select(); + let resultObj = [{'name': '친구3', 'age': 30}]; + if (JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) { throw 'jsonpath.Selector: $..[?(@.age >= 30)]'; } } { selector.path('$..[?(@.age == 20)]'); - let jsonObj = selector.selectAs(); - let resultObj = [{"name": "친구1", "age": 20}, {"name": "친구2", "age": 20}]; - if(JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) { + let jsonObj = selector.select(); + let resultObj = [{'name': '친구1', 'age': 20}, {'name': '친구2', 'age': 20}]; + if (JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) { throw 'jsonpath.Selector: $..[?(@.age >= 20)]'; } } { - selector.value({"friends": [ {"name": "친구5", "age": 20} ]}); - let jsonObj = selector.selectAs(); - let resultObj = [{"name": "친구5", "age": 20}]; - if(JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) { + selector.value({'friends': [{'name': '친구5', 'age': 20}]}); + let jsonObj = selector.select(); + let resultObj = [{'name': '친구5', 'age': 20}]; + if (JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) { throw 'jsonpath.Selector: change value'; } } + done(); + }); + + it('jsonpath.SelectorMut', (done) => { + let jsonObj = { + 'school': { + 'friends': [ + {'name': '친구1', 'age': 20}, + {'name': '친구2', 'age': 20}, + ], + }, + 'friends': [ + {'name': '친구3', 'age': 30}, + {'name': '친구4'}, + ], + }; + + let selector = new jsonpath.SelectorMut(); + selector.path('$..[?(@.age == 20)]'); + { + selector.value(jsonObj); + selector.deleteValue(); + + let resultObj = { + 'school': {'friends': [null, null]}, + 'friends': [ + {'name': '친구3', 'age': 30}, + {'name': '친구4'}, + ], + }; + if (JSON.stringify(selector.take()) !== JSON.stringify(resultObj)) { + throw 'jsonpath.SelectorMut.deleteValue'; + } + } + { selector.value(jsonObj); - selector.map(function(v) { - let f1 = v[0]; - f1.age = 30; + selector.replaceWith((v) => { + v.age = v.age * 2; return v; - }) - let jsonObj1 = selector.get(); + }); - let resultObj1 = [{"name": "친구1", "age": 30}, {"name": "친구2", "age": 20}]; - if(JSON.stringify(jsonObj1) !== JSON.stringify(resultObj1)) { - throw 'jsonpath.Selector.map'; - } - - selector.path('$..[?(@.age == 20)]'); - let jsonObj2 = selector.selectAs(); - let resultObj2 = [{"name": "친구2", "age": 20}]; - if(JSON.stringify(jsonObj2) !== JSON.stringify(resultObj2)) { - throw 'jsonpath.Selector.map and then select'; + let resultObj = { + 'school': { + 'friends': [ + {'name': '친구1', 'age': 40}, + {'name': '친구2', 'age': 40}, + ], + }, + 'friends': [ + {'name': '친구3', 'age': 30}, + {'name': '친구4'}, + ], + }; + if (JSON.stringify(selector.take()) !== JSON.stringify(resultObj)) { + throw 'jsonpath.SelectorMut.replaceWith'; } } @@ -520,28 +635,27 @@ describe('README test', () => { it('jsonpath.select(json: string|object, jsonpath: string)', (done) => { let jsonObj = { - "school": { - "friends": [ - {"name": "친구1", "age": 20}, - {"name": "친구2", "age": 20} - ] + 'school': { + 'friends': [ + {'name': '친구1', 'age': 20}, + {'name': '친구2', 'age': 20}, + ], }, - "friends": [ - {"name": "친구3", "age": 30}, - {"name": "친구4"} - ] + 'friends': [ + {'name': '친구3', 'age': 30}, + {'name': '친구4'}, + ], }; let ret = [ - {"name": "친구3", "age": 30}, - {"name": "친구1", "age": 20} + {'name': '친구3', 'age': 30}, + {'name': '친구1', 'age': 20}, ]; - let selectAsString = jsonpath.select(JSON.stringify(jsonObj), '$..friends[0]'); let selectAsObj = jsonpath.select(jsonObj, '$..friends[0]'); - if( + if ( JSON.stringify(ret) !== JSON.stringify(selectAsString) || JSON.stringify(ret) !== JSON.stringify(selectAsObj) ) { @@ -555,27 +669,27 @@ describe('README test', () => { let template = jsonpath.compile('$..friends[0]'); let jsonObj = { - "school": { - "friends": [ - {"name": "친구1", "age": 20}, - {"name": "친구2", "age": 20} - ] + 'school': { + 'friends': [ + {'name': '친구1', 'age': 20}, + {'name': '친구2', 'age': 20}, + ], }, - "friends": [ - {"name": "친구3", "age": 30}, - {"name": "친구4"} - ] + 'friends': [ + {'name': '친구3', 'age': 30}, + {'name': '친구4'}, + ], }; let ret = [ - {"name": "친구3", "age": 30}, - {"name": "친구1", "age": 20} + {'name': '친구3', 'age': 30}, + {'name': '친구1', 'age': 20}, ]; let selectAsString = template(JSON.stringify(jsonObj)); let selectAsObj = template(jsonObj); - if( + if ( JSON.stringify(ret) !== JSON.stringify(selectAsString) || JSON.stringify(ret) !== JSON.stringify(selectAsObj) ) { @@ -583,24 +697,24 @@ describe('README test', () => { } let jsonObj2 = { - "school": { - "friends": [ - {"name": "Millicent Norman"}, - {"name": "Vincent Cannon"} - ] + 'school': { + 'friends': [ + {'name': 'Millicent Norman'}, + {'name': 'Vincent Cannon'}, + ], }, - "friends": [ {"age": 30}, {"age": 40} ] + 'friends': [{'age': 30}, {'age': 40}], }; let ret2 = [ - {"age": 30}, - {"name": "Millicent Norman"} + {'age': 30}, + {'name': 'Millicent Norman'}, ]; let selectAsString2 = template(JSON.stringify(jsonObj2)); let selectAsObj2 = template(jsonObj2); - if( + if ( JSON.stringify(ret2) !== JSON.stringify(selectAsString2) || JSON.stringify(ret2) !== JSON.stringify(selectAsObj2) ) { @@ -612,33 +726,33 @@ describe('README test', () => { it('jsonpath.selector(json: string|object)', (done) => { let jsonObj = { - "school": { - "friends": [ - {"name": "친구1", "age": 20}, - {"name": "친구2", "age": 20} - ] + 'school': { + 'friends': [ + {'name': '친구1', 'age': 20}, + {'name': '친구2', 'age': 20}, + ], }, - "friends": [ - {"name": "친구3", "age": 30}, - {"name": "친구4"} - ] + 'friends': [ + {'name': '친구3', 'age': 30}, + {'name': '친구4'}, + ], }; let ret1 = [ - {"name": "친구3", "age": 30}, - {"name": "친구1", "age": 20} + {'name': '친구3', 'age': 30}, + {'name': '친구1', 'age': 20}, ]; let ret2 = [ - {"name": "친구4"}, - {"name": "친구2", "age": 20} + {'name': '친구4'}, + {'name': '친구2', 'age': 20}, ]; let selector = jsonpath.selector(jsonObj); let select1 = selector('$..friends[0]'); let select2 = selector('$..friends[1]'); - if( + if ( JSON.stringify(ret1) !== JSON.stringify(select1) || JSON.stringify(ret2) !== JSON.stringify(select2) ) { @@ -647,4 +761,64 @@ describe('README test', () => { done(); }); + + it('jsonpath.deleteValue(json: string|object, path: string)', (done) => { + let jsonObj = { + 'school': { + 'friends': [ + {'name': '친구1', 'age': 20}, + {'name': '친구2', 'age': 20}, + ], + }, + 'friends': [ + {'name': '친구3', 'age': 30}, + {'name': '친구4'}, + ], + }; + + let _1 = jsonpath.deleteValue(jsonObj, '$..friends[0]'); + let result = jsonpath.deleteValue(_1, '$..friends[1]'); + + if (JSON.stringify(result) === JSON.stringify({ + 'school': {'friends': [null, null]}, + 'friends': [null, null], + })) { + done(); + } + }); + + it('jsonpath.replaceWith(json: string|object, path: string, fun: function(json: object) => json: object', (done) => { + let jsonObj = { + 'school': { + 'friends': [ + {'name': '친구1', 'age': 20}, + {'name': '친구2', 'age': 20}, + ], + }, + 'friends': [ + {'name': '친구3', 'age': 30}, + {'name': '친구4'}, + ], + }; + + let result = jsonpath.replaceWith(jsonObj, '$..friends[0]', (v) => { + v.age = v.age * 2; + return v; + }); + + if (JSON.stringify(result) === JSON.stringify({ + 'school': { + 'friends': [ + {'name': '친구1', 'age': 40}, + {'name': '친구2', 'age': 20}, + ], + }, + 'friends': [ + {'name': '친구3', 'age': 60}, + {'name': '친구4'}, + ], + })) { + done(); + } + }); }); \ No newline at end of file diff --git a/wasm/tests/web.rs b/wasm/tests/web.rs index 0dac354..a8ee5df 100644 --- a/wasm/tests/web.rs +++ b/wasm/tests/web.rs @@ -95,20 +95,6 @@ fn selector() { assert_eq!(json, target_json()); } -#[wasm_bindgen_test] -fn alloc_dealloc_json() { - let ptr = jsonpath::alloc_json(JsValue::from_str(json_str())); - assert_eq!(ptr > 0, true); - - let json: Value = jsonpath::select(JsValue::from_f64(ptr as f64), "$..book[2]").into_serde().unwrap(); - assert_eq!(json, target_json()); - - assert_eq!(jsonpath::dealloc_json(ptr), true); - - let err = jsonpath::select(JsValue::from_f64(ptr as f64), "$..book[2]").as_string().unwrap(); - assert_eq!(err, "Invalid pointer".to_string()); -} - #[wasm_bindgen_test] fn selector_struct() { let mut selector = jsonpath::Selector::new(); diff --git a/wasm/www_bench/index.js b/wasm/www_bench/index.js index 89810ed..62e69a6 100644 --- a/wasm/www_bench/index.js +++ b/wasm/www_bench/index.js @@ -61,9 +61,6 @@ let path = '$..book[?(@.price<30 && @.category=="fiction")]'; let template = jpw.compile(path); let selector = jpw.selector(json); -let ptr = jpw.allocJson(json); -if(ptr == 0) console.error('invalid ptr'); - let iterCount = 2000; run('jsonpath', iterCount, function() { jp.query(json, path) }) @@ -73,17 +70,7 @@ run('jsonpath', iterCount, function() { jp.query(json, path) }) .then(function() { return run('jsonpath-wasm- compile', iterCount, function() { template(json) }); }) - .then(function() { - return run('jsonpath-wasm- compile-alloc', iterCount, function() { template(ptr) }); - }) .then(function() { return run('jsonpath-wasm- select', iterCount, function() { jpw.select(json, path) }); }) - .then(function() { - return run('jsonpath-wasm- select-alloc', iterCount, function() { jpw.select(ptr, path) }); - }) - .finally(function() { - if(!jpw.deallocJson(ptr)) { - console.error('fail to dealloc'); - } - }); + .finally(function() {});