mirror of
https://github.com/fluencelabs/jsonpath
synced 2025-04-25 09:22:19 +00:00
serde_json::Value clone 제거
This commit is contained in:
parent
ba57ae0ea5
commit
1a3104c5db
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "jsonpath_lib"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
authors = ["Changseok Han <freestrings@gmail.com>"]
|
||||
|
||||
description = "JsonPath in Rust and Webassembly - Webassembly Demo: https://freestrings.github.io/jsonpath"
|
||||
@ -28,3 +28,7 @@ bencher = "0.1.5"
|
||||
[lib]
|
||||
name = "jsonpath_lib"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
||||
lto = false
|
||||
|
183
README.md
183
README.md
@ -1,7 +1,7 @@
|
||||
# jsonpath-lib
|
||||
|
||||
[](https://travis-ci.org/freestrings/jsonpath)
|
||||
[](https://crates.io/crates/jsonpath_lib)
|
||||

|
||||
|
||||
`Rust` 버전 [JsonPath](https://goessner.net/articles/JsonPath/) 구현이다. Rust 구현과 동일한 기능을 `Webassembly` 로 제공하는 것도 목표.
|
||||
|
||||
@ -16,17 +16,18 @@ To enjoy Rust!
|
||||
[With Javascript (Webassembly)](#with-javascript-webassembly)
|
||||
|
||||
- [jsonpath-wasm library](#jsonpath-wasm-library)
|
||||
- [javascript - jsonpath.read(json: string|object, jsonpath: string)](#javascript---jsonpathreadjson-stringobject-jsonpath-string)
|
||||
- [javascript - jsonpath.select(json: string|object, jsonpath: string)](#javascript---jsonpathselectjson-stringobject-jsonpath-string)
|
||||
- [javascript - jsonpath.compile(jsonpath: string)](#javascript---jsonpathcompilejsonpath-string)
|
||||
- [javascript - jsonpath.reader(json: string|object)](#javascript---jsonpathreaderjson-stringobject)
|
||||
- [javascript - jsonpath.selector(json: string|object)](#javascript---jsonpathselectorjson-stringobject)
|
||||
- [javascript - alloc_json, dealloc_json](#javascript---alloc_json-dealloc_json)
|
||||
- [javascript - examples](#javascript---examples)
|
||||
|
||||
[With Rust (as library)](#with-rust-as-library)
|
||||
|
||||
- [jsonpath_lib library](#jsonpath_lib-library)
|
||||
- [rust - jsonpath::read(json: serde_json::value::Value, jsonpath: &str)](#rust---jsonpathreadjson-serde_jsonvaluevalue-jsonpath-str)
|
||||
- [rust - jsonpath::select(json: serde_json::value::Value, jsonpath: &str)](#rust---jsonpathselectjson-serde_jsonvaluevalue-jsonpath-str)
|
||||
- [rust - jsonpath::compile(jsonpath: &str)](#rust---jsonpathcompilejsonpath-str)
|
||||
- [rust - jsonpath::reader(json: serde_json::value::Value)](#rust---jsonpathreaderjson-serde_jsonvaluevalue)
|
||||
- [rust - jsonpath::selector(json: serde_json::value::Value)](#rust---jsonpathselectorjson-serde_jsonvaluevalue)
|
||||
- [rust - examples](#rust---examples)
|
||||
|
||||
[With AWS API Gateway](#with-aws-api-gateway)
|
||||
@ -45,7 +46,7 @@ import * as jsonpath from "jsonpath-wasm";
|
||||
let jsonpath = require('jsonpath-wasm');
|
||||
```
|
||||
|
||||
### javascript - jsonpath.read(json: string|object, jsonpath: string)
|
||||
### javascript - jsonpath.select(json: string|object, jsonpath: string)
|
||||
|
||||
```javascript
|
||||
let jsonObj = {
|
||||
@ -56,8 +57,8 @@ let jsonObj = {
|
||||
};
|
||||
let ret = [{"id": 0}, {"id": 0}];
|
||||
|
||||
let a = jsonpath.read(JSON.stringify(jsonObj), "$..friends[0]");
|
||||
let b = jsonpath.read(jsonObj, "$..friends[0]");
|
||||
let a = jsonpath.select(JSON.stringify(jsonObj), "$..friends[0]");
|
||||
let b = jsonpath.select(jsonObj, "$..friends[0]");
|
||||
console.log(
|
||||
JSON.stringify(ret) == JSON.stringify(a),
|
||||
JSON.stringify(a) == JSON.stringify(b)
|
||||
@ -101,7 +102,7 @@ console.log(JSON.stringify(template(jsonObj2)) == ret2);
|
||||
console.log(JSON.stringify(template(JSON.stringify(jsonObj2))) == ret2);
|
||||
```
|
||||
|
||||
### javascript - jsonpath.reader(json: string|object)
|
||||
### javascript - jsonpath.selector(json: string|object)
|
||||
|
||||
```javascript
|
||||
let jsonObj = {
|
||||
@ -115,14 +116,55 @@ let ret1 = JSON.stringify([ {"id": 0}, {"id": 0} ]);
|
||||
let ret2 = JSON.stringify([ {"id": 1}, {"id": 1} ]);
|
||||
|
||||
// 1. read as json object
|
||||
let reader = jsonpath.reader(jsonObj);
|
||||
console.log(JSON.stringify(reader("$..friends[0]")) == ret1);
|
||||
console.log(JSON.stringify(reader("$..friends[1]")) == ret2);
|
||||
let selector = jsonpath.selector(jsonObj);
|
||||
console.log(JSON.stringify(selector("$..friends[0]")) == ret1);
|
||||
console.log(JSON.stringify(selector("$..friends[1]")) == ret2);
|
||||
|
||||
// 2. read as json string
|
||||
let reader2 = jsonpath.reader(JSON.stringify(jsonObj));
|
||||
console.log(JSON.stringify(reader2("$..friends[0]")) == ret1);
|
||||
console.log(JSON.stringify(reader2("$..friends[1]")) == ret2);
|
||||
let selector = jsonpath.selector(JSON.stringify(jsonObj));
|
||||
console.log(JSON.stringify(selector("$..friends[0]")) == ret1);
|
||||
console.log(JSON.stringify(selector("$..friends[1]")) == ret2);
|
||||
```
|
||||
|
||||
### javascript - alloc_json, dealloc_json
|
||||
|
||||
wasm-bindgen은 Javascript와 Webassembly 간 값을 주고받을 때 JSON 객체는 String으로 변환되기 때문에, 반복해서 사용되는 JSON 객체를 Webassembly 영역에 생성해 두면 성능에 도움이 된다.
|
||||
|
||||
Since wasm-bindgen converts JSON objects to String when exchanging values between Javascript and Webassembly, it is helpful to create repeated Json objects in Webassembly area.
|
||||
|
||||
```javascript
|
||||
|
||||
let jsonObj = {
|
||||
"school": {
|
||||
"friends": [{"id": 0}, {"id": 1}]
|
||||
},
|
||||
"friends": [{"id": 0},{"id": 1}]
|
||||
};
|
||||
|
||||
let path = '$..friends[0]';
|
||||
let template = jsonpath.compile(path);
|
||||
let selector = jsonpath.selector(jsonObj);
|
||||
|
||||
let ptr = jsonpath.alloc_json(jsonObj);
|
||||
if(ptr == 0) console.error('invalid ptr'); // `0` is invalid pointer
|
||||
let selector2 = jsonpath.selector(ptr);
|
||||
|
||||
let ret1 = selector(path)
|
||||
let ret2 = selector2(path)
|
||||
let ret3 = template(jsonObj);
|
||||
let ret4 = template(ptr);
|
||||
let ret5 = jsonpath.select(jsonObj, path);
|
||||
let ret6 = jsonpath.select(ptr, path);
|
||||
|
||||
console.log(
|
||||
JSON.stringify(ret1) == JSON.stringify(ret2),// true
|
||||
JSON.stringify(ret1) == JSON.stringify(ret3),// true
|
||||
JSON.stringify(ret1) == JSON.stringify(ret4),// true
|
||||
JSON.stringify(ret1) == JSON.stringify(ret5),// true
|
||||
JSON.stringify(ret1) == JSON.stringify(ret6));// true
|
||||
|
||||
jsonpath.dealloc_json(ptr);
|
||||
|
||||
```
|
||||
|
||||
### javascript - examples
|
||||
@ -201,7 +243,7 @@ extern crate jsonpath_lib as jsonpath;
|
||||
extern crate serde_json;
|
||||
```
|
||||
|
||||
### rust - jsonpath::read(json: serde_json::value::Value, jsonpath: &str)
|
||||
### rust - jsonpath::select(json: serde_json::value::Value, jsonpath: &str)
|
||||
|
||||
```rust
|
||||
let json_obj = json!({
|
||||
@ -210,7 +252,7 @@ let json_obj = json!({
|
||||
},
|
||||
"friends": [{"id": 0}, {"id": 1}]
|
||||
});
|
||||
let json = jsonpath::read(json_obj, "$..friends[0]").unwrap();
|
||||
let json = jsonpath::select(json_obj, "$..friends[0]").unwrap();
|
||||
let ret = json!([ {"id": 0}, {"id": 0} ]);
|
||||
assert_eq!(json, ret)
|
||||
```
|
||||
@ -243,7 +285,7 @@ let ret = json!([ {"id": 0}, {"name": "Millicent Norman"} ]);
|
||||
assert_eq!(json, ret);
|
||||
```
|
||||
|
||||
### rust - jsonpath::reader(json: serde_json::value::Value)
|
||||
### rust - jsonpath::selector(json: serde_json::value::Value)
|
||||
|
||||
```rust
|
||||
let json_obj = json!({
|
||||
@ -253,13 +295,13 @@ let json_obj = json!({
|
||||
"friends": [{"id": 0},{"id": 1}]
|
||||
});
|
||||
|
||||
let mut reader = jsonpath::reader(json_obj);
|
||||
let mut selector = jsonpath::selector(json_obj);
|
||||
|
||||
let json = reader("$..friends[0]").unwrap();
|
||||
let json = selector("$..friends[0]").unwrap();
|
||||
let ret = json!([ {"id": 0}, {"id": 0} ]);
|
||||
assert_eq!(json, ret);
|
||||
|
||||
let json = reader("$..friends[1]").unwrap();
|
||||
let json = selector("$..friends[1]").unwrap();
|
||||
let ret = json!([ {"id": 1}, {"id": 1} ]);
|
||||
assert_eq!(json, ret);
|
||||
```
|
||||
@ -305,13 +347,13 @@ let json_obj = json!({
|
||||
"expensive": 10
|
||||
});
|
||||
|
||||
let mut reader = jsonpath::reader(json_obj);
|
||||
let mut selector = jsonpath::selector(json_obj);
|
||||
|
||||
```
|
||||
|
||||
#### $.store.book[*].author
|
||||
```rust
|
||||
let json = reader("$.store.book[*].author").unwrap();
|
||||
let json = selector("$.store.book[*].author").unwrap();
|
||||
let ret = json!([
|
||||
"Nigel Rees",
|
||||
"Evelyn Waugh",
|
||||
@ -323,7 +365,7 @@ assert_eq!(json, ret);
|
||||
|
||||
#### $..author
|
||||
```rust
|
||||
let json = reader("$..author").unwrap();
|
||||
let json = selector("$..author").unwrap();
|
||||
let ret = json!([
|
||||
"Nigel Rees",
|
||||
"Evelyn Waugh",
|
||||
@ -335,7 +377,7 @@ assert_eq!(json, ret);
|
||||
|
||||
#### $.store.*
|
||||
```rust
|
||||
let json = reader("$.store.*").unwrap();
|
||||
let json = selector("$.store.*").unwrap();
|
||||
let ret = json!([
|
||||
[
|
||||
{
|
||||
@ -375,14 +417,14 @@ assert_eq!(ret, json);
|
||||
|
||||
#### $.store..price
|
||||
```rust
|
||||
let json = reader("$.store..price").unwrap();
|
||||
let json = selector("$.store..price").unwrap();
|
||||
let ret = json!([8.95, 12.99, 8.99, 22.99, 19.95]);
|
||||
assert_eq!(ret, json);
|
||||
```
|
||||
|
||||
#### $..book[2]
|
||||
```rust
|
||||
let json = reader("$..book[2]").unwrap();
|
||||
let json = selector("$..book[2]").unwrap();
|
||||
let ret = json!([{
|
||||
"category" : "fiction",
|
||||
"author" : "Herman Melville",
|
||||
@ -395,7 +437,7 @@ assert_eq!(ret, json);
|
||||
|
||||
#### $..book[-2]
|
||||
```rust
|
||||
let json = reader("$..book[-2]").unwrap();
|
||||
let json = selector("$..book[-2]").unwrap();
|
||||
let ret = json!([{
|
||||
"category" : "fiction",
|
||||
"author" : "Herman Melville",
|
||||
@ -408,7 +450,7 @@ assert_eq!(ret, json);
|
||||
|
||||
#### $..book[0,1]
|
||||
```rust
|
||||
let json = reader("$..book[0,1]").unwrap();
|
||||
let json = selector("$..book[0,1]").unwrap();
|
||||
let ret = json!([
|
||||
{
|
||||
"category": "reference",
|
||||
@ -428,7 +470,7 @@ assert_eq!(ret, json);
|
||||
|
||||
#### $..book[:2]
|
||||
```rust
|
||||
let json = reader("$..book[:2]").unwrap();
|
||||
let json = selector("$..book[:2]").unwrap();
|
||||
let ret = json!([
|
||||
{
|
||||
"category": "reference",
|
||||
@ -448,7 +490,7 @@ assert_eq!(ret, json);
|
||||
|
||||
#### $..book[2:]
|
||||
```rust
|
||||
let json = reader("$..book[2:]").unwrap();
|
||||
let json = selector("$..book[2:]").unwrap();
|
||||
let ret = json!([
|
||||
{
|
||||
"category": "fiction",
|
||||
@ -470,7 +512,7 @@ assert_eq!(ret, json);
|
||||
|
||||
#### $..book[?(@.isbn)]
|
||||
```rust
|
||||
let json = reader("$..book[?(@.isbn)]").unwrap();
|
||||
let json = selector("$..book[?(@.isbn)]").unwrap();
|
||||
let ret = json!([
|
||||
{
|
||||
"category": "fiction",
|
||||
@ -492,7 +534,7 @@ assert_eq!(ret, json);
|
||||
|
||||
#### $.store.book[?(@.price < 10)]
|
||||
```rust
|
||||
let json = reader("$.store.book[?(@.price < 10)]").unwrap();
|
||||
let json = selector("$.store.book[?(@.price < 10)]").unwrap();
|
||||
let ret = json!([
|
||||
{
|
||||
"category": "reference",
|
||||
@ -513,7 +555,7 @@ assert_eq!(ret, json);
|
||||
|
||||
#### $..book[?((@.price == 12.99 || $.store.bicycle.price < @.price) || @.category == "reference")]
|
||||
```rust
|
||||
let json = reader(r#"$..book[
|
||||
let json = selector(r#"$..book[
|
||||
?(
|
||||
(@.price == 12.99 || $.store.bicycle.price < @.price)
|
||||
|| @.category == "reference"
|
||||
@ -554,15 +596,21 @@ assert_eq!(ret, json);
|
||||
|
||||
### Browser [Bench Demo](https://freestrings.github.io/jsonpath/bench)
|
||||
|
||||
```
|
||||
'$..book[?(@.price<30 && @.category=="fiction")]' (loop 2,000)
|
||||
```
|
||||
|
||||
#### Chrome: 72.0
|
||||
|
||||
> Something to wrong in chrome
|
||||
|
||||
```
|
||||
jsonpath, 134
|
||||
jsonpath-wasm- reader, 1409
|
||||
jsonpath-wasm- compile, 3237
|
||||
jsonpath-wasm- read, 5302
|
||||
jsonpath, 166
|
||||
jsonpath-wasm- selector, 256
|
||||
jsonpath-wasm- compile, 1168
|
||||
jsonpath-wasm- compile-alloc, 645
|
||||
jsonpath-wasm- select, 3224
|
||||
jsonpath-wasm- select-alloc, 1427
|
||||
```
|
||||
|
||||
#### Firefox: 65.0
|
||||
@ -570,40 +618,65 @@ jsonpath-wasm- read, 5302
|
||||
> jsonpath-wasm is faster than jsonpath
|
||||
|
||||
```
|
||||
jsonpath, 301
|
||||
jsonpath-wasm- reader, 166
|
||||
jsonpath-wasm- compile, 130
|
||||
jsonpath-wasm- read, 144
|
||||
jsonpath, 125
|
||||
jsonpath-wasm- selector, 101
|
||||
jsonpath-wasm- compile, 169
|
||||
jsonpath-wasm- compile-alloc, 78
|
||||
jsonpath-wasm- select, 186
|
||||
jsonpath-wasm- select-alloc, 93
|
||||
```
|
||||
|
||||
### NodeJs
|
||||
|
||||
* NodeJS: 11.0
|
||||
* CPU: Intel Core i5-4460
|
||||
* Memory: 16GB
|
||||
|
||||
> Rust > jsonpath > jsonpath-wasm
|
||||
|
||||
|
||||
```bash
|
||||
cd benches && ./bench_node_vs_rust.sh
|
||||
|
||||
$..book[?(@.price<30 && @.category==fiction)] (loop 100,000)
|
||||
|
||||
Rust:
|
||||
|
||||
real 0m1.141s
|
||||
user 0m1.137s
|
||||
sys 0m0.004s
|
||||
real 0m0.862s
|
||||
user 0m0.862s
|
||||
sys 0m0.000s
|
||||
|
||||
NodeJs - jsonpath module:
|
||||
|
||||
real 0m3.718s
|
||||
user 0m4.175s
|
||||
sys 0m0.050s
|
||||
real 0m3.667s
|
||||
user 0m4.139s
|
||||
sys 0m0.045s
|
||||
|
||||
NodeJs - jsonpath-wasm module:
|
||||
NodeJs - jsonpath-wasm module - selector:
|
||||
|
||||
real 0m5.331s
|
||||
user 0m5.494s
|
||||
sys 0m0.093s
|
||||
|
||||
NodeJs - jsonpath-wasm module - compile:
|
||||
|
||||
real 0m8.665s
|
||||
user 0m8.809s
|
||||
sys 0m0.197s
|
||||
|
||||
NodeJs - jsonpath-wasm module - compile-alloc:
|
||||
|
||||
real 0m4.014s
|
||||
user 0m4.173s
|
||||
sys 0m0.088s
|
||||
|
||||
NodeJs - jsonpath-wasm module - select:
|
||||
|
||||
real 0m9.843s
|
||||
user 0m9.897s
|
||||
sys 0m0.244s
|
||||
|
||||
NodeJs - jsonpath-wasm module - select-alloc:
|
||||
|
||||
real 0m5.212s
|
||||
user 0m5.339s
|
||||
sys 0m0.096s
|
||||
|
||||
real 0m10.205s
|
||||
user 0m10.281s
|
||||
sys 0m0.383s
|
||||
```
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "bench_bin"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
|
||||
[dependencies]
|
||||
jsonpath_lib = {path = "../../"}
|
||||
|
@ -14,8 +14,8 @@ fn read_json(path: &str) -> String {
|
||||
fn main() {
|
||||
let string = read_json("../example.json");
|
||||
let json: Value = serde_json::from_str(string.as_str()).unwrap();
|
||||
let mut reader = jsonpath::reader(json);
|
||||
let mut selector = jsonpath::selector(json);
|
||||
for _ in 1..100000 {
|
||||
let _ = reader(r#"$..book[?(@.price<30 && @.category=="fiction")]"#).unwrap();
|
||||
let _ = selector(r#"$..book[?(@.price<30 && @.category=="fiction")]"#).unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,14 @@ printf "\n\n"
|
||||
|
||||
echo "Rust: " && time ./bench.sh
|
||||
printf "\n"
|
||||
cd "${DIR}"/javascript && echo "NodeJs - jsonpath module: " && time ./bench.sh jsonpathOnly
|
||||
cd "${DIR}"/javascript && echo "NodeJs - jsonpath module: " && time ./bench.sh jsonpath
|
||||
printf "\n"
|
||||
cd "${DIR}"/javascript && echo "NodeJs - jsonpath-wasm module: " && time ./bench.sh jsonpathWasmOnly
|
||||
cd "${DIR}"/javascript && echo "NodeJs - jsonpath-wasm module - selector: " && time ./bench.sh wasmSelector
|
||||
printf "\n"
|
||||
cd "${DIR}"/javascript && echo "NodeJs - jsonpath-wasm module - compile: " && time ./bench.sh wasmCompile
|
||||
printf "\n"
|
||||
cd "${DIR}"/javascript && echo "NodeJs - jsonpath-wasm module - compile-alloc: " && time ./bench.sh wasmCompileAlloc
|
||||
printf "\n"
|
||||
cd "${DIR}"/javascript && echo "NodeJs - jsonpath-wasm module - select:" && time ./bench.sh wasmSelect
|
||||
printf "\n"
|
||||
cd "${DIR}"/javascript && echo "NodeJs - jsonpath-wasm module - select-alloc:" && time ./bench.sh wasmSelectAlloc
|
@ -1,118 +1,124 @@
|
||||
let json = {
|
||||
"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,
|
||||
};
|
||||
|
||||
const jp = require('jsonpath');
|
||||
const jpw = require('@nodejs/jsonpath-wasm');
|
||||
const Benchmark = require('benchmark');
|
||||
const iter = 100000;
|
||||
|
||||
function compareJsonpath(path) {
|
||||
let r1 = jp.query(json, path);
|
||||
let r2 = jpw.read(json, path);
|
||||
|
||||
let template = jpw.compile(path);
|
||||
|
||||
var suite = new Benchmark.Suite;
|
||||
|
||||
suite.add('jp', function() {
|
||||
jp.query(json, path);
|
||||
})
|
||||
.add('jpw', function() {
|
||||
template(json);
|
||||
})
|
||||
.on('cycle', function(event) {
|
||||
console.log(String(event.target));
|
||||
})
|
||||
.on('complete', function() {
|
||||
console.log('Fastest is ' + this.filter('fastest').map('name'));
|
||||
console.log('Slowest is ' + this.filter('slowest').map('name'));
|
||||
})
|
||||
.run({ 'async': true });
|
||||
}
|
||||
|
||||
function compareEmptyFunction() {
|
||||
var suite = new Benchmark.Suite;
|
||||
|
||||
suite.add('js', function() {
|
||||
})
|
||||
.add('rust', function() {
|
||||
jpw.testa();
|
||||
})
|
||||
.on('cycle', function(event) {
|
||||
console.log(String(event.target));
|
||||
})
|
||||
.on('complete', function() {
|
||||
console.log('Fastest is ' + this.filter('fastest').map('name'));
|
||||
console.log('Slowest is ' + this.filter('slowest').map('name'));
|
||||
})
|
||||
.run({});
|
||||
}
|
||||
|
||||
function jsonpathOnly() {
|
||||
for(var i = 0; i < 100000 ; i++) {
|
||||
function jsonpath() {
|
||||
for (var i = 0; i < iter; i++) {
|
||||
let _ = jp.query(json, '$..book[?(@.price<30 && @.category=="fiction")]');
|
||||
}
|
||||
}
|
||||
|
||||
function jsonpathWasmOnly() {
|
||||
let reader = jpw.reader(json);
|
||||
for(var i = 0; i < 100000 ; i++) {
|
||||
let _ = reader('$..book[?(@.price<30 && @.category=="fiction")]');
|
||||
function wasmSelector() {
|
||||
let selector = jpw.selector(json);
|
||||
for (var i = 0; i < iter; i++) {
|
||||
let _ = selector('$..book[?(@.price<30 && @.category=="fiction")]');
|
||||
}
|
||||
}
|
||||
|
||||
if(process.argv.length < 3) {
|
||||
let functions = ['', 'compareJsonpath', 'compareEmptyFunction', 'jsonpathOnly', 'jsonpathWasmOnly'];
|
||||
console.log("node bench.js", functions.join("\n\t|"));
|
||||
return;
|
||||
function wasmCompile() {
|
||||
let template = jpw.compile('$..book[?(@.price<30 && @.category=="fiction")]');
|
||||
for (var i = 0; i < iter; i++) {
|
||||
let _ = template(json);
|
||||
}
|
||||
}
|
||||
|
||||
function wasmCompileAlloc() {
|
||||
let ptr = jpw.alloc_json(json);
|
||||
if (ptr == 0) {
|
||||
console.error('Invalid pointer');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let template = jpw.compile('$..book[?(@.price<30 && @.category=="fiction")]');
|
||||
for (var i = 0; i < iter; i++) {
|
||||
let _ = template(ptr);
|
||||
}
|
||||
} finally {
|
||||
jpw.dealloc_json(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
function wasmSelect() {
|
||||
for (var i = 0; i < iter; i++) {
|
||||
let _ = jpw.select(json, '$..book[?(@.price<30 && @.category=="fiction")]');
|
||||
}
|
||||
}
|
||||
|
||||
function wasmSelectAlloc() {
|
||||
let ptr = jpw.alloc_json(json);
|
||||
if (ptr == 0) {
|
||||
console.error('Invalid pointer');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
for (var i = 0; i < iter; i++) {
|
||||
let _ = jpw.select(ptr, '$..book[?(@.price<30 && @.category=="fiction")]');
|
||||
}
|
||||
} finally {
|
||||
jpw.dealloc_json(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
let functionName = process.argv[2];
|
||||
|
||||
switch (functionName) {
|
||||
case 'compareJsonpath':
|
||||
compareJsonpath('$..book[?(@.price<30 && @.category=="fiction")]');
|
||||
case 'jsonpath':
|
||||
jsonpath();
|
||||
break;
|
||||
case 'compareEmptyFunction':
|
||||
compareEmptyFunction();
|
||||
case 'wasmSelector':
|
||||
wasmSelector();
|
||||
break;
|
||||
case 'jsonpathWasmOnly':
|
||||
jsonpathWasmOnly();
|
||||
case 'wasmCompile':
|
||||
wasmCompile();
|
||||
break;
|
||||
case 'wasmSelect':
|
||||
wasmSelect();
|
||||
break;
|
||||
case 'wasmCompileAlloc':
|
||||
wasmCompileAlloc();
|
||||
break;
|
||||
case 'wasmSelectAlloc':
|
||||
wasmSelectAlloc();
|
||||
default:
|
||||
jsonpathOnly();
|
||||
console.error('Invalid function name');
|
||||
}
|
19
benches/javascript/package-lock.json
generated
19
benches/javascript/package-lock.json
generated
@ -9,15 +9,6 @@
|
||||
"resolved": "https://registry.npmjs.org/JSONSelect/-/JSONSelect-0.4.0.tgz",
|
||||
"integrity": "sha1-oI7cxn6z/L6Z7WMIVTRKDPKCu40="
|
||||
},
|
||||
"benchmark": {
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz",
|
||||
"integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=",
|
||||
"requires": {
|
||||
"lodash": "^4.17.4",
|
||||
"platform": "^1.3.3"
|
||||
}
|
||||
},
|
||||
"cjson": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/cjson/-/cjson-0.2.1.tgz",
|
||||
@ -131,11 +122,6 @@
|
||||
"resolved": "https://registry.npmjs.org/lex-parser/-/lex-parser-0.1.4.tgz",
|
||||
"integrity": "sha1-ZMTwJfF/1Tv7RXY/rrFvAVp0dVA="
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.11",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
|
||||
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
|
||||
},
|
||||
"nomnom": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.5.2.tgz",
|
||||
@ -165,11 +151,6 @@
|
||||
"wordwrap": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"platform": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/platform/-/platform-1.3.5.tgz",
|
||||
"integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q=="
|
||||
},
|
||||
"prelude-ls": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
|
||||
|
@ -6,7 +6,6 @@
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"dependencies": {
|
||||
"jsonpath": "*",
|
||||
"benchmark": "*"
|
||||
"jsonpath": "*"
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
BIN
docs/9a826648f4cbc2bc8591.module.wasm
Normal file
BIN
docs/9a826648f4cbc2bc8591.module.wasm
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
BIN
docs/bench/9a826648f4cbc2bc8591.module.wasm
Normal file
BIN
docs/bench/9a826648f4cbc2bc8591.module.wasm
Normal file
Binary file not shown.
36
docs/bench/bootstrap.js
vendored
36
docs/bench/bootstrap.js
vendored
@ -55,20 +55,29 @@
|
||||
/******/ "../browser_pkg/jsonpath_wasm_bg.wasm": function() {
|
||||
/******/ return {
|
||||
/******/ "./jsonpath_wasm": {
|
||||
/******/ "__wbindgen_string_new": function(p0i32,p1i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_string_new"](p0i32,p1i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_object_drop_ref": function(p0i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_object_drop_ref"](p0i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_object_clone_ref": function(p0i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_object_clone_ref"](p0i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_cb_forget": function(p0i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_cb_forget"](p0i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_json_parse": function(p0i32,p1i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_json_parse"](p0i32,p1i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_json_serialize": function(p0i32,p1i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_json_serialize"](p0i32,p1i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_cb_forget": function(p0i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_cb_forget"](p0i32);
|
||||
/******/ "__widl_f_log_1_": function(p0i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__widl_f_log_1_"](p0i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_object_drop_ref": function(p0i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_object_drop_ref"](p0i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_string_new": function(p0i32,p1i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_string_new"](p0i32,p1i32);
|
||||
/******/ "__wbindgen_number_get": function(p0i32,p1i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_number_get"](p0i32,p1i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_is_string": function(p0i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_is_string"](p0i32);
|
||||
@ -76,17 +85,14 @@
|
||||
/******/ "__wbindgen_string_get": function(p0i32,p1i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_string_get"](p0i32,p1i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_object_clone_ref": function(p0i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_object_clone_ref"](p0i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_throw": function(p0i32,p1i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_throw"](p0i32,p1i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_closure_wrapper33": function(p0i32,p1i32,p2i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper33"](p0i32,p1i32,p2i32);
|
||||
/******/ "__wbindgen_closure_wrapper101": function(p0i32,p1i32,p2i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper101"](p0i32,p1i32,p2i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_closure_wrapper35": function(p0i32,p1i32,p2i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper35"](p0i32,p1i32,p2i32);
|
||||
/******/ "__wbindgen_closure_wrapper103": function(p0i32,p1i32,p2i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper103"](p0i32,p1i32,p2i32);
|
||||
/******/ }
|
||||
/******/ }
|
||||
/******/ };
|
||||
@ -186,7 +192,7 @@
|
||||
/******/ promises.push(installedWasmModuleData);
|
||||
/******/ else {
|
||||
/******/ var importObject = wasmImportObjects[wasmModuleId]();
|
||||
/******/ var req = fetch(__webpack_require__.p + "" + {"../browser_pkg/jsonpath_wasm_bg.wasm":"95acaf85df86c0a76234"}[wasmModuleId] + ".module.wasm");
|
||||
/******/ var req = fetch(__webpack_require__.p + "" + {"../browser_pkg/jsonpath_wasm_bg.wasm":"9a826648f4cbc2bc8591"}[wasmModuleId] + ".module.wasm");
|
||||
/******/ var promise;
|
||||
/******/ if(importObject instanceof Promise && typeof WebAssembly.compileStreaming === 'function') {
|
||||
/******/ promise = Promise.all([WebAssembly.compileStreaming(req), importObject]).then(function(items) {
|
||||
|
60
docs/bootstrap.js
vendored
60
docs/bootstrap.js
vendored
@ -52,41 +52,47 @@
|
||||
/******/ function promiseResolve() { return Promise.resolve(); }
|
||||
/******/
|
||||
/******/ var wasmImportObjects = {
|
||||
/******/ "../pkg/jsonpath_wasm_bg.wasm": function() {
|
||||
/******/ "../browser_pkg/jsonpath_wasm_bg.wasm": function() {
|
||||
/******/ return {
|
||||
/******/ "./jsonpath_wasm": {
|
||||
/******/ "__wbindgen_json_parse": function(p0i32,p1i32) {
|
||||
/******/ return installedModules["../pkg/jsonpath_wasm.js"].exports["__wbindgen_json_parse"](p0i32,p1i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_json_serialize": function(p0i32,p1i32) {
|
||||
/******/ return installedModules["../pkg/jsonpath_wasm.js"].exports["__wbindgen_json_serialize"](p0i32,p1i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_cb_forget": function(p0i32) {
|
||||
/******/ return installedModules["../pkg/jsonpath_wasm.js"].exports["__wbindgen_cb_forget"](p0i32);
|
||||
/******/ "__wbindgen_string_new": function(p0i32,p1i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_string_new"](p0i32,p1i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_object_drop_ref": function(p0i32) {
|
||||
/******/ return installedModules["../pkg/jsonpath_wasm.js"].exports["__wbindgen_object_drop_ref"](p0i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_string_new": function(p0i32,p1i32) {
|
||||
/******/ return installedModules["../pkg/jsonpath_wasm.js"].exports["__wbindgen_string_new"](p0i32,p1i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_is_string": function(p0i32) {
|
||||
/******/ return installedModules["../pkg/jsonpath_wasm.js"].exports["__wbindgen_is_string"](p0i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_string_get": function(p0i32,p1i32) {
|
||||
/******/ return installedModules["../pkg/jsonpath_wasm.js"].exports["__wbindgen_string_get"](p0i32,p1i32);
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_object_drop_ref"](p0i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_object_clone_ref": function(p0i32) {
|
||||
/******/ return installedModules["../pkg/jsonpath_wasm.js"].exports["__wbindgen_object_clone_ref"](p0i32);
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_object_clone_ref"](p0i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_cb_forget": function(p0i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_cb_forget"](p0i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_json_parse": function(p0i32,p1i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_json_parse"](p0i32,p1i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_json_serialize": function(p0i32,p1i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_json_serialize"](p0i32,p1i32);
|
||||
/******/ },
|
||||
/******/ "__widl_f_log_1_": function(p0i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__widl_f_log_1_"](p0i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_number_get": function(p0i32,p1i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_number_get"](p0i32,p1i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_is_string": function(p0i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_is_string"](p0i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_string_get": function(p0i32,p1i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_string_get"](p0i32,p1i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_throw": function(p0i32,p1i32) {
|
||||
/******/ return installedModules["../pkg/jsonpath_wasm.js"].exports["__wbindgen_throw"](p0i32,p1i32);
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_throw"](p0i32,p1i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_closure_wrapper28": function(p0i32,p1i32,p2i32) {
|
||||
/******/ return installedModules["../pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper28"](p0i32,p1i32,p2i32);
|
||||
/******/ "__wbindgen_closure_wrapper101": function(p0i32,p1i32,p2i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper101"](p0i32,p1i32,p2i32);
|
||||
/******/ },
|
||||
/******/ "__wbindgen_closure_wrapper30": function(p0i32,p1i32,p2i32) {
|
||||
/******/ return installedModules["../pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper30"](p0i32,p1i32,p2i32);
|
||||
/******/ "__wbindgen_closure_wrapper103": function(p0i32,p1i32,p2i32) {
|
||||
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper103"](p0i32,p1i32,p2i32);
|
||||
/******/ }
|
||||
/******/ }
|
||||
/******/ };
|
||||
@ -176,7 +182,7 @@
|
||||
/******/
|
||||
/******/ // Fetch + compile chunk loading for webassembly
|
||||
/******/
|
||||
/******/ var wasmModules = {"0":["../pkg/jsonpath_wasm_bg.wasm"]}[chunkId] || [];
|
||||
/******/ var wasmModules = {"0":["../browser_pkg/jsonpath_wasm_bg.wasm"]}[chunkId] || [];
|
||||
/******/
|
||||
/******/ wasmModules.forEach(function(wasmModuleId) {
|
||||
/******/ var installedWasmModuleData = installedWasmModules[wasmModuleId];
|
||||
@ -186,7 +192,7 @@
|
||||
/******/ promises.push(installedWasmModuleData);
|
||||
/******/ else {
|
||||
/******/ var importObject = wasmImportObjects[wasmModuleId]();
|
||||
/******/ var req = fetch(__webpack_require__.p + "" + {"../pkg/jsonpath_wasm_bg.wasm":"d6c7cff8a1d228da11b7"}[wasmModuleId] + ".module.wasm");
|
||||
/******/ var req = fetch(__webpack_require__.p + "" + {"../browser_pkg/jsonpath_wasm_bg.wasm":"9a826648f4cbc2bc8591"}[wasmModuleId] + ".module.wasm");
|
||||
/******/ var promise;
|
||||
/******/ if(importObject instanceof Promise && typeof WebAssembly.compileStreaming === 'function') {
|
||||
/******/ promise = Promise.all([WebAssembly.compileStreaming(req), importObject]).then(function(items) {
|
||||
|
Binary file not shown.
@ -23,7 +23,7 @@ mod tests {
|
||||
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, false, false)
|
||||
ValueFilter::new(json.into(), false, false)
|
||||
}
|
||||
|
||||
fn do_filter(path: &str, file: &str) -> JsonValueFilter {
|
||||
@ -71,7 +71,7 @@ mod tests {
|
||||
"Vincent Cannon",
|
||||
"Gray Berry"
|
||||
]);
|
||||
assert_eq!(&friends, current.get_val());
|
||||
assert_eq!(friends, current.get_val().into_value());
|
||||
}
|
||||
let mut jf = new_value_filter("./benches/data_obj.json");
|
||||
{
|
||||
@ -84,7 +84,7 @@ mod tests {
|
||||
"Vincent Cannon",
|
||||
"Gray Berry"
|
||||
]);
|
||||
assert_eq!(&names, current.get_val());
|
||||
assert_eq!(names, current.get_val().into_value());
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,32 +98,32 @@ mod tests {
|
||||
]);
|
||||
|
||||
let jf = do_filter("$.school.friends[1, 2]", "./benches/data_obj.json");
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
assert_eq!(friends, jf.current_value().into_value());
|
||||
|
||||
let jf = do_filter("$.school.friends[1:]", "./benches/data_obj.json");
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
assert_eq!(friends, jf.current_value().into_value());
|
||||
|
||||
let jf = do_filter("$.school.friends[:-2]", "./benches/data_obj.json");
|
||||
let friends = json!([
|
||||
{"id": 0, "name": "Millicent Norman"}
|
||||
]);
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
assert_eq!(friends, jf.current_value().into_value());
|
||||
|
||||
let jf = do_filter("$..friends[2].name", "./benches/data_obj.json");
|
||||
let friends = json!(["Gray Berry", "Gray Berry"]);
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
assert_eq!(friends, jf.current_value().into_value());
|
||||
|
||||
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.current_value());
|
||||
assert_eq!(friends, jf.current_value().into_value());
|
||||
|
||||
let jf = do_filter("$['school']['friends'][*].['name']", "./benches/data_obj.json");
|
||||
let friends = json!(["Millicent Norman","Vincent Cannon","Gray Berry"]);
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
assert_eq!(friends, jf.current_value().into_value());
|
||||
|
||||
let jf = do_filter("$['school']['friends'][0].['name']", "./benches/data_obj.json");
|
||||
let friends = json!("Millicent Norman");
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
assert_eq!(friends, jf.current_value().into_value());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -139,16 +139,16 @@ mod tests {
|
||||
});
|
||||
|
||||
let jf = do_filter("$.school", "./benches/data_obj.json");
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
assert_eq!(friends, jf.current_value().into_value());
|
||||
|
||||
let jf = do_filter("$.school[?(@.friends[0])]", "./benches/data_obj.json");
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
assert_eq!(friends, jf.current_value().into_value());
|
||||
|
||||
let jf = do_filter("$.school[?(@.friends[10])]", "./benches/data_obj.json");
|
||||
assert_eq!(&Value::Null, jf.current_value());
|
||||
assert_eq!(Value::Null, jf.current_value().into_value());
|
||||
|
||||
let jf = do_filter("$.school[?(1==1)]", "./benches/data_obj.json");
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
assert_eq!(friends, jf.current_value().into_value());
|
||||
|
||||
let jf = do_filter("$.school.friends[?(1==1)]", "./benches/data_obj.json");
|
||||
let friends = json!([
|
||||
@ -156,7 +156,7 @@ mod tests {
|
||||
{"id": 1, "name": "Vincent Cannon" },
|
||||
{"id": 2, "name": "Gray Berry"}
|
||||
]);
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
assert_eq!(friends, jf.current_value().into_value());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -171,42 +171,42 @@ mod tests {
|
||||
{"id": 2, "name": "Gray Berry"}
|
||||
]
|
||||
});
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
assert_eq!(friends, jf.current_value().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.current_value());
|
||||
assert_eq!(friends, jf.current_value().into_value());
|
||||
|
||||
let jf = do_filter("$.friends[?(@.id >= 2)]", "./benches/data_obj.json");
|
||||
let friends = json!([
|
||||
{ "id" : 2, "name" : "Gray Berry" }
|
||||
]);
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
assert_eq!(friends, jf.current_value().into_value());
|
||||
|
||||
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.current_value());
|
||||
assert_eq!(friends, jf.current_value().into_value());
|
||||
|
||||
let jf = do_filter("$.friends[?( (@.id >= 2 || @.id == 1) && @.id == 0)]", "./benches/data_obj.json");
|
||||
assert_eq!(&Value::Null, jf.current_value());
|
||||
assert_eq!(Value::Null, jf.current_value().into_value());
|
||||
|
||||
let jf = do_filter("$..friends[?(@.id == $.index)].id", "./benches/data_obj.json");
|
||||
let friends = json!([0, 0]);
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
assert_eq!(friends, jf.current_value().into_value());
|
||||
|
||||
let jf = do_filter("$..book[?($.store.bicycle.price < @.price)].price", "./benches/example.json");
|
||||
let friends = json!([22.99]);
|
||||
assert_eq!(&friends, jf.current_value());
|
||||
assert_eq!(friends, jf.current_value().into_value());
|
||||
|
||||
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.current_value());
|
||||
assert_eq!(friends, jf.current_value().into_value());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -215,10 +215,10 @@ mod tests {
|
||||
|
||||
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.current_value());
|
||||
assert_eq!(ret, jf.current_value().into_value());
|
||||
|
||||
let jf = do_filter("$..author", "./benches/example.json");
|
||||
assert_eq!(&ret, jf.current_value());
|
||||
assert_eq!(ret, jf.current_value().into_value());
|
||||
|
||||
let jf = do_filter("$.store.*", "./benches/example.json");
|
||||
let ret = json!([
|
||||
@ -230,11 +230,11 @@ mod tests {
|
||||
],
|
||||
{"color" : "red","price" : 19.95},
|
||||
]);
|
||||
assert_eq!(&ret, jf.current_value());
|
||||
assert_eq!(ret, jf.current_value().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.current_value());
|
||||
assert_eq!(ret, jf.current_value().into_value());
|
||||
|
||||
let jf = do_filter("$..book[2]", "./benches/example.json");
|
||||
let ret = json!([{
|
||||
@ -244,7 +244,7 @@ mod tests {
|
||||
"isbn" : "0-553-21311-3",
|
||||
"price" : 8.99
|
||||
}]);
|
||||
assert_eq!(&ret, jf.current_value());
|
||||
assert_eq!(ret, jf.current_value().into_value());
|
||||
|
||||
let jf = do_filter("$..book[-2]", "./benches/example.json");
|
||||
let ret = json!([{
|
||||
@ -254,7 +254,7 @@ mod tests {
|
||||
"isbn" : "0-553-21311-3",
|
||||
"price" : 8.99
|
||||
}]);
|
||||
assert_eq!(&ret, jf.current_value());
|
||||
assert_eq!(ret, jf.current_value().into_value());
|
||||
|
||||
let jf = do_filter("$..book[0,1]", "./benches/example.json");
|
||||
let ret = json!([
|
||||
@ -271,7 +271,7 @@ mod tests {
|
||||
"price" : 12.99
|
||||
}
|
||||
]);
|
||||
assert_eq!(&ret, jf.current_value());
|
||||
assert_eq!(ret, jf.current_value().into_value());
|
||||
|
||||
let jf = do_filter("$..book[:2]", "./benches/example.json");
|
||||
let ret = json!([
|
||||
@ -288,7 +288,7 @@ mod tests {
|
||||
"price" : 12.99
|
||||
}
|
||||
]);
|
||||
assert_eq!(&ret, jf.current_value());
|
||||
assert_eq!(ret, jf.current_value().into_value());
|
||||
|
||||
let jf = do_filter("$..book[2:]", "./benches/example.json");
|
||||
let ret = json!([
|
||||
@ -307,7 +307,7 @@ mod tests {
|
||||
"price" : 22.99
|
||||
}
|
||||
]);
|
||||
assert_eq!(&ret, jf.current_value());
|
||||
assert_eq!(ret, jf.current_value().into_value());
|
||||
|
||||
let jf = do_filter("$..book[?(@.isbn)]", "./benches/example.json");
|
||||
let ret = json!([
|
||||
@ -326,7 +326,7 @@ mod tests {
|
||||
"price" : 22.99
|
||||
}
|
||||
]);
|
||||
assert_eq!(&ret, jf.current_value());
|
||||
assert_eq!(ret, jf.current_value().into_value());
|
||||
|
||||
let jf = do_filter("$.store.book[?(@.price < 10)]", "./benches/example.json");
|
||||
let ret = json!([
|
||||
@ -344,10 +344,10 @@ mod tests {
|
||||
"price" : 8.99
|
||||
}
|
||||
]);
|
||||
assert_eq!(&ret, jf.current_value());
|
||||
assert_eq!(ret, jf.current_value().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.current_value());
|
||||
assert_eq!(json, jf.current_value().into_value());
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@ pub enum TermContext {
|
||||
}
|
||||
|
||||
impl TermContext {
|
||||
fn cmp<F: PrivCmp + IntoType>(&mut self, other: &mut TermContext, cmp_fn: F, default: bool) -> TermContext {
|
||||
fn cmp<F: PrivCmp + IntoType>(&self, other: &TermContext, cmp_fn: F, default: bool) -> TermContext {
|
||||
match self {
|
||||
TermContext::Constants(et) => {
|
||||
match other {
|
||||
@ -51,7 +51,7 @@ impl TermContext {
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp_cond(&mut self, other: &mut TermContext, cmp_cond_type: CmpCondType) -> TermContext {
|
||||
fn cmp_cond(&self, other: &TermContext, cmp_cond_type: CmpCondType) -> TermContext {
|
||||
match self {
|
||||
TermContext::Constants(et) => {
|
||||
match other {
|
||||
@ -86,35 +86,35 @@ impl TermContext {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq(&mut self, other: &mut TermContext) -> TermContext {
|
||||
pub fn eq(&self, other: &TermContext) -> TermContext {
|
||||
self.cmp(other, CmpEq, false)
|
||||
}
|
||||
|
||||
pub fn ne(&mut self, other: &mut TermContext) -> TermContext {
|
||||
pub fn ne(&self, other: &TermContext) -> TermContext {
|
||||
self.cmp(other, CmpNe, true)
|
||||
}
|
||||
|
||||
pub fn gt(&mut self, other: &mut TermContext) -> TermContext {
|
||||
pub fn gt(&self, other: &TermContext) -> TermContext {
|
||||
self.cmp(other, CmpGt, false)
|
||||
}
|
||||
|
||||
pub fn ge(&mut self, other: &mut TermContext) -> TermContext {
|
||||
pub fn ge(&self, other: &TermContext) -> TermContext {
|
||||
self.cmp(other, CmpGe, false)
|
||||
}
|
||||
|
||||
pub fn lt(&mut self, other: &mut TermContext) -> TermContext {
|
||||
pub fn lt(&self, other: &TermContext) -> TermContext {
|
||||
self.cmp(other, CmpLt, false)
|
||||
}
|
||||
|
||||
pub fn le(&mut self, other: &mut TermContext) -> TermContext {
|
||||
pub fn le(&self, other: &TermContext) -> TermContext {
|
||||
self.cmp(other, CmpLe, false)
|
||||
}
|
||||
|
||||
pub fn and(&mut self, other: &mut TermContext) -> TermContext {
|
||||
pub fn and(&self, other: &TermContext) -> TermContext {
|
||||
self.cmp_cond(other, CmpCondType::And)
|
||||
}
|
||||
|
||||
pub fn or(&mut self, other: &mut TermContext) -> TermContext {
|
||||
pub fn or(&self, other: &TermContext) -> TermContext {
|
||||
self.cmp_cond(other, CmpCondType::Or)
|
||||
}
|
||||
}
|
||||
|
@ -1,29 +1,28 @@
|
||||
use core::borrow::Borrow;
|
||||
use std::error::Error;
|
||||
use std::rc::Rc;
|
||||
use std::result;
|
||||
|
||||
use ref_value::*;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
use super::parser::*;
|
||||
|
||||
use super::term::*;
|
||||
use super::value_wrapper::*;
|
||||
|
||||
trait ArrayIndex {
|
||||
fn index(&self, v: &Value) -> usize;
|
||||
fn index(&self, v: &RefValueWrapper) -> usize;
|
||||
|
||||
fn take_value(&self, v: &mut Value) -> Value {
|
||||
fn take_value(&self, v: &RefValueWrapper) -> RefValueWrapper {
|
||||
let idx = self.index(v);
|
||||
match v.get_mut(idx) {
|
||||
Some(v) => v.take(),
|
||||
_ => Value::Null
|
||||
match v.get(idx) {
|
||||
Some(v) => v,
|
||||
_ => RefValue::Null.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArrayIndex for f64 {
|
||||
fn index(&self, v: &Value) -> usize {
|
||||
fn index(&self, v: &RefValueWrapper) -> usize {
|
||||
if v.is_array() && self < &0_f64 {
|
||||
(v.as_array().unwrap().len() as f64 + self) as usize
|
||||
} else {
|
||||
@ -33,7 +32,7 @@ impl ArrayIndex for f64 {
|
||||
}
|
||||
|
||||
impl ArrayIndex for isize {
|
||||
fn index(&self, v: &Value) -> usize {
|
||||
fn index(&self, v: &RefValueWrapper) -> usize {
|
||||
if v.is_array() && self < &0_isize {
|
||||
(v.as_array().unwrap().len() as isize + self) as usize
|
||||
} else {
|
||||
@ -43,7 +42,7 @@ impl ArrayIndex for isize {
|
||||
}
|
||||
|
||||
impl ArrayIndex for usize {
|
||||
fn index(&self, _: &Value) -> usize {
|
||||
fn index(&self, _: &RefValueWrapper) -> usize {
|
||||
*self as usize
|
||||
}
|
||||
}
|
||||
@ -63,54 +62,55 @@ pub struct ValueFilter {
|
||||
}
|
||||
|
||||
impl ValueFilter {
|
||||
pub fn new(v: Value, is_leaves: bool, filter_mode: bool) -> Self {
|
||||
pub fn new(v: RefValueWrapper, is_leaves: bool, filter_mode: bool) -> Self {
|
||||
ValueFilter { vw: ValueWrapper::new(v, is_leaves), last_key: None, filter_mode }
|
||||
}
|
||||
|
||||
fn iter_to_value_vec<'a, I: Iterator<Item=&'a mut Value>>(iter: I) -> Vec<Value> {
|
||||
iter.map(|v| v.take())
|
||||
fn iter_to_value_vec<'a, I: Iterator<Item=&'a TypeRefValue>>(iter: I) -> Vec<TypeRefValue> {
|
||||
iter
|
||||
.map(|v| v.clone())
|
||||
.filter(|v| !v.is_null())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_nested_array<F: ArrayIndex>(v: &mut Value, key: F, filter_mode: bool) -> Value {
|
||||
fn get_nested_array<F: ArrayIndex>(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.take()
|
||||
v.clone()
|
||||
} else {
|
||||
let idx = key.index(v);
|
||||
v.get_mut(idx).unwrap().take()
|
||||
v.get(idx).unwrap().clone()
|
||||
}
|
||||
} else {
|
||||
key.take_value(v)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_nested_object(v: &mut Value, key: &String, filter_mode: bool) -> Value {
|
||||
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.take()
|
||||
v.clone()
|
||||
} else {
|
||||
v.get_mut(key).unwrap().take()
|
||||
v.get(key.clone()).unwrap().clone()
|
||||
}
|
||||
} else {
|
||||
Value::Null
|
||||
RefValue::Null.into()
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_all(key: Option<&String>, v: &Value, buf: &mut Vec<Value>) {
|
||||
match v {
|
||||
Value::Array(vec) => {
|
||||
fn collect_all(key: Option<&String>, v: &RefValueWrapper, buf: &mut Vec<TypeRefValue>) {
|
||||
match v.get_data_ref() {
|
||||
RefValue::Array(vec) => {
|
||||
if key.is_none() {
|
||||
for v in vec {
|
||||
buf.push(v.clone());
|
||||
}
|
||||
}
|
||||
for i in vec {
|
||||
Self::collect_all(key, &i, buf);
|
||||
Self::collect_all(key, &i.into(), buf);
|
||||
}
|
||||
}
|
||||
Value::Object(v) => {
|
||||
RefValue::Object(v) => {
|
||||
for (k, v) in v.into_iter() {
|
||||
if match key {
|
||||
Some(map_key) => map_key == k,
|
||||
@ -120,7 +120,7 @@ impl ValueFilter {
|
||||
}
|
||||
}
|
||||
for (_, v) in v.into_iter() {
|
||||
Self::collect_all(key, &v, buf);
|
||||
Self::collect_all(key, &v.into(), buf);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@ -133,7 +133,7 @@ impl ValueFilter {
|
||||
Self::collect_all(None, &self.vw.get_val(), &mut buf);
|
||||
trace!("step_leaves_all - {:?}", buf);
|
||||
self.last_key = Some(ValueFilterKey::All);
|
||||
self.vw = ValueWrapper::new(Value::Array(buf), true);
|
||||
self.vw = ValueWrapper::new(RefValue::Array(buf).into(), true);
|
||||
&self.vw
|
||||
}
|
||||
|
||||
@ -143,53 +143,63 @@ impl ValueFilter {
|
||||
|
||||
pub fn step_leaves_string(&mut self, key: &String) -> &ValueWrapper {
|
||||
debug!("step_leaves_string");
|
||||
let mut buf: Vec<Value> = Vec::new();
|
||||
let mut buf = Vec::new();
|
||||
Self::collect_all(Some(key), &self.vw.get_val(), &mut buf);
|
||||
trace!("step_leaves_string - {:?}", buf);
|
||||
self.last_key = Some(ValueFilterKey::String(key.clone()));
|
||||
self.vw = ValueWrapper::new(Value::Array(buf), true);
|
||||
self.vw = ValueWrapper::new(RefValue::Array(buf).into(), true);
|
||||
&self.vw
|
||||
}
|
||||
|
||||
pub fn step_in_all(&mut self) -> &ValueWrapper {
|
||||
debug!("step_in_all");
|
||||
|
||||
let vec = match &mut self.vw.get_val_mut() {
|
||||
Value::Object(map) => Self::iter_to_value_vec(map.values_mut()),
|
||||
Value::Array(list) => Self::iter_to_value_vec(list.iter_mut()),
|
||||
Value::Null => Vec::new(),
|
||||
other => vec![other.take()]
|
||||
let vec = match self.vw.get_val().get_data_ref() {
|
||||
RefValue::Object(ref map) => {
|
||||
Self::iter_to_value_vec(map.values())
|
||||
}
|
||||
RefValue::Array(ref list) => {
|
||||
Self::iter_to_value_vec(list.iter())
|
||||
}
|
||||
RefValue::Null => Vec::new(),
|
||||
_ => vec![self.vw.get_val().clone_data()]
|
||||
};
|
||||
|
||||
self.last_key = Some(ValueFilterKey::All);
|
||||
self.vw.replace(Value::Array(vec));
|
||||
trace!("step_in_all - {:?}", self.vw.get_val());
|
||||
self.vw.replace(RefValue::Array(vec).into());
|
||||
trace!("step_in_all - {:?}", self.vw.get_val().get_data_ref());
|
||||
&self.vw
|
||||
}
|
||||
|
||||
pub fn step_in_num(&mut self, key: &f64) -> &ValueWrapper {
|
||||
debug!("step_in_num");
|
||||
trace!("step_in_num - before: leaves {}, filterMode {} - {:?}", self.vw.is_leaves(), self.filter_mode, self.vw.get_val());
|
||||
trace!("step_in_num - before: leaves {}, filterMode {} - {:?}"
|
||||
, self.vw.is_leaves()
|
||||
, self.filter_mode
|
||||
, self.vw.get_val().get_data_ref());
|
||||
|
||||
let v = if self.vw.is_leaves() {
|
||||
let filter_mode = self.filter_mode;
|
||||
match &mut self.vw.get_val_mut() {
|
||||
Value::Array(v) => {
|
||||
let vec: Vec<Value> = v.iter_mut()
|
||||
.map(|v| Self::get_nested_array(v, *key, filter_mode))
|
||||
.filter(|v| !v.is_null())
|
||||
.collect();
|
||||
Value::Array(vec)
|
||||
match self.vw.get_val().get_data_ref() {
|
||||
RefValue::Array(ref vec) => {
|
||||
let mut ret = Vec::new();
|
||||
for v in vec {
|
||||
let wrapper = Self::get_nested_array(&v.into(), *key, filter_mode);
|
||||
if !wrapper.is_null() {
|
||||
ret.push(wrapper.clone_data());
|
||||
}
|
||||
}
|
||||
RefValue::Array(ret).into()
|
||||
}
|
||||
other => key.take_value(other)
|
||||
_ => key.take_value(&self.vw.get_val())
|
||||
}
|
||||
} else {
|
||||
key.take_value(self.vw.get_val_mut())
|
||||
key.take_value(&self.vw.get_val())
|
||||
};
|
||||
|
||||
self.last_key = Some(ValueFilterKey::Num(key.index(&v)));
|
||||
self.vw.replace(v);
|
||||
trace!("step_in_num - after: {:?}", self.vw.get_val());
|
||||
trace!("step_in_num - after: {:?}", self.vw.get_val().get_data_ref());
|
||||
&self.vw
|
||||
}
|
||||
|
||||
@ -199,55 +209,68 @@ impl ValueFilter {
|
||||
|
||||
pub fn step_in_string(&mut self, key: &String) -> &ValueWrapper {
|
||||
debug!("step_in_string");
|
||||
trace!("step_in_string - before: {},{},{:?}", self.vw.is_leaves(), self.filter_mode, self.vw.get_val());
|
||||
trace!("step_in_string - before: {},{},{:?}"
|
||||
, self.vw.is_leaves()
|
||||
, self.filter_mode
|
||||
, self.vw.get_val().get_data_ref());
|
||||
|
||||
let filter_mode = self.filter_mode;
|
||||
let is_leaves = self.vw.is_leaves();
|
||||
let v = match &mut self.vw.get_val_mut() {
|
||||
Value::Array(ref mut vec) if is_leaves => {
|
||||
let val = match self.vw.get_val().get_data_ref() {
|
||||
RefValue::Array(ref vec) if is_leaves => {
|
||||
let mut buf = Vec::new();
|
||||
for mut item in vec {
|
||||
if let Value::Array(v) = item {
|
||||
let mut ret: Vec<Value> = v.iter_mut()
|
||||
.map(|v| Self::get_nested_object(v, key, filter_mode))
|
||||
.filter(|v| !v.is_null())
|
||||
.collect();
|
||||
for mut v in vec {
|
||||
let wrapper: RefValueWrapper = v.into();
|
||||
if wrapper.is_array() {
|
||||
let vec = wrapper.as_array().unwrap();
|
||||
let mut ret = Vec::new();
|
||||
for v in vec {
|
||||
let nested_wrapper = Self::get_nested_object(&v.into(), key, filter_mode);
|
||||
if !nested_wrapper.is_null() {
|
||||
ret.push(nested_wrapper.clone_data());
|
||||
}
|
||||
}
|
||||
buf.append(&mut ret);
|
||||
} else {
|
||||
match item.get_mut(key) {
|
||||
Some(v) => buf.push(v.take()),
|
||||
match wrapper.get(key.clone()) {
|
||||
Some(v) => buf.push(v.clone_data()),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Value::Array(buf)
|
||||
RefValue::Array(buf).into()
|
||||
}
|
||||
Value::Array(v) if !is_leaves => {
|
||||
let vec: Vec<Value> = v.iter_mut()
|
||||
.map(|v| Self::get_nested_object(v, key, filter_mode))
|
||||
.filter(|v| !v.is_null())
|
||||
.collect();
|
||||
|
||||
Value::Array(vec)
|
||||
RefValue::Array(ref vec) if !is_leaves => {
|
||||
let mut ret = Vec::new();
|
||||
for v in vec {
|
||||
let wrapper = Self::get_nested_object(&v.into(), key, filter_mode);
|
||||
if !wrapper.is_null() {
|
||||
ret.push(wrapper.clone_data());
|
||||
}
|
||||
}
|
||||
RefValue::Array(ret).into()
|
||||
}
|
||||
other => {
|
||||
match other.get_mut(key) {
|
||||
Some(v) => v.take(),
|
||||
_ => Value::Null
|
||||
_ => {
|
||||
match self.vw.get_val().get(key.clone()) {
|
||||
Some(v) => v.clone(),
|
||||
_ => RefValue::Null.into()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.last_key = Some(ValueFilterKey::String(key.clone()));
|
||||
self.vw.replace(v);
|
||||
trace!("step_in_string - after: {},{},{:?}", self.vw.is_leaves(), self.filter_mode, self.vw.get_val());
|
||||
self.vw.replace(val);
|
||||
trace!("step_in_string - after: {},{},{:?}"
|
||||
, self.vw.is_leaves()
|
||||
, self.filter_mode
|
||||
, self.vw.get_val().get_data_ref());
|
||||
&self.vw
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JsonValueFilter {
|
||||
json: Rc<Box<Value>>,
|
||||
json: RefValueWrapper,
|
||||
filter_stack: Vec<ValueFilter>,
|
||||
token_stack: Vec<ParseToken>,
|
||||
term_stack: Vec<TermContext>,
|
||||
@ -257,12 +280,12 @@ impl JsonValueFilter {
|
||||
pub fn new(json: &str) -> result::Result<Self, String> {
|
||||
let json: Value = serde_json::from_str(json)
|
||||
.map_err(|e| e.description().to_string())?;
|
||||
Ok(JsonValueFilter::new_from_value(Rc::new(Box::new(json))))
|
||||
Ok(JsonValueFilter::new_from_value(json.into()))
|
||||
}
|
||||
|
||||
pub fn new_from_value(json: Rc<Box<Value>>) -> Self {
|
||||
pub fn new_from_value(json: RefValueWrapper) -> Self {
|
||||
JsonValueFilter {
|
||||
json: json,
|
||||
json,
|
||||
filter_stack: Vec::new(),
|
||||
token_stack: Vec::new(),
|
||||
term_stack: Vec::new(),
|
||||
@ -287,14 +310,13 @@ impl JsonValueFilter {
|
||||
Some(self.filter_stack.push(vf))
|
||||
});
|
||||
} else {
|
||||
let v: &Value = self.json.as_ref().borrow();
|
||||
self.filter_stack.push({
|
||||
ValueFilter::new(v.clone(), false, from_current)
|
||||
ValueFilter::new(self.json.clone(), false, from_current)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn replace_filter_stack(&mut self, v: Value, is_leaves: bool) {
|
||||
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 {
|
||||
@ -312,17 +334,17 @@ impl JsonValueFilter {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_value(&self) -> &Value {
|
||||
pub fn current_value(&self) -> RefValueWrapper {
|
||||
match self.filter_stack.last() {
|
||||
Some(v) => &v.vw.get_val(),
|
||||
_ => &Value::Null
|
||||
Some(v) => v.vw.get_val().clone(),
|
||||
_ => RefValue::Null.into()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn take_value(&mut self) -> Value {
|
||||
pub fn take_value(&mut self) -> RefValueWrapper {
|
||||
match self.filter_stack.last_mut() {
|
||||
Some(v) => v.vw.get_val_mut().take(),
|
||||
_ => Value::Null
|
||||
Some(v) => v.vw.get_val().clone(),
|
||||
_ => RefValue::Null.into()
|
||||
}
|
||||
}
|
||||
|
||||
@ -331,25 +353,28 @@ impl JsonValueFilter {
|
||||
|
||||
match self.filter_stack.last_mut() {
|
||||
Some(ref mut vf) if vf.vw.is_array() && vf.vw.is_leaves() => {
|
||||
if let Value::Array(mut val) = vf.vw.get_val_mut().take() {
|
||||
let mut ret = Vec::new();
|
||||
for mut v in &mut val {
|
||||
let mut ret = Vec::new();
|
||||
if let RefValue::Array(val) = vf.vw.get_val().get_data_ref() {
|
||||
for mut v in val {
|
||||
for i in &indices {
|
||||
let v = i.take_value(v);
|
||||
let v = i.take_value(&v.into());
|
||||
if !v.is_null() {
|
||||
ret.push(v);
|
||||
ret.push(v.clone_data());
|
||||
}
|
||||
}
|
||||
}
|
||||
vf.vw.replace(Value::Array(ret));
|
||||
}
|
||||
vf.vw.replace(RefValue::Array(ret).into());
|
||||
}
|
||||
Some(ref mut vf) if vf.vw.is_array() && !vf.vw.is_leaves() => {
|
||||
let ret = indices.into_iter()
|
||||
.map(|i| i.take_value(vf.vw.get_val_mut()))
|
||||
.filter(|v| !v.is_null())
|
||||
.collect();
|
||||
vf.vw.replace(Value::Array(ret));
|
||||
let mut ret = Vec::new();
|
||||
for i in indices {
|
||||
let wrapper = i.take_value(&vf.vw.get_val());
|
||||
if !wrapper.is_null() {
|
||||
ret.push(wrapper.clone_data());
|
||||
}
|
||||
}
|
||||
vf.vw.replace(RefValue::Array(ret).into());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -358,43 +383,51 @@ impl JsonValueFilter {
|
||||
fn token_range(&mut self, from: Option<isize>, to: Option<isize>) {
|
||||
self.token_stack.pop();
|
||||
|
||||
fn _from_to<F: ArrayIndex>(from: Option<F>, to: Option<F>, val: &Value) -> (usize, usize) {
|
||||
fn _from_to<F: ArrayIndex>(from: Option<F>, to: Option<F>, 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 Value::Array(v) = val { v.len() } else { 0 }
|
||||
_ => {
|
||||
if let RefValue::Array(v) = val.get_data_ref() {
|
||||
v.len()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
};
|
||||
(from, to)
|
||||
}
|
||||
|
||||
fn _range(from: usize, to: usize, v: &mut Value) -> Vec<Value> {
|
||||
fn _range(from: usize, to: usize, v: &RefValueWrapper) -> Vec<TypeRefValue> {
|
||||
trace!("range - {}:{}", from, to);
|
||||
|
||||
(from..to).into_iter()
|
||||
.map(|i| i.take_value(v))
|
||||
.filter(|v| !v.is_null())
|
||||
.map(|v| v.clone_data())
|
||||
.collect()
|
||||
}
|
||||
|
||||
match self.filter_stack.last_mut() {
|
||||
Some(ref mut vf) if vf.vw.is_array() && vf.vw.is_leaves() => {
|
||||
if let Value::Array(mut vec) = vf.vw.get_val_mut().take() {
|
||||
let mut buf = Vec::new();
|
||||
for mut item in &mut vec {
|
||||
let (from, to) = _from_to(from, to, item);
|
||||
let mut v: Vec<Value> = _range(from, to, item);
|
||||
let mut buf = Vec::new();
|
||||
if let RefValue::Array(vec) = vf.vw.get_val().get_data_ref() {
|
||||
for mut v in vec {
|
||||
let wrapper = v.into();
|
||||
let (from, to) = _from_to(from, to, &wrapper);
|
||||
let mut v: Vec<TypeRefValue> = _range(from, to, &wrapper);
|
||||
buf.append(&mut v);
|
||||
}
|
||||
vf.vw.replace(Value::Array(buf));
|
||||
}
|
||||
vf.vw.replace(RefValue::Array(buf).into());
|
||||
}
|
||||
Some(ref mut vf) if vf.vw.is_array() && !vf.vw.is_leaves() => {
|
||||
let (from, to) = _from_to(from, to, vf.vw.get_val());
|
||||
let v: Vec<Value> = _range(from, to, vf.vw.get_val_mut());
|
||||
vf.vw.replace(Value::Array(v));
|
||||
let (from, to) = _from_to(from, to, &vf.vw.get_val());
|
||||
let vec: Vec<TypeRefValue> = _range(from, to, vf.vw.get_val());
|
||||
vf.vw.replace(RefValue::Array(vec).into());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -449,19 +482,19 @@ impl JsonValueFilter {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Some(TermContext::Json(_, mut vw)) => {
|
||||
self.replace_filter_stack(vw.get_val_mut().take(), vw.is_leaves());
|
||||
Some(TermContext::Json(_, vw)) => {
|
||||
self.replace_filter_stack(vw.get_val().clone(), vw.is_leaves());
|
||||
}
|
||||
_ => {
|
||||
match self.filter_stack.pop() {
|
||||
Some(mut vf) => {
|
||||
let is_leaves = vf.vw.is_leaves();
|
||||
match vf.vw.get_val_mut() {
|
||||
Value::Null | Value::Bool(false) => {
|
||||
self.replace_filter_stack(Value::Null, is_leaves);
|
||||
match vf.vw.get_val().get_data_ref() {
|
||||
RefValue::Null | RefValue::Bool(false) => {
|
||||
self.replace_filter_stack(RefValue::Null.into(), is_leaves);
|
||||
}
|
||||
other => {
|
||||
self.replace_filter_stack(other.take(), is_leaves);
|
||||
_ => {
|
||||
self.replace_filter_stack(vf.vw.get_val().clone(), is_leaves);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -479,18 +512,18 @@ impl JsonValueFilter {
|
||||
trace!("right {:?}", right);
|
||||
|
||||
if left.is_some() && right.is_some() {
|
||||
let mut left = left.unwrap();
|
||||
let mut right = right.unwrap();
|
||||
let left = left.unwrap();
|
||||
let right = right.unwrap();
|
||||
|
||||
let tc = match ft {
|
||||
FilterToken::Equal => left.eq(&mut right),
|
||||
FilterToken::NotEqual => left.ne(&mut right),
|
||||
FilterToken::Greater => left.gt(&mut right),
|
||||
FilterToken::GreaterOrEqual => left.ge(&mut right),
|
||||
FilterToken::Little => left.lt(&mut right),
|
||||
FilterToken::LittleOrEqual => left.le(&mut right),
|
||||
FilterToken::And => left.and(&mut right),
|
||||
FilterToken::Or => left.or(&mut right),
|
||||
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);
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
use indexmap::map::IndexMap;
|
||||
|
||||
use serde_json::Value;
|
||||
use ref_value::*;
|
||||
|
||||
use super::cmp::*;
|
||||
use super::term::*;
|
||||
@ -8,12 +7,12 @@ use super::value_filter::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ValueWrapper {
|
||||
val: Value,
|
||||
val: RefValueWrapper,
|
||||
is_leaves: bool,
|
||||
}
|
||||
|
||||
impl ValueWrapper {
|
||||
pub fn new(val: Value, leaves: bool) -> Self {
|
||||
pub fn new(val: RefValueWrapper, leaves: bool) -> Self {
|
||||
ValueWrapper { val, is_leaves: leaves }
|
||||
}
|
||||
|
||||
@ -25,7 +24,7 @@ impl ValueWrapper {
|
||||
self.is_leaves = is_leaves;
|
||||
}
|
||||
|
||||
pub fn cmp(&mut self, other: &mut ValueWrapper, cmp_type: CmpType) -> TermContext {
|
||||
pub fn cmp(&self, other: &ValueWrapper, cmp_type: CmpType) -> TermContext {
|
||||
match cmp_type {
|
||||
CmpType::Eq => {
|
||||
TermContext::Json(None, self.intersect(other))
|
||||
@ -39,24 +38,19 @@ impl ValueWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp_with_term<F: PrivCmp>(val: &Value, et: &ExprTerm, cmp_fn: &F, default: bool, reverse: bool) -> bool {
|
||||
match val {
|
||||
Value::Bool(ref v1) => {
|
||||
fn cmp_with_term<F: PrivCmp>(val: &RefValueWrapper, et: &ExprTerm, cmp_fn: &F, default: bool, reverse: bool) -> bool {
|
||||
match val.get_data_ref() {
|
||||
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
|
||||
}
|
||||
}
|
||||
Value::Number(ref v1) => match v1.as_f64() {
|
||||
Some(ref v1) => {
|
||||
match et {
|
||||
ExprTerm::Number(v2) => if reverse { cmp_fn.cmp_f64(v2, v1) } else { cmp_fn.cmp_f64(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
|
||||
},
|
||||
Value::String(ref v1) => {
|
||||
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
|
||||
@ -66,12 +60,12 @@ impl ValueWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
fn take_object_in_array<F: PrivCmp>(&mut self, key: &String, et: &ExprTerm, cmp: &F, reverse: bool) -> Option<Self> {
|
||||
fn _filter_with_object<F: Fn(&Value) -> bool>(v: &&mut Value, key: &String, fun: F) -> bool {
|
||||
match &v {
|
||||
Value::Object(map) => {
|
||||
fn take_object_in_array<F: PrivCmp>(&self, key: &String, et: &ExprTerm, cmp: &F, reverse: bool) -> Option<Self> {
|
||||
fn _filter_with_object<F: Fn(&RefValueWrapper) -> bool>(v: &RefValueWrapper, key: &String, fun: F) -> bool {
|
||||
match v.get_data_ref() {
|
||||
RefValue::Object(map) => {
|
||||
match map.get(key) {
|
||||
Some(vv) => fun(vv),
|
||||
Some(val) => fun(&val.into()),
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
@ -79,21 +73,24 @@ impl ValueWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
match self.val.take() {
|
||||
Value::Array(mut vec) => {
|
||||
let mut ret: Vec<Value> = vec.iter_mut()
|
||||
.filter(|v| {
|
||||
_filter_with_object(v, key, |vv| Self::cmp_with_term(vv, et, cmp, false, reverse))
|
||||
})
|
||||
.map(|v| v.take())
|
||||
.collect();
|
||||
Some(ValueWrapper::new(Value::Array(ret), false))
|
||||
match self.val.get_data_ref() {
|
||||
RefValue::Array(vec) => {
|
||||
let mut ret = Vec::new();
|
||||
for v in vec {
|
||||
if _filter_with_object(&v.into(), key, |vv| {
|
||||
Self::cmp_with_term(vv, et, cmp, false, reverse)
|
||||
}) {
|
||||
ret.push(v.clone());
|
||||
}
|
||||
}
|
||||
|
||||
Some(ValueWrapper::new(RefValue::Array(ret).into(), false))
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn take_with_key_type<F: PrivCmp>(&mut self, key: &Option<ValueFilterKey>, et: &ExprTerm, cmp: &F, reverse: bool) -> Option<Self> {
|
||||
fn take_with_key_type<F: PrivCmp>(&self, key: &Option<ValueFilterKey>, et: &ExprTerm, cmp: &F, reverse: bool) -> Option<Self> {
|
||||
match key {
|
||||
Some(ValueFilterKey::String(key)) => {
|
||||
self.take_object_in_array(key, et, cmp, reverse)
|
||||
@ -102,23 +99,25 @@ impl ValueWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn take_with<F: PrivCmp>(&mut self, key: &Option<ValueFilterKey>, et: &ExprTerm, cmp: F, reverse: bool) -> Self {
|
||||
pub fn take_with<F: PrivCmp>(&self, key: &Option<ValueFilterKey>, et: &ExprTerm, cmp: F, reverse: bool) -> Self {
|
||||
match self.take_with_key_type(key, et, &cmp, reverse) {
|
||||
Some(vw) => vw,
|
||||
_ => {
|
||||
match self.val.take() {
|
||||
Value::Array(mut vec) => {
|
||||
let mut ret = vec.iter_mut()
|
||||
.filter(|v| Self::cmp_with_term(&v, et, &cmp, false, reverse))
|
||||
.map(|v| v.take())
|
||||
.collect();
|
||||
ValueWrapper::new(Value::Array(ret), false)
|
||||
match self.val.get_data_ref() {
|
||||
RefValue::Array(vec) => {
|
||||
let mut ret = Vec::new();
|
||||
for v in vec {
|
||||
if Self::cmp_with_term(&v.into(), et, &cmp, false, reverse) {
|
||||
ret.push(v.clone());
|
||||
}
|
||||
}
|
||||
ValueWrapper::new(RefValue::Array(ret).into(), false)
|
||||
}
|
||||
other => {
|
||||
if Self::cmp_with_term(&other, et, &cmp, false, reverse) {
|
||||
ValueWrapper::new(other, false)
|
||||
_ => {
|
||||
if Self::cmp_with_term(&self.val, et, &cmp, false, reverse) {
|
||||
ValueWrapper::new(self.val.clone(), false)
|
||||
} else {
|
||||
ValueWrapper::new(Value::Null, false)
|
||||
ValueWrapper::new(RefValue::Null.into(), false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -126,24 +125,25 @@ impl ValueWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace(&mut self, val: Value) {
|
||||
let is_null = match &val {
|
||||
Value::Array(v) => if v.is_empty() { true } else { false },
|
||||
Value::Object(m) => if m.is_empty() { true } else { false },
|
||||
pub fn replace(&mut self, val: RefValueWrapper) {
|
||||
let is_null = match val.get_data_ref() {
|
||||
RefValue::Array(v) => if v.is_empty() { true } else { false },
|
||||
RefValue::Object(m) => if m.is_empty() { true } else { false },
|
||||
_ => val.is_null()
|
||||
};
|
||||
self.val = if is_null { Value::Null } else { val };
|
||||
self.val = if is_null {
|
||||
let v = RefValueWrapper::wrap(RefValue::Null);
|
||||
RefValueWrapper::new(v)
|
||||
} else {
|
||||
val
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_val(&self) -> &Value {
|
||||
pub fn get_val(&self) -> &RefValueWrapper {
|
||||
&self.val
|
||||
}
|
||||
|
||||
pub fn get_val_mut(&mut self) -> &mut Value {
|
||||
&mut self.val
|
||||
}
|
||||
|
||||
pub fn clone_val(&self) -> Value {
|
||||
pub fn clone_val(&self) -> RefValueWrapper {
|
||||
self.val.clone()
|
||||
}
|
||||
|
||||
@ -151,150 +151,161 @@ impl ValueWrapper {
|
||||
self.val.is_array()
|
||||
}
|
||||
|
||||
fn uuid(v: &Value) -> String {
|
||||
fn _fn(v: &Value) -> String {
|
||||
match v {
|
||||
Value::Null => "null".to_string(),
|
||||
Value::String(v) => v.to_string(),
|
||||
Value::Bool(v) => v.to_string(),
|
||||
Value::Number(v) => v.to_string(),
|
||||
Value::Array(v) => {
|
||||
v.iter().enumerate()
|
||||
.map(|(i, v)| { format!("{}{}", i, _fn(v)) })
|
||||
.collect()
|
||||
fn uuid(v: &RefValueWrapper) -> String {
|
||||
fn _fn(v: &RefValueWrapper, acc: &mut String) {
|
||||
match v.get_data_ref() {
|
||||
RefValue::Null => acc.push_str("null"),
|
||||
RefValue::String(v) => acc.push_str(v),
|
||||
RefValue::Bool(v) => acc.push_str(if *v { "true" } else { "false" }),
|
||||
RefValue::Number(v) => acc.push_str(&*v.to_string()),
|
||||
RefValue::Array(v) => {
|
||||
for (i, v) in v.iter().enumerate() {
|
||||
acc.push_str(&*i.to_string());
|
||||
_fn(&v.into(), acc);
|
||||
}
|
||||
}
|
||||
Value::Object(v) => {
|
||||
v.into_iter().map(|(k, v)| { format!("{}{}", k, _fn(v)) }).collect()
|
||||
RefValue::Object(ref v) => {
|
||||
for (k, v) in v.into_iter() {
|
||||
acc.push_str(&*k.to_string());
|
||||
_fn(&v.into(), acc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_fn(v)
|
||||
let mut acc = String::new();
|
||||
_fn(v, &mut acc);
|
||||
acc
|
||||
}
|
||||
|
||||
fn into_map(&mut self) -> IndexMap<String, Value> {
|
||||
fn into_map(&self) -> IndexMap<String, RefValueWrapper> {
|
||||
let mut map = IndexMap::new();
|
||||
match &mut self.val {
|
||||
Value::Array(v1) => {
|
||||
match self.val.get_data_ref() {
|
||||
RefValue::Array(ref v1) => {
|
||||
for v in v1 {
|
||||
map.insert(Self::uuid(v), v.take());
|
||||
let wrapper = v.into();
|
||||
let key = Self::uuid(&wrapper);
|
||||
map.insert(key, wrapper);
|
||||
}
|
||||
}
|
||||
other => {
|
||||
map.insert(Self::uuid(other), other.take());
|
||||
_ => {
|
||||
map.insert(Self::uuid(&self.val), self.val.clone());
|
||||
}
|
||||
}
|
||||
map
|
||||
}
|
||||
|
||||
pub fn except(&mut self, other: &mut Self) -> Self {
|
||||
pub fn except(&self, other: &Self) -> Self {
|
||||
let map = self.into_map();
|
||||
let mut ret: IndexMap<String, Value> = IndexMap::new();
|
||||
match &mut other.val {
|
||||
Value::Array(v1) => {
|
||||
let mut ret: IndexMap<String, RefValueWrapper> = IndexMap::new();
|
||||
match other.val.get_data_ref() {
|
||||
RefValue::Array(ref v1) => {
|
||||
for v in v1 {
|
||||
let key = Self::uuid(v);
|
||||
let wrapper = v.into();
|
||||
let key = Self::uuid(&wrapper);
|
||||
if !map.contains_key(&key) {
|
||||
ret.insert(key, v.take());
|
||||
ret.insert(key, wrapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
other => {
|
||||
let key = Self::uuid(other);
|
||||
_ => {
|
||||
let key = Self::uuid(&other.val);
|
||||
if !map.contains_key(&key) {
|
||||
ret.insert(key, other.take());
|
||||
ret.insert(key, other.val.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let v = ret.values_mut().into_iter().map(|v| v.take()).collect();
|
||||
ValueWrapper::new(v, false)
|
||||
let vec = ret.values().into_iter().map(|v| v.clone_data()).collect();
|
||||
ValueWrapper::new(RefValue::Array(vec).into(), false)
|
||||
}
|
||||
|
||||
pub fn intersect(&mut self, other: &mut Self) -> Self {
|
||||
pub fn intersect(&self, other: &Self) -> Self {
|
||||
let map = self.into_map();
|
||||
let mut ret: IndexMap<String, Value> = IndexMap::new();
|
||||
match &mut other.val {
|
||||
Value::Array(v1) => {
|
||||
let mut ret: IndexMap<String, RefValueWrapper> = IndexMap::new();
|
||||
match other.val.get_data_ref() {
|
||||
RefValue::Array(ref v1) => {
|
||||
for v in v1 {
|
||||
let key = Self::uuid(v);
|
||||
let wrapper = v.into();
|
||||
let key = Self::uuid(&wrapper);
|
||||
if map.contains_key(&key) {
|
||||
ret.insert(key, v.take());
|
||||
ret.insert(key, wrapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
other => {
|
||||
let key = Self::uuid(other);
|
||||
_ => {
|
||||
let key = Self::uuid(&other.val);
|
||||
if map.contains_key(&key) {
|
||||
ret.insert(key, other.take());
|
||||
ret.insert(key, other.val.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let v = ret.values_mut().into_iter().map(|v| v.take()).collect();
|
||||
ValueWrapper::new(v, false)
|
||||
let vec = ret.values().into_iter().map(|v| v.clone_data()).collect();
|
||||
ValueWrapper::new(RefValue::Array(vec).into(), false)
|
||||
}
|
||||
|
||||
pub fn union(&mut self, other: &mut Self) -> Self {
|
||||
pub fn union(&self, other: &Self) -> Self {
|
||||
let mut map = self.into_map();
|
||||
match &mut other.val {
|
||||
Value::Array(v1) => {
|
||||
match other.val.get_data_ref() {
|
||||
RefValue::Array(ref v1) => {
|
||||
for v in v1 {
|
||||
let key = Self::uuid(v);
|
||||
let wrapper = v.into();
|
||||
let key = Self::uuid(&wrapper);
|
||||
if !map.contains_key(&key) {
|
||||
map.insert(key, v.take());
|
||||
map.insert(key, wrapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
other => {
|
||||
let key = Self::uuid(other);
|
||||
_ => {
|
||||
let key = Self::uuid(&other.val);
|
||||
if !map.contains_key(&key) {
|
||||
map.insert(key, other.take());
|
||||
map.insert(key, other.val.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut vw = ValueWrapper::new(Value::Null, false);
|
||||
let list: Vec<Value> = map.values_mut().into_iter().map(|val| val.take()).collect();
|
||||
vw.replace(Value::Array(list));
|
||||
let mut vw = ValueWrapper::new(RefValue::Null.into(), false);
|
||||
let list = map.values().into_iter().map(|val| val.clone_data()).collect();
|
||||
vw.replace(RefValue::Array(list).into());
|
||||
vw
|
||||
}
|
||||
|
||||
pub fn into_term(&mut self, key: &mut Option<ValueFilterKey>) -> TermContext {
|
||||
match self.val.take() {
|
||||
Value::String(s) => TermContext::Constants(ExprTerm::String(s)),
|
||||
Value::Number(n) => TermContext::Constants(ExprTerm::Number(n.as_f64().unwrap())),
|
||||
Value::Bool(b) => TermContext::Constants(ExprTerm::Bool(b)),
|
||||
other => TermContext::Json(match key {
|
||||
pub fn into_term(&self, key: &Option<ValueFilterKey>) -> TermContext {
|
||||
match self.val.get_data_ref() {
|
||||
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(other, false))
|
||||
}, ValueWrapper::new(self.val.clone(), false))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn filter(&mut self, key: &mut Option<ValueFilterKey>) -> Self {
|
||||
let v = match &mut self.val {
|
||||
Value::Array(vec) => {
|
||||
let ret = vec.iter_mut()
|
||||
.filter(|v| match key {
|
||||
Some(ValueFilterKey::String(val_key)) => {
|
||||
v.get(val_key.as_str()).is_some()
|
||||
pub fn filter(&self, key: &Option<ValueFilterKey>) -> Self {
|
||||
let v = match self.val.get_data_ref() {
|
||||
RefValue::Array(ref vec) => {
|
||||
let mut ret = Vec::new();
|
||||
for v in vec {
|
||||
if let Some(ValueFilterKey::String(k)) = key {
|
||||
let wrapper: RefValueWrapper = v.into();
|
||||
if wrapper.get(k.clone()).is_some() {
|
||||
ret.push(v.clone());
|
||||
}
|
||||
_ => false
|
||||
})
|
||||
.map(|v| v.take())
|
||||
.collect();
|
||||
Value::Array(ret)
|
||||
}
|
||||
}
|
||||
RefValue::Array(ret).into()
|
||||
}
|
||||
Value::Object(map) => {
|
||||
RefValue::Object(ref map) => {
|
||||
match key {
|
||||
Some(ValueFilterKey::String(val_key)) => match map.get_mut(val_key) {
|
||||
Some(v) => v.take(),
|
||||
_ => Value::Null
|
||||
Some(ValueFilterKey::String(k)) => match map.get(k) {
|
||||
Some(v) => v.into(),
|
||||
_ => RefValue::Null.into()
|
||||
},
|
||||
_ => Value::Null
|
||||
_ => RefValue::Null.into()
|
||||
}
|
||||
}
|
||||
other => other.take()
|
||||
_ => self.val.clone()
|
||||
};
|
||||
|
||||
ValueWrapper::new(v, false)
|
||||
|
73
src/lib.rs
73
src/lib.rs
@ -43,26 +43,26 @@
|
||||
//! "expensive": 10
|
||||
//! });
|
||||
//!
|
||||
//! let mut reader = jsonpath::reader(json_obj);
|
||||
//! let mut selector = jsonpath::selector(json_obj);
|
||||
//!
|
||||
//! //
|
||||
//! // $.store.book[*].author
|
||||
//! //
|
||||
//! let json = reader("$.store.book[*].author").unwrap();
|
||||
//! let json = selector("$.store.book[*].author").unwrap();
|
||||
//! let ret = json!(["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]);
|
||||
//! assert_eq!(json, ret);
|
||||
//!
|
||||
//! //
|
||||
//! // $..author
|
||||
//! //
|
||||
//! let json = reader("$..author").unwrap();
|
||||
//! let json = selector("$..author").unwrap();
|
||||
//! let ret = json!(["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]);
|
||||
//! assert_eq!(json, ret);
|
||||
//!
|
||||
//! //
|
||||
//! // $.store.*
|
||||
//! //
|
||||
//! let json = reader("$.store.*").unwrap();
|
||||
//! let json = selector("$.store.*").unwrap();
|
||||
//! let ret = json!([
|
||||
//! [
|
||||
//! {"category" : "reference", "author" : "Nigel Rees","title" : "Sayings of the Century", "price" : 8.95},
|
||||
@ -77,14 +77,14 @@
|
||||
//! //
|
||||
//! // $.store..price
|
||||
//! //
|
||||
//! let json = reader("$.store..price").unwrap();
|
||||
//! let json = selector("$.store..price").unwrap();
|
||||
//! let ret = json!([8.95, 12.99, 8.99, 22.99, 19.95]);
|
||||
//! assert_eq!(ret, json);
|
||||
//!
|
||||
//! //
|
||||
//! // $..book[2]
|
||||
//! //
|
||||
//! let json = reader("$..book[2]").unwrap();
|
||||
//! let json = selector("$..book[2]").unwrap();
|
||||
//! let ret = json!([{
|
||||
//! "category" : "fiction",
|
||||
//! "author" : "Herman Melville",
|
||||
@ -97,7 +97,7 @@
|
||||
//! //
|
||||
//! // $..book[-2]
|
||||
//! //
|
||||
//! let json = reader("$..book[-2]").unwrap();
|
||||
//! let json = selector("$..book[-2]").unwrap();
|
||||
//! let ret = json!([{
|
||||
//! "category" : "fiction",
|
||||
//! "author" : "Herman Melville",
|
||||
@ -110,7 +110,7 @@
|
||||
//! //
|
||||
//! // $..book[0,1]
|
||||
//! //
|
||||
//! let json = reader("$..book[0,1]").unwrap();
|
||||
//! 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}
|
||||
@ -120,7 +120,7 @@
|
||||
//! //
|
||||
//! // $..book[:2]
|
||||
//! //
|
||||
//! let json = reader("$..book[:2]").unwrap();
|
||||
//! 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}
|
||||
@ -130,7 +130,7 @@
|
||||
//! //
|
||||
//! // $..book[2:]
|
||||
//! //
|
||||
//! let json = reader("$..book[2:]").unwrap();
|
||||
//! 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}
|
||||
@ -140,7 +140,7 @@
|
||||
//! //
|
||||
//! // $..book[?(@.isbn)]
|
||||
//! //
|
||||
//! let json = reader("$..book[?(@.isbn)]").unwrap();
|
||||
//! 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}
|
||||
@ -150,7 +150,7 @@
|
||||
//! //
|
||||
//! // $.store.book[?(@.price < 10)]
|
||||
//! //
|
||||
//! let json = reader("$.store.book[?(@.price < 10)]").unwrap();
|
||||
//! 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}
|
||||
@ -175,14 +175,17 @@ extern crate indexmap;
|
||||
pub mod parser;
|
||||
#[doc(hidden)]
|
||||
pub mod filter;
|
||||
#[doc(hidden)]
|
||||
pub mod ref_value;
|
||||
|
||||
use parser::parser::*;
|
||||
use filter::value_filter::*;
|
||||
|
||||
use std::result;
|
||||
use std::rc::Rc;
|
||||
use serde_json::Value;
|
||||
|
||||
use ref_value::*;
|
||||
|
||||
type Result = result::Result<Value, String>;
|
||||
|
||||
/// # Read multiple Json multiple times with the same JsonPath
|
||||
@ -223,9 +226,9 @@ pub fn compile<'a>(path: &'a str) -> impl FnMut(Value) -> Result + 'a {
|
||||
move |json| {
|
||||
match &node {
|
||||
Ok(n) => {
|
||||
let mut jf = JsonValueFilter::new_from_value(Rc::new(Box::new(json)));
|
||||
let mut jf = JsonValueFilter::new_from_value(json.into());
|
||||
jf.visit(n.clone());
|
||||
Ok(jf.take_value())
|
||||
Ok(jf.take_value().into_value())
|
||||
}
|
||||
Err(e) => Err(e.clone())
|
||||
}
|
||||
@ -246,26 +249,31 @@ pub fn compile<'a>(path: &'a str) -> impl FnMut(Value) -> Result + 'a {
|
||||
/// "friends": [{"id": 0},{"id": 1}]
|
||||
/// });
|
||||
///
|
||||
/// let mut reader = jsonpath::reader(json_obj);
|
||||
/// let mut selector = jsonpath::selector(json_obj);
|
||||
///
|
||||
/// let json = reader("$..friends[0]").unwrap();
|
||||
/// let json = selector("$..friends[0]").unwrap();
|
||||
/// let ret = json!([ {"id": 0}, {"id": 0} ]);
|
||||
/// assert_eq!(json, ret);
|
||||
///
|
||||
/// let json = reader("$..friends[1]").unwrap();
|
||||
/// let json = selector("$..friends[1]").unwrap();
|
||||
/// let ret = json!([ {"id": 1}, {"id": 1} ]);
|
||||
/// assert_eq!(json, ret);
|
||||
/// ```
|
||||
pub fn reader(json: Value) -> impl FnMut(&str) -> Result {
|
||||
let n = Rc::new(Box::new(json));
|
||||
pub fn selector(json: Value) -> impl FnMut(&str) -> Result {
|
||||
let wrapper: RefValueWrapper = json.into();
|
||||
move |path: &str| {
|
||||
let mut jf = JsonValueFilter::new_from_value(n.clone());
|
||||
let mut jf = JsonValueFilter::new_from_value(wrapper.clone());
|
||||
let mut parser = Parser::new(path);
|
||||
parser.parse(&mut jf)?;
|
||||
Ok(jf.take_value())
|
||||
Ok(jf.take_value().into_value())
|
||||
}
|
||||
}
|
||||
|
||||
/// # Read the same Json multiple times using different JsonPath - Deprecated. use selector
|
||||
pub fn reader(json: Value) -> impl FnMut(&str) -> Result {
|
||||
selector(json)
|
||||
}
|
||||
|
||||
/// # Read Json using JsonPath
|
||||
///
|
||||
/// ```rust
|
||||
@ -278,15 +286,20 @@ pub fn reader(json: Value) -> impl FnMut(&str) -> Result {
|
||||
/// },
|
||||
/// "friends": [{"id": 0}, {"id": 1}]
|
||||
/// });
|
||||
/// let json = jsonpath::read(json_obj, "$..friends[0]").unwrap();
|
||||
/// let json = jsonpath::select(json_obj, "$..friends[0]").unwrap();
|
||||
/// let ret = json!([ {"id": 0}, {"id": 0} ]);
|
||||
/// assert_eq!(json, ret);
|
||||
/// ```
|
||||
pub fn read(json: Value, path: &str) -> Result {
|
||||
let mut jf = JsonValueFilter::new_from_value(Rc::new(Box::new(json)));
|
||||
pub fn select(json: Value, path: &str) -> Result {
|
||||
let mut jf = JsonValueFilter::new_from_value(json.into());
|
||||
let mut parser = Parser::new(path);
|
||||
parser.parse(&mut jf)?;
|
||||
Ok(jf.take_value())
|
||||
Ok(jf.take_value().into_value())
|
||||
}
|
||||
|
||||
/// # Read Json using JsonPath - Deprecated. use select
|
||||
pub fn read(json: Value, path: &str) -> Result {
|
||||
select(json, path)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -323,9 +336,9 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reader() {
|
||||
fn selector() {
|
||||
let json_obj = read_json("./benches/data_obj.json");
|
||||
let mut reader = super::reader(json_obj);
|
||||
let mut reader = super::selector(json_obj);
|
||||
let json = reader("$..friends[2]").unwrap();
|
||||
let ret = json!([
|
||||
{"id": 2,"name": "Gray Berry"},
|
||||
@ -342,9 +355,9 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read() {
|
||||
fn select() {
|
||||
let json_obj = read_json("./benches/example.json");
|
||||
let json = super::read(json_obj, "$..book[2]").unwrap();
|
||||
let json = super::select(json_obj, "$..book[2]").unwrap();
|
||||
let ret = json!([{
|
||||
"category" : "fiction",
|
||||
"author" : "Herman Melville",
|
||||
|
@ -13,25 +13,21 @@ type Result<T> = result::Result<T, String>;
|
||||
mod utils {
|
||||
use std::result;
|
||||
|
||||
pub fn vec_to_int<F>(vec: &Vec<char>, msg_handler: F) -> result::Result<isize, String>
|
||||
pub fn string_to_isize<F>(string: &String, msg_handler: F) -> result::Result<isize, String>
|
||||
where F: Fn() -> String {
|
||||
match vec.iter().map(|c| *c).collect::<String>().as_str().parse::<isize>() {
|
||||
match string.as_str().parse::<isize>() {
|
||||
Ok(n) => Ok(n),
|
||||
_ => Err(msg_handler())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vec_to_float<F>(vec: &Vec<char>, msg_handler: F) -> result::Result<f64, String>
|
||||
pub fn string_to_f64<F>(string: &String, msg_handler: F) -> result::Result<f64, String>
|
||||
where F: Fn() -> String {
|
||||
match vec.iter().map(|c| *c).collect::<String>().as_str().parse::<f64>() {
|
||||
match string.as_str().parse::<f64>() {
|
||||
Ok(n) => Ok(n),
|
||||
_ => Err(msg_handler())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vec_to_string(vec: &Vec<char>) -> String {
|
||||
vec.iter().map(|c| *c).collect::<String>()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
@ -236,7 +232,7 @@ impl<'a> Parser<'a> {
|
||||
debug!("#key");
|
||||
match self.tokenizer.next_token() {
|
||||
Ok(Token::Key(_, v)) => {
|
||||
Ok(self.node(ParseToken::Key(utils::vec_to_string(&v))))
|
||||
Ok(self.node(ParseToken::Key(v)))
|
||||
}
|
||||
_ => {
|
||||
Err(self.tokenizer.err_msg())
|
||||
@ -247,9 +243,9 @@ impl<'a> Parser<'a> {
|
||||
fn array_quota_value(&mut self) -> Result<Node> {
|
||||
debug!("#array_quota_value");
|
||||
match self.tokenizer.next_token() {
|
||||
Ok(Token::SingleQuoted(_, ref vec))
|
||||
| Ok(Token::DoubleQuoted(_, ref vec)) => {
|
||||
Ok(self.node(ParseToken::Key(utils::vec_to_string(vec))))
|
||||
Ok(Token::SingleQuoted(_, val))
|
||||
| Ok(Token::DoubleQuoted(_, val)) => {
|
||||
Ok(self.node(ParseToken::Key(val)))
|
||||
}
|
||||
Err(TokenError::Eof) => {
|
||||
Ok(self.node(ParseToken::Eof))
|
||||
@ -299,9 +295,8 @@ impl<'a> Parser<'a> {
|
||||
fn array_value_key(&mut self) -> Result<Node> {
|
||||
debug!("#array_value_key");
|
||||
match self.tokenizer.next_token() {
|
||||
Ok(Token::Key(pos, ref vec)) => {
|
||||
let digit = utils::vec_to_int(vec,
|
||||
|| self.tokenizer.err_msg_with_pos(pos))?;
|
||||
Ok(Token::Key(pos, ref val)) => {
|
||||
let digit = utils::string_to_isize(val, || self.tokenizer.err_msg_with_pos(pos))?;
|
||||
self.eat_whitespace();
|
||||
|
||||
match self.tokenizer.peek_token() {
|
||||
@ -357,9 +352,8 @@ impl<'a> Parser<'a> {
|
||||
self.eat_token();
|
||||
self.eat_whitespace();
|
||||
match self.tokenizer.next_token() {
|
||||
Ok(Token::Key(pos, ref vec)) => {
|
||||
let digit = utils::vec_to_int(vec,
|
||||
|| self.tokenizer.err_msg_with_pos(pos))?;
|
||||
Ok(Token::Key(pos, ref val)) => {
|
||||
let digit = utils::string_to_isize(val, || self.tokenizer.err_msg_with_pos(pos))?;
|
||||
values.push(digit);
|
||||
}
|
||||
_ => {
|
||||
@ -387,9 +381,8 @@ impl<'a> Parser<'a> {
|
||||
fn range_to(&mut self) -> Result<Node> {
|
||||
debug!("#range_to");
|
||||
match self.tokenizer.next_token() {
|
||||
Ok(Token::Key(pos, ref vec)) => {
|
||||
let digit = utils::vec_to_int(vec,
|
||||
|| self.tokenizer.err_msg_with_pos(pos))?;
|
||||
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))))
|
||||
}
|
||||
_ => {
|
||||
@ -401,9 +394,8 @@ impl<'a> Parser<'a> {
|
||||
fn range(&mut self, num: isize) -> Result<Node> {
|
||||
debug!("#range");
|
||||
match self.tokenizer.next_token() {
|
||||
Ok(Token::Key(pos, ref vec)) => {
|
||||
let digit = utils::vec_to_int(vec,
|
||||
|| self.tokenizer.err_msg_with_pos(pos))?;
|
||||
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))))
|
||||
}
|
||||
_ => {
|
||||
@ -505,14 +497,13 @@ impl<'a> Parser<'a> {
|
||||
fn term_num(&mut self) -> Result<Node> {
|
||||
debug!("#term_num");
|
||||
match self.tokenizer.next_token() {
|
||||
Ok(Token::Key(pos, mut vec)) => {
|
||||
Ok(Token::Key(pos, val)) => {
|
||||
match self.tokenizer.peek_token() {
|
||||
Ok(Token::Dot(_)) => {
|
||||
self.term_num_float(&mut vec)
|
||||
self.term_num_float(val.as_str())
|
||||
}
|
||||
_ => {
|
||||
let number = utils::vec_to_float(&vec,
|
||||
|| self.tokenizer.err_msg_with_pos(pos))?;
|
||||
let number = utils::string_to_f64(&val, || self.tokenizer.err_msg_with_pos(pos))?;
|
||||
Ok(self.node(ParseToken::Number(number)))
|
||||
}
|
||||
}
|
||||
@ -526,17 +517,16 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn term_num_float(&mut self, mut num: &mut Vec<char>) -> Result<Node> {
|
||||
fn term_num_float(&mut self, mut num: &str) -> Result<Node> {
|
||||
debug!("#term_num_float");
|
||||
self.eat_token();
|
||||
match self.tokenizer.next_token() {
|
||||
Ok(Token::Key(pos, mut frac)) => {
|
||||
let mut f = vec![];
|
||||
f.append(&mut num);
|
||||
Ok(Token::Key(pos, frac)) => {
|
||||
let mut f = String::new();
|
||||
f.push_str(&mut num);
|
||||
f.push('.');
|
||||
f.append(&mut frac);
|
||||
let number = utils::vec_to_float(&f,
|
||||
|| self.tokenizer.err_msg_with_pos(pos))?;
|
||||
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)))
|
||||
}
|
||||
_ => {
|
||||
|
@ -23,19 +23,23 @@ impl<'a> PathReader<'a> {
|
||||
Ok((self.pos + ch.len_utf8(), ch))
|
||||
}
|
||||
|
||||
pub fn take_while<F>(&mut self, fun: F) -> result::Result<(usize, Vec<char>), ReaderError>
|
||||
pub fn take_while<F>(&mut self, fun: F) -> result::Result<(usize, String), ReaderError>
|
||||
where
|
||||
F: Fn(&char) -> bool
|
||||
{
|
||||
let vec: Vec<char> = self.input.chars()
|
||||
.by_ref()
|
||||
.take_while(fun)
|
||||
.collect();
|
||||
let mut char_len: usize = 0;
|
||||
let mut ret = String::new();
|
||||
for c in self.input.chars().by_ref() {
|
||||
if !fun(&c) {
|
||||
break;
|
||||
}
|
||||
char_len += c.len_utf8();
|
||||
ret.push(c);
|
||||
}
|
||||
|
||||
let char_len: usize = vec.iter().by_ref().map(|c| c.len_utf8()).sum();
|
||||
self.pos += char_len;
|
||||
self.input = &self.input[char_len..];
|
||||
Ok((self.pos, vec))
|
||||
Ok((self.pos, ret))
|
||||
}
|
||||
|
||||
pub fn next_char(&mut self) -> result::Result<(usize, char), ReaderError> {
|
||||
|
@ -75,9 +75,9 @@ pub enum Token {
|
||||
Split(usize),
|
||||
OpenParenthesis(usize),
|
||||
CloseParenthesis(usize),
|
||||
Key(usize, Vec<char>),
|
||||
DoubleQuoted(usize, Vec<char>),
|
||||
SingleQuoted(usize, Vec<char>),
|
||||
Key(usize, String),
|
||||
DoubleQuoted(usize, String),
|
||||
SingleQuoted(usize, String),
|
||||
Equal(usize),
|
||||
GreaterOrEqual(usize),
|
||||
Greater(usize),
|
||||
@ -153,15 +153,15 @@ impl<'a> Tokenizer<'a> {
|
||||
}
|
||||
|
||||
fn single_quota(&mut self, pos: usize, ch: char) -> result::Result<Token, TokenError> {
|
||||
let (_, vec) = self.input.take_while(|c| *c != ch).map_err(to_token_error)?;
|
||||
let (_, val) = self.input.take_while(|c| *c != ch).map_err(to_token_error)?;
|
||||
self.input.next_char().map_err(to_token_error)?;
|
||||
Ok(Token::SingleQuoted(pos, vec))
|
||||
Ok(Token::SingleQuoted(pos, val))
|
||||
}
|
||||
|
||||
fn double_quota(&mut self, pos: usize, ch: char) -> result::Result<Token, TokenError> {
|
||||
let (_, vec) = self.input.take_while(|c| *c != ch).map_err(to_token_error)?;
|
||||
let (_, val) = self.input.take_while(|c| *c != ch).map_err(to_token_error)?;
|
||||
self.input.next_char().map_err(to_token_error)?;
|
||||
Ok(Token::DoubleQuoted(pos, vec))
|
||||
Ok(Token::DoubleQuoted(pos, val))
|
||||
}
|
||||
|
||||
fn equal(&mut self, pos: usize, _: char) -> result::Result<Token, TokenError> {
|
||||
@ -414,9 +414,9 @@ mod tests {
|
||||
vec![
|
||||
Token::Absolute(0),
|
||||
Token::Dot(1),
|
||||
Token::Key(2, vec!['0', '1']),
|
||||
Token::Key(2, "01".to_string()),
|
||||
Token::Dot(4),
|
||||
Token::Key(5, vec!['a'])
|
||||
Token::Key(5, "a".to_string())
|
||||
]
|
||||
, Some(TokenError::Eof)
|
||||
));
|
||||
@ -449,7 +449,7 @@ mod tests {
|
||||
Token::Absolute(0),
|
||||
Token::Dot(1),
|
||||
Token::Dot(2),
|
||||
Token::Key(3, vec!['a', 'b'])
|
||||
Token::Key(3, "ab".to_string())
|
||||
]
|
||||
, Some(TokenError::Eof)
|
||||
));
|
||||
@ -460,7 +460,7 @@ mod tests {
|
||||
Token::Absolute(0),
|
||||
Token::Dot(1),
|
||||
Token::Dot(2),
|
||||
Token::Key(3, vec!['가']),
|
||||
Token::Key(3, "가".to_string()),
|
||||
Token::Whitespace(6, 0),
|
||||
Token::OpenArray(7),
|
||||
]
|
||||
@ -471,10 +471,10 @@ mod tests {
|
||||
(
|
||||
vec![
|
||||
Token::OpenArray(0),
|
||||
Token::Key(1, vec!['-', '1']),
|
||||
Token::Key(1, "-1".to_string()),
|
||||
Token::Comma(3),
|
||||
Token::Whitespace(4, 0),
|
||||
Token::Key(5, vec!['2']),
|
||||
Token::Key(5, "2".to_string()),
|
||||
Token::Whitespace(6, 0),
|
||||
Token::CloseArray(7),
|
||||
]
|
||||
@ -486,19 +486,19 @@ mod tests {
|
||||
vec![
|
||||
Token::OpenArray(0),
|
||||
Token::Whitespace(1, 0),
|
||||
Token::Key(2, vec!['1']),
|
||||
Token::Key(2, "1".to_string()),
|
||||
Token::Whitespace(3, 0),
|
||||
Token::Key(4, vec!['2']),
|
||||
Token::Key(4, "2".to_string()),
|
||||
Token::Whitespace(5, 0),
|
||||
Token::Comma(6),
|
||||
Token::Whitespace(7, 0),
|
||||
Token::Key(8, vec!['3']),
|
||||
Token::Key(8, "3".to_string()),
|
||||
Token::Whitespace(9, 0),
|
||||
Token::DoubleQuoted(10, vec!['a', 'b', 'c']),
|
||||
Token::DoubleQuoted(10, "abc".to_string()),
|
||||
Token::Whitespace(15, 0),
|
||||
Token::Split(16),
|
||||
Token::Whitespace(17, 0),
|
||||
Token::Key(18, vec!['-', '1', '0']),
|
||||
Token::Key(18, "-10".to_string()),
|
||||
Token::Whitespace(21, 0),
|
||||
Token::CloseArray(22),
|
||||
]
|
||||
@ -512,12 +512,12 @@ mod tests {
|
||||
Token::OpenParenthesis(1),
|
||||
Token::At(2),
|
||||
Token::Dot(3),
|
||||
Token::Key(4, vec!['a', '가']),
|
||||
Token::Key(4, "a가".to_string()),
|
||||
Token::Whitespace(8, 0),
|
||||
Token::Little(9),
|
||||
Token::Key(10, vec!['4', '1']),
|
||||
Token::Key(10, "41".to_string()),
|
||||
Token::Dot(12),
|
||||
Token::Key(13, vec!['0', '1']),
|
||||
Token::Key(13, "01".to_string()),
|
||||
Token::CloseParenthesis(15),
|
||||
]
|
||||
, Some(TokenError::Eof)
|
||||
@ -530,12 +530,12 @@ mod tests {
|
||||
Token::OpenParenthesis(1),
|
||||
Token::At(2),
|
||||
Token::Dot(3),
|
||||
Token::Key(4, vec!['a']),
|
||||
Token::Key(4, "a".to_string()),
|
||||
Token::Whitespace(5, 0),
|
||||
Token::Little(6),
|
||||
Token::Key(7, vec!['4', 'a']),
|
||||
Token::Key(7, "4a".to_string()),
|
||||
Token::Dot(9),
|
||||
Token::Key(10, vec!['0', '1']),
|
||||
Token::Key(10, "01".to_string()),
|
||||
Token::CloseParenthesis(12),
|
||||
]
|
||||
, Some(TokenError::Eof)
|
||||
@ -547,11 +547,11 @@ mod tests {
|
||||
Token::OpenParenthesis(1),
|
||||
Token::Absolute(2),
|
||||
Token::Dot(3),
|
||||
Token::Key(4, vec!['c']),
|
||||
Token::Key(4, "c".to_string()),
|
||||
Token::Greater(5),
|
||||
Token::At(6),
|
||||
Token::Dot(7),
|
||||
Token::Key(8, vec!['d']),
|
||||
Token::Key(8, "d".to_string()),
|
||||
Token::CloseParenthesis(9)
|
||||
]
|
||||
, Some(TokenError::Eof)
|
||||
|
384
src/ref_value/mod.rs
Normal file
384
src/ref_value/mod.rs
Normal file
@ -0,0 +1,384 @@
|
||||
extern crate indexmap;
|
||||
extern crate serde_json;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::convert::Into;
|
||||
|
||||
use indexmap::map::IndexMap;
|
||||
use serde_json::Number;
|
||||
use serde_json::Value;
|
||||
|
||||
pub type TypeRefValue = Arc<Box<RefValue>>;
|
||||
|
||||
impl Into<RefValueWrapper> for TypeRefValue {
|
||||
fn into(self) -> RefValueWrapper {
|
||||
RefValueWrapper::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<RefValueWrapper> for &TypeRefValue {
|
||||
fn into(self) -> RefValueWrapper {
|
||||
RefValueWrapper::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// serde_json::Value 참고
|
||||
///
|
||||
|
||||
pub trait RefIndex {
|
||||
fn index_into<'v>(&self, v: &'v RefValue) -> Option<&'v TypeRefValue>;
|
||||
fn index_into_mut<'v>(&self, v: &'v mut RefValue) -> Option<&'v mut TypeRefValue>;
|
||||
fn index_or_insert<'v>(&self, v: &'v mut RefValue) -> &'v mut TypeRefValue;
|
||||
}
|
||||
|
||||
impl RefIndex for usize {
|
||||
fn index_into<'v>(&self, v: &'v RefValue) -> Option<&'v TypeRefValue> {
|
||||
match *v {
|
||||
RefValue::Array(ref vec) => vec.get(*self),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn index_into_mut<'v>(&self, v: &'v mut RefValue) -> Option<&'v mut TypeRefValue> {
|
||||
match *v {
|
||||
RefValue::Array(ref mut vec) => vec.get_mut(*self),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn index_or_insert<'v>(&self, v: &'v mut RefValue) -> &'v mut TypeRefValue {
|
||||
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 TypeRefValue> {
|
||||
match *v {
|
||||
RefValue::Object(ref map) => map.get(self),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn index_into_mut<'v>(&self, v: &'v mut RefValue) -> Option<&'v mut TypeRefValue> {
|
||||
match *v {
|
||||
RefValue::Object(ref mut map) => map.get_mut(self),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn index_or_insert<'v>(&self, v: &'v mut RefValue) -> &'v mut TypeRefValue {
|
||||
if let RefValue::Null = *v {
|
||||
*v = RefValue::Object(IndexMap::new());
|
||||
}
|
||||
match *v {
|
||||
RefValue::Object(ref mut map) => {
|
||||
map.entry(self.to_owned()).or_insert(RefValueWrapper::wrap(RefValue::Null))
|
||||
},
|
||||
_ => panic!("cannot access key {:?} in JSON {:?}", self, v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RefIndex for String {
|
||||
fn index_into<'v>(&self, v: &'v RefValue) -> Option<&'v TypeRefValue> {
|
||||
self[..].index_into(v)
|
||||
}
|
||||
fn index_into_mut<'v>(&self, v: &'v mut RefValue) -> Option<&'v mut TypeRefValue> {
|
||||
self[..].index_into_mut(v)
|
||||
}
|
||||
fn index_or_insert<'v>(&self, v: &'v mut RefValue) -> &'v mut TypeRefValue {
|
||||
self[..].index_or_insert(v)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RefValueWrapper {
|
||||
data: TypeRefValue
|
||||
}
|
||||
|
||||
impl RefValueWrapper {
|
||||
pub fn new(ref_value: TypeRefValue) -> Self {
|
||||
RefValueWrapper { data: ref_value }
|
||||
}
|
||||
|
||||
pub fn wrap(ref_val: RefValue) -> TypeRefValue {
|
||||
Arc::new(Box::new(ref_val))
|
||||
}
|
||||
|
||||
pub fn into_value(&self) -> Value {
|
||||
ValueConverter::new(&self.data)
|
||||
}
|
||||
|
||||
pub fn clone(&self) -> Self {
|
||||
RefValueWrapper { data: self.data.clone() }
|
||||
}
|
||||
|
||||
pub fn clone_data(&self) -> TypeRefValue {
|
||||
self.data.clone()
|
||||
}
|
||||
|
||||
pub fn get<I: RefIndex>(&self, index: I) -> Option<RefValueWrapper> {
|
||||
index.index_into(&**self.data).map(|v| Self::new(v.clone()))
|
||||
}
|
||||
|
||||
pub fn is_object(&self) -> bool {
|
||||
(**self.data).is_object()
|
||||
}
|
||||
|
||||
pub fn as_object(&self) -> Option<&IndexMap<String, TypeRefValue>> {
|
||||
(**self.data).as_object()
|
||||
}
|
||||
|
||||
pub fn is_array(&self) -> bool {
|
||||
(**self.data).is_array()
|
||||
}
|
||||
|
||||
pub fn as_array(&self) -> Option<&Vec<TypeRefValue>> {
|
||||
(**self.data).as_array()
|
||||
}
|
||||
|
||||
pub fn is_string(&self) -> bool {
|
||||
(**self.data).is_string()
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
(**self.data).as_str()
|
||||
}
|
||||
|
||||
pub fn is_number(&self) -> bool {
|
||||
(**self.data).is_number()
|
||||
}
|
||||
|
||||
pub fn as_number(&self) -> Option<Number> {
|
||||
(**self.data).as_number()
|
||||
}
|
||||
|
||||
pub fn is_boolean(&self) -> bool {
|
||||
(**self.data).is_boolean()
|
||||
}
|
||||
|
||||
pub fn as_bool(&self) -> Option<bool> {
|
||||
(**self.data).as_bool()
|
||||
}
|
||||
|
||||
pub fn is_null(&self) -> bool {
|
||||
(**self.data).is_null()
|
||||
}
|
||||
|
||||
pub fn as_null(&self) -> Option<()> {
|
||||
(**self.data).as_null()
|
||||
}
|
||||
|
||||
pub fn get_data_ref(&self) -> &RefValue {
|
||||
&(**self.data)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<RefValueWrapper> for Value {
|
||||
fn into(self) -> RefValueWrapper {
|
||||
let ref_val = RefValueConverter::new(self);
|
||||
RefValueWrapper::new(ref_val)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RefValue {
|
||||
Null,
|
||||
Bool(bool),
|
||||
Number(Number),
|
||||
String(String),
|
||||
Array(Vec<TypeRefValue>),
|
||||
Object(IndexMap<String, TypeRefValue>),
|
||||
}
|
||||
|
||||
impl RefValue {
|
||||
pub fn get<I: RefIndex>(&self, index: I) -> Option<&TypeRefValue> {
|
||||
index.index_into(self)
|
||||
}
|
||||
|
||||
pub fn is_object(&self) -> bool {
|
||||
self.as_object().is_some()
|
||||
}
|
||||
|
||||
pub fn as_object(&self) -> Option<&IndexMap<String, TypeRefValue>> {
|
||||
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<TypeRefValue>> {
|
||||
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<Number> {
|
||||
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<bool> {
|
||||
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<RefValueWrapper> for RefValue {
|
||||
|
||||
fn into(self) -> RefValueWrapper {
|
||||
let wrap = RefValueWrapper::wrap(self);
|
||||
RefValueWrapper::new(wrap)
|
||||
}
|
||||
}
|
||||
|
||||
struct RefValueConverter;
|
||||
|
||||
impl RefValueConverter {
|
||||
fn new(value: Value) -> TypeRefValue {
|
||||
RefValueConverter {}.visit_value(value)
|
||||
}
|
||||
|
||||
fn visit_value(&self, value: Value) -> TypeRefValue {
|
||||
match value {
|
||||
Value::Null => self.visit_null(),
|
||||
Value::Bool(v) => self.visit_bool(v),
|
||||
Value::Number(v) => self.visit_number(v),
|
||||
Value::String(v) => self.visit_string(v),
|
||||
Value::Array(v) => self.visit_array(v),
|
||||
Value::Object(v) => self.visit_object(v),
|
||||
}
|
||||
}
|
||||
fn visit_null(&self) -> TypeRefValue {
|
||||
RefValueWrapper::wrap(RefValue::Null)
|
||||
}
|
||||
fn visit_bool(&self, value: bool) -> TypeRefValue {
|
||||
RefValueWrapper::wrap(RefValue::Bool(value))
|
||||
}
|
||||
fn visit_number(&self, value: serde_json::Number) -> TypeRefValue {
|
||||
RefValueWrapper::wrap(RefValue::Number(value))
|
||||
}
|
||||
fn visit_string(&self, value: String) -> TypeRefValue {
|
||||
RefValueWrapper::wrap(RefValue::String(value.to_string()))
|
||||
}
|
||||
fn visit_array(&self, value: Vec<Value>) -> TypeRefValue {
|
||||
let mut values = Vec::new();
|
||||
for v in value {
|
||||
values.push(self.visit_value(v));
|
||||
}
|
||||
RefValueWrapper::wrap(RefValue::Array(values))
|
||||
}
|
||||
fn visit_object(&self, mut value: serde_json::Map<String, Value>) -> TypeRefValue {
|
||||
let mut map = IndexMap::new();
|
||||
let keys: Vec<String> = value.keys().into_iter().map(|k| k.to_string()).collect();
|
||||
for k in keys {
|
||||
let value = self.visit_value(match value.get_mut(&k) {
|
||||
Some(v) => v.take(),
|
||||
_ => Value::Null
|
||||
});
|
||||
map.insert(k, value);
|
||||
}
|
||||
RefValueWrapper::wrap(RefValue::Object(map))
|
||||
}
|
||||
}
|
||||
|
||||
struct ValueConverter;
|
||||
|
||||
impl ValueConverter {
|
||||
fn new(value: &TypeRefValue) -> Value {
|
||||
ValueConverter {}.visit_value(value)
|
||||
}
|
||||
|
||||
fn visit_value(&self, value: &TypeRefValue) -> Value {
|
||||
match &***value {
|
||||
RefValue::Null => self.visit_null(),
|
||||
RefValue::Bool(v) => self.visit_bool(v),
|
||||
RefValue::Number(v) => self.visit_number(v),
|
||||
RefValue::String(v) => self.visit_string(v),
|
||||
RefValue::Array(v) => self.visit_array(v),
|
||||
RefValue::Object(v) => self.visit_object(v),
|
||||
}
|
||||
}
|
||||
fn visit_null(&self) -> Value {
|
||||
Value::Null
|
||||
}
|
||||
fn visit_bool(&self, value: &bool) -> Value {
|
||||
Value::Bool(*value)
|
||||
}
|
||||
fn visit_number(&self, value: &serde_json::Number) -> Value {
|
||||
Value::Number(value.clone())
|
||||
}
|
||||
fn visit_string(&self, value: &String) -> Value {
|
||||
Value::String(value.clone())
|
||||
}
|
||||
fn visit_array(&self, value: &Vec<TypeRefValue>) -> Value {
|
||||
let mut values = Vec::new();
|
||||
for v in value {
|
||||
values.push(self.visit_value(v));
|
||||
}
|
||||
Value::Array(values)
|
||||
}
|
||||
fn visit_object(&self, map: &IndexMap<String, TypeRefValue>) -> Value {
|
||||
let mut ret = serde_json::Map::new();
|
||||
let keys: Vec<String> = map.keys().into_iter().map(|k: &String| k.to_string()).collect();
|
||||
let tmp_null = &RefValueWrapper::wrap(RefValue::Null);
|
||||
for k in keys {
|
||||
let value = self.visit_value(match map.get(&k) {
|
||||
Some(e) => e,
|
||||
_ => tmp_null
|
||||
});
|
||||
ret.insert(k, value);
|
||||
}
|
||||
Value::Object(ret)
|
||||
}
|
||||
}
|
2
wasm/.gitignore
vendored
2
wasm/.gitignore
vendored
@ -4,3 +4,5 @@ Cargo.lock
|
||||
bin/
|
||||
pkg/
|
||||
wasm-pack.log
|
||||
.idea/*
|
||||
.vscode
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "jsonpath-wasm"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
authors = ["Changseok Han <freestrings@gmail.com>"]
|
||||
|
||||
description = "JsonPath Webassembly version compiled by Rust - Demo: https://freestrings.github.io/jsonpath"
|
||||
@ -22,8 +22,10 @@ wasm-bindgen = { version = "0.2", features = ["serde-serialize"] }
|
||||
console_error_panic_hook = { version = "0.1.1", optional = true }
|
||||
wee_alloc = { version = "0.4.2", optional = true }
|
||||
|
||||
jsonpath_lib = {path = "../"}
|
||||
jsonpath_lib = { path = "../" }
|
||||
serde_json = { version = "1.0", features = ["preserve_order"] }
|
||||
lazy_static = "1.3.0"
|
||||
web-sys = { version = "0.3", features = ['console'] }
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.2"
|
||||
|
@ -6,8 +6,10 @@ set -e
|
||||
DIR="$(pwd)"
|
||||
|
||||
cd "${DIR}"/www && \
|
||||
rm -rf "${DIR}"/dist && \
|
||||
rm -rf "${DIR}"/node_modules && \
|
||||
rm -rf "${DIR}"/www/dist && \
|
||||
rm -rf "${DIR}"/www/node_modules && \
|
||||
rm -rf "${DIR}"/www_bench/dist && \
|
||||
rm -rf "${DIR}"/www_bench/node_modules && \
|
||||
npm install && \
|
||||
cd "${DIR}"
|
||||
|
||||
@ -24,6 +26,10 @@ echo "-------------------- build nodejs pkg done --------------------"
|
||||
|
||||
cd "${DIR}"
|
||||
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
echo "-------------------- start build browser pkg --------------------"
|
||||
@ -34,6 +40,10 @@ cd "${DIR}"/browser_pkg && npm link && \
|
||||
cd "${DIR}"/www && npm link @browser/jsonpath-wasm
|
||||
echo "-------------------- build browser pkg done --------------------"
|
||||
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
echo "-------------------- start build browser bench pkg --------------------"
|
||||
@ -41,3 +51,29 @@ echo
|
||||
rm -rf "${DIR}"/www_bench/node_modules && \
|
||||
cd "${DIR}"/www_bench && npm install && npm link @browser/jsonpath-wasm
|
||||
echo "-------------------- build browser bench pkg done --------------------"
|
||||
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
echo "-------------------- start build docs --------------------"
|
||||
cd "${DIR}"/www && \
|
||||
npm run build && \
|
||||
rm -f "${DIR}"/../docs/*.js && rm -f "${DIR}"/../docs/*.wasm && rm -f "${DIR}"/../docs/*.html && \
|
||||
cp "${DIR}"/www/dist/*.* "${DIR}"/../docs/
|
||||
echo "-------------------- build docs done --------------------"
|
||||
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
echo "-------------------- start build docs bench --------------------"
|
||||
cd "${DIR}"/www_bench && \
|
||||
npm run build && \
|
||||
rm -f "${DIR}"/../docs/bench/*.js && rm -f "${DIR}"/../docs/bench/*.wasm && rm -f "${DIR}"/../docs/bench/*.html && \
|
||||
cp "${DIR}"/www_bench/dist/*.* "${DIR}"/../docs/bench/
|
||||
echo "-------------------- build docs bench done --------------------"
|
123
wasm/src/lib.rs
123
wasm/src/lib.rs
@ -1,19 +1,24 @@
|
||||
extern crate cfg_if;
|
||||
extern crate wasm_bindgen;
|
||||
|
||||
extern crate serde_json;
|
||||
extern crate jsonpath_lib as jsonpath;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
extern crate serde_json;
|
||||
extern crate wasm_bindgen;
|
||||
extern crate web_sys;
|
||||
|
||||
mod utils;
|
||||
use std::collections::HashMap;
|
||||
use std::result::Result;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use std::result::Result;
|
||||
use std::rc::Rc;
|
||||
use serde_json::Value;
|
||||
|
||||
use jsonpath::parser::parser::*;
|
||||
use jsonpath::filter::value_filter::*;
|
||||
use jsonpath::parser::parser::*;
|
||||
use jsonpath::ref_value::*;
|
||||
use serde_json::Value;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use web_sys::console;
|
||||
|
||||
mod utils;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "wee_alloc")] {
|
||||
@ -23,17 +28,17 @@ cfg_if! {
|
||||
}
|
||||
}
|
||||
|
||||
fn filter_value(json: Value, node: Node) -> JsValue {
|
||||
let mut jf = JsonValueFilter::new_from_value(Rc::new(Box::new(json)));
|
||||
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();
|
||||
let taken = jf.take_value().into_value();
|
||||
match JsValue::from_serde(&taken) {
|
||||
Ok(js_value) => js_value,
|
||||
Err(e) => JsValue::from_str(&format!("Json deserialize error: {:?}", e))
|
||||
}
|
||||
}
|
||||
|
||||
fn into_value(js_value: &JsValue) -> Result<Value, String> {
|
||||
fn into_serde_json(js_value: &JsValue) -> Result<Value, String> {
|
||||
if js_value.is_string() {
|
||||
match serde_json::from_str(js_value.as_string().unwrap().as_str()) {
|
||||
Ok(json) => Ok(json),
|
||||
@ -47,20 +52,64 @@ fn into_value(js_value: &JsValue) -> Result<Value, String> {
|
||||
}
|
||||
}
|
||||
|
||||
fn into_js_value(js_value: &JsValue, node: Node) -> JsValue {
|
||||
match into_value(js_value) {
|
||||
Ok(json) => filter_value(json, node),
|
||||
fn into_ref_value(js_value: &JsValue, node: Node) -> JsValue {
|
||||
match into_serde_json(js_value) {
|
||||
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")
|
||||
}
|
||||
}
|
||||
_ => into_ref_value(&js_value, node)
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref CACHE_JSON: Mutex<HashMap<usize, RefValueWrapper>> = Mutex::new(HashMap::new());
|
||||
static ref CACHE_JSON_IDX: Mutex<usize> = Mutex::new(0);
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn alloc_json(js_value: JsValue) -> usize {
|
||||
match into_serde_json(&js_value) {
|
||||
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::log_1(&e.into());
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub 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 cb = Closure::wrap(Box::new(move |js_value: JsValue| {
|
||||
match &node {
|
||||
Ok(node) => into_js_value(&js_value, node.clone()),
|
||||
Ok(node) => get_ref_value(js_value, node.clone()),
|
||||
Err(e) => JsValue::from_str(&format!("Json path error: {:?}", e))
|
||||
}
|
||||
}) as Box<Fn(JsValue) -> JsValue>);
|
||||
@ -70,12 +119,35 @@ pub fn compile(path: &str) -> JsValue {
|
||||
ret
|
||||
}
|
||||
|
||||
///
|
||||
/// deprecated. use selector
|
||||
///
|
||||
#[wasm_bindgen]
|
||||
pub fn reader(js_value: JsValue) -> JsValue {
|
||||
selector(js_value)
|
||||
}
|
||||
|
||||
#[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 cb = Closure::wrap(Box::new(move |path: String| {
|
||||
let mut parser = Parser::new(path.as_str());
|
||||
match parser.compile() {
|
||||
Ok(node) => into_js_value(&js_value, node),
|
||||
Ok(node) => filter_ref_value(json.clone(), node),
|
||||
Err(e) => return JsValue::from_str(e.as_str())
|
||||
}
|
||||
}) as Box<Fn(String) -> JsValue>);
|
||||
@ -86,14 +158,21 @@ pub fn reader(js_value: JsValue) -> JsValue {
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn read(js_value: JsValue, path: &str) -> JsValue {
|
||||
pub fn select(js_value: JsValue, path: &str) -> JsValue {
|
||||
let mut parser = Parser::new(path);
|
||||
match parser.compile() {
|
||||
Ok(node) => into_js_value(&js_value, node),
|
||||
Ok(node) => get_ref_value(js_value, node),
|
||||
Err(e) => return JsValue::from_str(e.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// deprecated. use select
|
||||
///
|
||||
#[wasm_bindgen]
|
||||
pub fn testa() {
|
||||
pub fn read(js_value: JsValue, path: &str) -> JsValue {
|
||||
select(js_value, path)
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn testa() {}
|
@ -2,11 +2,14 @@ import * as jpw from "@browser/jsonpath-wasm";
|
||||
import * as jp from "jsonpath/jsonpath.js";
|
||||
|
||||
function run(message, iter, cb) {
|
||||
let d = Date.now();
|
||||
for (let i = 0; i < iter; i++) {
|
||||
cb();
|
||||
}
|
||||
msg([message, Date.now() - d].join(", "));
|
||||
return new Promise(function(resolve, _reject) {
|
||||
let d = Date.now();
|
||||
for (let i = 0; i < iter; i++) {
|
||||
cb();
|
||||
}
|
||||
msg([message, Date.now() - d].join(", "));
|
||||
setTimeout(resolve, 0);
|
||||
})
|
||||
}
|
||||
|
||||
function msg(msg) {
|
||||
@ -54,12 +57,33 @@ let json = {
|
||||
"expensive": 10
|
||||
};
|
||||
|
||||
setTimeout(function() {
|
||||
let path = '$..book[?(@.price<30 && @.category=="fiction")]';
|
||||
let template = jpw.compile(path);
|
||||
let reader = jpw.reader(json);
|
||||
run('jsonpath', 1000, function() { jp.query(json, path) });
|
||||
run('jsonpath-wasm- reader', 1000, function() { reader(path) });
|
||||
run('jsonpath-wasm- compile', 1000, function() { template(json) });
|
||||
run('jsonpath-wasm- read', 1000, function() { jpw.read(json, path) });
|
||||
}, 0);
|
||||
let path = '$..book[?(@.price<30 && @.category=="fiction")]';
|
||||
let template = jpw.compile(path);
|
||||
let selector = jpw.selector(json);
|
||||
|
||||
let ptr = jpw.alloc_json(json);
|
||||
if(ptr == 0) console.error('invalid ptr');
|
||||
|
||||
let iterCount = 2000;
|
||||
|
||||
run('jsonpath', iterCount, function() { jp.query(json, path) })
|
||||
.then(function() {
|
||||
return run('jsonpath-wasm- selector', iterCount, function() { selector(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.dealloc_json(ptr)) {
|
||||
console.error('fail to dealloc');
|
||||
}
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user