serde_json::Value clone 제거

This commit is contained in:
freestrings 2019-03-11 17:35:15 +09:00
parent ba57ae0ea5
commit 1a3104c5db
30 changed files with 1333 additions and 672 deletions

View File

@ -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
View File

@ -1,7 +1,7 @@
# jsonpath-lib
[![Build Status](https://travis-ci.org/freestrings/jsonpath.svg?branch=master)](https://travis-ci.org/freestrings/jsonpath)
[![version](https://img.shields.io/crates/v/:jsonpath.svg)](https://crates.io/crates/jsonpath_lib)
![crates.io](https://img.shields.io/crates/v/jsonpath_lib.svg)
`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
```

View File

@ -1,6 +1,6 @@
[package]
name = "bench_bin"
version = "0.1.0"
version = "0.1.1"
[dependencies]
jsonpath_lib = {path = "../../"}

View File

@ -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();
}
}

View File

@ -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

View File

@ -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');
}

View File

@ -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",

View File

@ -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

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -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
View File

@ -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) {

View File

@ -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());
}
}

View File

@ -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)
}
}

View File

@ -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);
}

View File

@ -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)

View File

@ -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",

View File

@ -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)))
}
_ => {

View File

@ -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> {

View File

@ -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
View 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
View File

@ -4,3 +4,5 @@ Cargo.lock
bin/
pkg/
wasm-pack.log
.idea/*
.vscode

View File

@ -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"

View File

@ -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 --------------------"

View File

@ -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() {}

View File

@ -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');
}
});