63 Commits

Author SHA1 Message Date
488e0b400f jsonpath-rs unit test for 0.2.2 2019-06-19 16:17:58 +09:00
fff0e869cb improve error message 2019-06-19 15:10:50 +09:00
6a270c9456 IntelliJ rustfmt default rule 적용 2019-06-19 11:52:53 +09:00
ebd49c2205 Resolved #6 (from v0.2.2), new array filter (range step, escaped quote notation, array keys) 2019-06-19 11:45:55 +09:00
2537469f03 0.2.2 docs 2019-06-18 14:19:08 +09:00
2e0f78f017 0.2.2 배포 전까지 jsonpath-rs unit test 임시 롤백 2019-06-18 13:38:11 +09:00
5d36a0cf15 array keys - js unit test 2019-06-18 13:27:28 +09:00
a72a13117e array range with step - js unit test 2019-06-18 13:27:09 +09:00
964e0c00f5 escaped quote notation - js unit test 2019-06-18 13:26:42 +09:00
de97e2f95a array keys 2019-06-18 13:26:36 +09:00
74666d264e array range with step 2019-06-18 13:26:36 +09:00
51deec66d0 escaped quote notation 2019-06-18 13:26:36 +09:00
909c851dcc Results do not match other implementations - js unit test 2019-06-18 13:25:59 +09:00
b41b9f3aa6 Results do not match other implementations #6 2019-06-17 12:07:52 +09:00
1a5e8cc025 IntelliJ Run 설정 수정 2019-06-17 11:27:12 +09:00
5abbfba254 테스트 코드에 select 대신 select_as를 사용하게 수정 2019-06-17 11:19:25 +09:00
ffefb7b2e6 jsonpath-wasm 배포 오류 수정 2019-06-11 23:04:40 +09:00
950966d57e update docs 2019-06-11 22:49:27 +09:00
635b5b8d43 update wasm README.md, nodejs README.md. restore nodejs build 2019-06-11 22:16:37 +09:00
ff52821323 v0.2.0 - add: delete, replace feature. resolve #1. remove: map function. 2019-06-11 21:53:46 +09:00
4af31947f5 0.2.0 배포 전까지 nodejs 빌드 제거 2019-06-11 18:56:27 +09:00
766be8cab2 Stable 버전에서 컴파일 에러 수정 2019-06-11 18:56:27 +09:00
2e9e0ac6fc compiled_path 인자 타입 변경, compute_path 로직 개선 2019-06-11 18:56:27 +09:00
fbb2b49ba0 update READEME.md 2019-06-11 18:56:27 +09:00
e096e62dbf SelectorMut NodeJs 적용 2019-06-11 18:56:27 +09:00
ec5d76d2d6 SelectorMut WASM 적용 2019-06-11 18:56:27 +09:00
24d18efb6f SelectorMut documentation 2019-06-11 18:56:27 +09:00
dd9315bc90 SelectorMut first commit 2019-06-11 18:56:27 +09:00
9a08df7843 re-indent 2019-06-11 18:56:27 +09:00
ac3224892b Remove Parser's lifetime 2019-06-11 18:56:27 +09:00
498f2ce4f4 fix nodejs 2019-06-11 18:56:27 +09:00
56a22674bf fix wasm benches 2019-06-11 18:56:27 +09:00
893af2afc0 0.2.0 initail commit 2019-06-11 18:56:27 +09:00
3f89b9b183 remove alloc, dealloc in wasm 2019-06-11 18:56:27 +09:00
d2a5d9092e refactoring done default 2019-06-11 18:56:27 +09:00
9a35357ddb Merge pull request #5 from freestrings/dependabot/npm_and_yarn/examples/nodejs-rs/handlebars-4.1.2
Bump handlebars from 4.1.1 to 4.1.2 in /examples/nodejs-rs
2019-06-11 18:55:39 +09:00
bc2db273bf Bump handlebars from 4.1.1 to 4.1.2 in /examples/nodejs-rs
Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.1.1 to 4.1.2.
- [Release notes](https://github.com/wycats/handlebars.js/releases)
- [Changelog](https://github.com/wycats/handlebars.js/blob/master/release-notes.md)
- [Commits](https://github.com/wycats/handlebars.js/compare/v4.1.1...v4.1.2)

Signed-off-by: dependabot[bot] <support@github.com>
2019-06-05 18:07:44 +00:00
416636bc48 add mocha test in wasm instead of "wasm-pack test" 2019-05-21 10:48:18 +09:00
fe8a2f70c0 The JSON comparison algorithm is changed 2019-05-21 10:44:52 +09:00
846ad26e2c move static function to outside of a struct 2019-05-20 10:40:25 +09:00
9d94d1cd41 Build broken since wasm-pack 0.8.1. Remove wasm-pack build temporary 2019-05-17 15:01:41 +09:00
802640a6da jsonpath-rs add "map", "get" function 2019-05-16 14:40:29 +09:00
5b653ab8a0 REAEME.md - Selector's "map" 2019-05-16 10:03:11 +09:00
9d8ab7ae23 jsonpath-wasm add "map", "get" function 2019-05-15 17:06:39 +09:00
135d3c319b Selector's "select_to" function is deprecated 2019-05-15 14:09:00 +09:00
e2a6b13c9a Added a new function 'map', 'map_as' to the Selector 2019-05-15 14:09:00 +09:00
765f04ce5d jsonpath-rs version up 0.1.10 2019-05-15 14:07:47 +09:00
503ee9ae13 improve nodejs/build.sh 2019-05-15 12:12:38 +09:00
4e4d7c4c22 Merge pull request #3 from gkorland/selector
remove unneeded module prefix
2019-05-15 11:39:28 +09:00
5cff83ebbb remove unneeded module prefix 2019-05-14 14:19:01 +03:00
b49d95d5db Merge pull request #2 from gkorland/cleanmutable
remove unneeded mut
2019-05-14 10:52:58 +09:00
b9e4049a5e remove uneeded mut 2019-05-13 13:54:06 +03:00
671ca83eed neon build broken on nodejs v12.1.0 2019-05-07 00:11:47 +09:00
3792e0014d v0.1.12 - value function in Selector parameter type change. from RefValue to &Value 2019-05-06 22:31:39 +09:00
e4a50bd689 mark deprecated. Selector::value(RefValue) since 0.1.12 2019-04-15 11:02:46 +09:00
132f63b7f9 jsonpath_lib Cargo.toml - keywords 개수 수정 2019-04-13 23:15:59 +09:00
8a580e3b2f wasm-bindgen package.json에 keywords 생성하지 않는 문제 임시조치 2019-04-13 23:04:23 +09:00
d0e572ff56 README.md - fix link path 2019-04-13 22:47:11 +09:00
4a044ba250 jsonpath-wasm publish 2019-04-13 22:39:48 +09:00
35ef9f8c5e Remove firefix buid 2019-04-11 12:26:36 +09:00
753a822341 JsonPath evaluator - Google GA 추가 2019-04-10 18:19:04 +09:00
3276e7e18a 'jsonpath-rs' 버전 badge 적용 2019-04-10 18:04:38 +09:00
3b45f1c4a5 Unit test - 'wasm-bindgen-test' 적용 2019-04-10 17:26:07 +09:00
97 changed files with 14600 additions and 7483 deletions

3
.gitignore vendored
View File

@ -2,4 +2,5 @@
.vscode
!.idea/runConfigurations/
/target/
Cargo.lock
Cargo.lock
callgrind.out.*

View File

@ -1,7 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="selector" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<configuration default="false" name="mutable" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="channel" value="DEFAULT" />
<option name="command" value="test --package jsonpath_lib --test selector &quot;&quot;" />
<option name="command" value="test --package jsonpath_lib --test mutable &quot;&quot;" />
<option name="allFeatures" value="false" />
<option name="nocapture" value="false" />
<option name="backtrace" value="SHORT" />

View File

@ -1,7 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="parser" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="channel" value="DEFAULT" />
<option name="command" value="test --package jsonpath_lib --test parser &quot;&quot;" />
<option name="command" value="test --package jsonpath_lib --lib parser::parser_tests" />
<option name="allFeatures" value="false" />
<option name="nocapture" value="false" />
<option name="backtrace" value="SHORT" />

View File

@ -1,7 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="serde" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<configuration default="false" name="readme" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="channel" value="DEFAULT" />
<option name="command" value="test --package jsonpath_lib --test serde &quot;&quot;" />
<option name="command" value="test --package jsonpath_lib --test readme &quot;&quot;" />
<option name="allFeatures" value="false" />
<option name="nocapture" value="false" />
<option name="backtrace" value="SHORT" />

View File

@ -1,7 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="tokenizer" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="channel" value="DEFAULT" />
<option name="command" value="test --package jsonpath_lib --test tokenizer &quot;&quot;" />
<option name="command" value="test --package jsonpath_lib --lib parser::tokenizer_tests" />
<option name="allFeatures" value="false" />
<option name="nocapture" value="false" />
<option name="backtrace" value="SHORT" />

View File

@ -13,6 +13,8 @@ matrix:
- rust: stable
os: linux
env: RUST_BACKTRACE=1
addons:
chrome: stable
before_script:
- (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update)
- (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate)
@ -21,11 +23,11 @@ matrix:
script:
- cargo build --verbose --all
- cargo test --verbose --all
- cd wasm
- wasm-pack build
- rust: stable
os: osx
env: RUST_BACKTRACE=1
addons:
chrome: stable
before_script:
- (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update)
- (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate)
@ -34,12 +36,9 @@ matrix:
script:
- cargo build --verbose --all
- cargo test --verbose --all
- cd wasm
- wasm-pack build
- language: node_js
os: linux
node_js:
- 'node'
- '11'
- '10'
- '9'
@ -49,6 +48,7 @@ matrix:
- sh /tmp/rustup.sh -y
- export PATH="$HOME/.cargo/bin:$PATH"
- source "$HOME/.cargo/env"
- npm install -g neon-cli
- cd nodejs
- node -v
- npm -v
@ -58,7 +58,6 @@ matrix:
- language: node_js
os: osx
node_js:
- 'node'
- '11'
- '10'
- '9'
@ -68,6 +67,7 @@ matrix:
- sh /tmp/rustup.sh -y
- export PATH="$HOME/.cargo/bin:$PATH"
- source "$HOME/.cargo/env"
- npm install -g neon-cli
- cd nodejs
- node -v
- npm -v

View File

@ -1,17 +1,19 @@
[package]
name = "jsonpath_lib"
version = "0.1.9"
version = "0.2.2"
authors = ["Changseok Han <freestrings@gmail.com>"]
description = "JsonPath in Rust and Webassembly - Webassembly Demo: https://freestrings.github.io/jsonpath"
description = "It is JsonPath engine written in Rust. it provide a similar API interface in Webassembly and Javascript also. - Webassembly Demo: https://freestrings.github.io/jsonpath"
readme = "README.md"
keywords = ["library", "jsonpath", "json", "webassembly"]
keywords = ["jsonpath", "json", "webassembly", "nodejs", "javascript"]
repository = "https://github.com/freestrings/jsonpath"
documentation = "https://docs.rs/jsonpath_lib/0.1.0/jsonpath_lib"
license = "MIT"
categories = ["parsing"]
[badges]
travis-ci = { repository = "freestrings/jsonpath", branch = "master" }
@ -21,6 +23,7 @@ env_logger = "0.6.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
indexmap = "1.0.2"
array_tool = "~1.0.3"
[dev-dependencies]
bencher = "0.1.5"

452
README.md
View File

@ -2,63 +2,33 @@
[![Build Status](https://travis-ci.org/freestrings/jsonpath.svg?branch=master)](https://travis-ci.org/freestrings/jsonpath)
![crates.io](https://img.shields.io/crates/v/jsonpath_lib.svg)
![Crates.io](https://img.shields.io/crates/d/jsonpath_lib.svg?label=%60jsonpath_lib%60%20crates.io%20downloads)
![npm](https://img.shields.io/npm/dt/jsonpath-rs.svg?label=%60jsonpath-rs%60%20npm%20downloads)
![npm](https://img.shields.io/npm/v/jsonpath-rs.svg?label=npm%20%60jsonpath-rs%60)
![npm](https://img.shields.io/npm/v/jsonpath-wasm.svg?label=npm%20%60jsonpath-wasm%60)
`Rust` 버전 [JsonPath](https://goessner.net/articles/JsonPath/) 구현이다. `Webassembly``Javascript`에서도 유사한 API 인터페이스를 제공 한다.
It is an implementation for [JsonPath](https://goessner.net/articles/JsonPath/) written in `Rust`. it provide a similar API interface in `Webassembly` and` Javascript` also.
It is JsonPath [JsonPath](https://goessner.net/articles/JsonPath/) engine written in `Rust`. it provide a similar API interface in `Webassembly` and` Javascript` also.
- [Webassembly Demo](https://freestrings.github.io/jsonpath/)
- [Rust documentation](https://docs.rs/jsonpath_lib)
## Why?
To enjoy Rust!
- [NPM jsonpath-wasm - webassembly](https://www.npmjs.com/package/jsonpath-wasm)
- [NPM jsonpath-rs - native addon](https://www.npmjs.com/package/jsonpath-rs)
## Rust API
- [jsonpath_lib crate](#jsonpath_lib-crate)
- [Rust - jsonpath::Selector struct](#rust---jsonpathselector-struct)
- [Rust - jsonpath::select(json: &serde_json::value::Value, jsonpath: &str)](#rust---jsonpathselectjson-serde_jsonvaluevalue-jsonpath-str)
- [Rust - jsonpath::select_as_str(json_str: &str, jsonpath: &str)](#rust---jsonpathselect_as_strjson-str-jsonpath-str)
- [Rust - jsonpath::select_as\<T: `serde::de::DeserializeOwned`\>(json_str: &str, jsonpath: &str)](#rust---jsonpathselect_ast-serdededeserializeownedjson-str-jsonpath-str)
- [Rust - jsonpath::compile(jsonpath: &str)](#rust---jsonpathcompilejsonpath-str)
- [Rust - jsonpath::selector(json: &serde_json::value::Value)](#rust---jsonpathselectorjson-serde_jsonvaluevalue)
- [Rust - jsonpath::selector_as\<T: `serde::de::DeserializeOwned`\>(json: &serde_json::value::Value)](#rust---jsonpathselector_ast-serdededeserializeownedjson-serde_jsonvaluevalue)
- [Rust - examples](https://github.com/freestrings/jsonpath/wiki/rust-examples)
<details><summary><b>jsonpath_lib crate</b></summary>
## Javascript API
- [npm package](#npm-package)
- [Javascript - jsonpath.Selector class](#javascript---selector-class)
- [Javascript - jsonpath.select(json: string|object, jsonpath: string)](#javascript---jsonpathselectjson-stringobject-jsonpath-string)
- [Javascript - jsonpath.compile(jsonpath: string)](#javascript---jsonpathcompilejsonpath-string)
- [Javascript - jsonpath.selector(json: string|object)](#javascript---jsonpathselectorjson-stringobject)
- [Javascript - allocJson, deallocJson (Webassembly Only)](#javascript---allocjson-deallocjson-webassembly-only)
- [Javascript - examples](https://github.com/freestrings/jsonpath/wiki/Javascript-examples)
## Simple time check
- [jsonpath-wasm](https://github.com/freestrings/jsonpath/wiki/Simple-timecheck---jsonpath-wasm)
- [jsonpath-rs](https://github.com/freestrings/jsonpath/wiki/Simple-timecheck-jsonpath-native)
---
### Rust API
#### jsonpath_lib crate
[Go to creates.io](https://crates.io/crates/jsonpath_lib)
Go to [`jsonpath_lib` creates.io](https://crates.io/crates/jsonpath_lib)
```rust
extern crate jsonpath_lib as jsonpath;
#[macro_use]
extern crate serde_json;
```
#### Rust - jsonpath::Selector struct
</details>
<details><summary><b>Rust - jsonpath::Selector struct</b></summary>
```rust
#[derive(Serialize, Deserialize, PartialEq, Debug)]
#[derive(Deserialize, PartialEq, Debug)]
struct Friend {
name: String,
age: Option<u8>,
@ -80,21 +50,67 @@ let mut selector = Selector::new();
let result = selector
.path("$..[?(@.age >= 30)]").unwrap()
// .value_from_str(&serde_json::to_string(&json_obj).unwrap() /*&str*/).unwrap()
// .value_from(&json_obj /*&impl serde::ser::Serialize*/).unwrap()
.value((&json_obj /*serde_json::value::Value*/ ).into()).unwrap()
.select_to_value().unwrap();
.value(&json_obj)
.select().unwrap();
assert_eq!(json!([{"name": "친구3", "age": 30}]), result);
assert_eq!(vec![&json!({"name": "친구3", "age": 30})], result);
let result = selector.select_to_str().unwrap();
let result = selector.select_as_str().unwrap();
assert_eq!(r#"[{"name":"친구3","age":30}]"#, result);
let result = selector.select_to::<Vec<Friend>>().unwrap();
let result = selector.select_as::<Friend>().unwrap();
assert_eq!(vec![Friend { name: "친구3".to_string(), age: Some(30) }], result);
```
#### Rust - jsonpath::select(json: &serde_json::value::Value, jsonpath: &str)
</details>
<details><summary><b>Rust - jsonpath::SelectorMut struct</b></summary>
```rust
let json_obj = json!({
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]});
let mut selector_mut = SelectorMut::new();
let result = selector_mut
.str_path("$..[?(@.age == 20)].age").unwrap()
.value(json_obj)
.replace_with(&mut |v| {
let age = if let Value::Number(n) = v {
n.as_u64().unwrap() * 2
} else {
0
};
json!(age)
}).unwrap()
.take().unwrap();
assert_eq!(result, json!({
"school": {
"friends": [
{"name": "친구1", "age": 40},
{"name": "친구2", "age": 40}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]}));
```
</details>
<details><summary><b>Rust - jsonpath::select(json: &serde_json::value::Value, jsonpath: &str)</b></summary>
```rust
let json_obj = json!({
@ -111,14 +127,16 @@ let json_obj = json!({
let json = jsonpath::select(&json_obj, "$..friends[0]").unwrap();
let ret = json!([
{"name": "친구3", "age": 30},
{"name": "친구1", "age": 20}
assert_eq!(json, vec![
&json!({"name": "친구3", "age": 30}),
&json!({"name": "친구1", "age": 20})
]);
assert_eq!(json, ret);
```
#### Rust - jsonpath::select_as_str(json: &str, jsonpath: &str)
</details>
<details><summary><b>Rust - jsonpath::select_as_str(json_str: &str, jsonpath: &str)</b></summary>
```rust
let ret = jsonpath::select_as_str(r#"
@ -139,7 +157,9 @@ let ret = jsonpath::select_as_str(r#"
assert_eq!(ret, r#"[{"name":"친구3","age":30},{"name":"친구1","age":20}]"#);
```
#### Rust - jsonpath::select_as\<T: `serde::de::DeserializeOwned`\>(json: &str, jsonpath: &str)
</details>
<details><summary><b>Rust - jsonpath::select_as&lt;T: `serde::de::DeserializeOwned`&gt;(json_str: &str, jsonpath: &str)</b></summary>
```rust
#[derive(Deserialize, PartialEq, Debug)]
@ -149,7 +169,7 @@ struct Person {
phones: Vec<String>,
}
let ret: Person = jsonpath::select_as(r#"
let ret: Vec<Person> = jsonpath::select_as(r#"
{
"person":
{
@ -169,10 +189,12 @@ let person = Person {
phones: vec!["+44 1234567".to_string(), "+44 2345678".to_string()],
};
assert_eq!(person, ret);
assert_eq!(ret[0], person);
```
#### Rust - jsonpath::compile(jsonpath: &str)
</details>
<details><summary><b>Rust - jsonpath::compile(jsonpath: &str)</b></summary>
```rust
let mut template = jsonpath::compile("$..friends[0]");
@ -191,15 +213,15 @@ let json_obj = json!({
let json = template(&json_obj).unwrap();
let ret = json!([
{"name": "친구3", "age": 30},
{"name": "친구1", "age": 20}
assert_eq!(json, vec![
&json!({"name": "친구3", "age": 30}),
&json!({"name": "친구1", "age": 20})
]);
assert_eq!(json, ret);
```
#### Rust - jsonpath::selector(json: &serde_json::value::Value)
</details>
<details><summary><b>Rust - jsonpath::selector(json: &serde_json::value::Value)</b></summary>
```rust
let json_obj = json!({
@ -218,24 +240,22 @@ let mut selector = jsonpath::selector(&json_obj);
let json = selector("$..friends[0]").unwrap();
let ret = json!([
{"name": "친구3", "age": 30},
{"name": "친구1", "age": 20}
assert_eq!(json, vec![
&json!({"name": "친구3", "age": 30}),
&json!({"name": "친구1", "age": 20})
]);
assert_eq!(json, ret);
let json = selector("$..friends[1]").unwrap();
let ret = json!([
{"name": "친구4"},
{"name": "친구2", "age": 20}
assert_eq!(json, vec![
&json!({"name": "친구4"}),
&json!({"name": "친구2", "age": 20})
]);
assert_eq!(json, ret);
```
#### Rust - jsonpath::selector_as\<T: `serde::de::DeserializeOwned`\>(json: &serde_json::value::Value)
</details>
<details><summary><b>Rust - jsonpath::selector_as&lt;T: serde::de::DeserializeOwned&gt;(json: &serde_json::value::Value)</b></summary>
```rust
let json_obj = json!({
@ -250,13 +270,13 @@ let json_obj = json!({
{"name": "친구4"}
]});
#[derive(Serialize, Deserialize, PartialEq, Debug)]
#[derive(Deserialize, PartialEq, Debug)]
struct Friend {
name: String,
age: Option<u8>,
}
let mut selector = jsonpath::selector_as::<Vec<Friend>>(&json_obj);
let mut selector = jsonpath::selector_as::<Friend>(&json_obj);
let json = selector("$..friends[0]").unwrap();
@ -276,13 +296,89 @@ let ret = vec!(
assert_eq!(json, ret);
```
---
</details>
### Javascript API
<details><summary><b>Rust - jsonpath::delete(value: &Value, path: &str)</b></summary>
#### npm package
```rust
let json_obj = json!({
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]});
##### jsonpath-wasm (Not yet published)
let ret = jsonpath::delete(json_obj, "$..[?(20 == @.age)]").unwrap();
assert_eq!(ret, json!({
"school": {
"friends": [
null,
null
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]}));
```
</details>
<details><summary><b>Rust - jsonpath::replace_with&lt;F: FnMut(&Value) -> Value&gt;(value: &Value, path: &str, fun: &mut F)</b></summary>
```rust
let json_obj = json!({
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]});
let ret = jsonpath::replace_with(json_obj, "$..[?(@.age == 20)].age", &mut |v| {
let age = if let Value::Number(n) = v {
n.as_u64().unwrap() * 2
} else {
0
};
json!(age)
}).unwrap();
assert_eq!(ret, json!({
"school": {
"friends": [
{"name": "친구1", "age": 40},
{"name": "친구2", "age": 40}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]}));
```
</details>
[Rust - Other Examples](https://github.com/freestrings/jsonpath/wiki/rust-examples)
## Javascript API
<details><summary><b>npm package</b></summary>
##### jsonpath-wasm
Goto [`jsonpath-wasm` npmjs.org](https://www.npmjs.com/package/jsonpath-wasm)
```javascript
// browser
@ -293,16 +389,18 @@ const jsonpath = require('jsonpath-wasm');
##### jsonpath-rs (NodeJS only)
[Goto npmjs.org](https://www.npmjs.com/package/jsonpath-rs)
Goto [`jsonpath-rs` npmjs.org](https://www.npmjs.com/package/jsonpath-rs)
```javascript
const jsonpath = require('jsonpath-rs');
```
#### javascript - Selector class
</details>
<details><summary><b>Javascript - jsonpath.Selector class</b></summary>
##### jsonpath-wasm
`wasm-bindgen` 리턴타입 제약 때문에 빌더 패턴은 지원하지 않는다.
`wasm-bindgen` 리턴 타입 제약 때문에 빌더 패턴은 지원하지 않는다.
It does not support `builder-pattern` due to the `return type` restriction of `wasm-bindgen`.
@ -329,15 +427,11 @@ let selector = new jsonpath.Selector();
selector.path('$..friends[0]');
selector.value(jsonObj);
let selectToObj = selector.selectTo();
let selectToString = selector.selectToStr();
let retObj = selector.select();
console.log(
JSON.stringify(ret) == JSON.stringify(selectToObj),
JSON.stringify(ret) == selectToString
);
console.log(JSON.stringify(ret) == JSON.stringify(retObj));
// => true, true
// => true
```
##### jsonpath-rs
@ -365,18 +459,80 @@ let selector = new jsonpath.Selector()
.path('$..friends[0]')
.value(jsonObj);
let selectToObj = selector.selectTo();
let selectToString = selector.selectToStr();
let retObj = selector.select();
console.log(
JSON.stringify(ret) == JSON.stringify(selectToObj),
JSON.stringify(ret) == selectToString
);
console.log(JSON.stringify(ret) == JSON.stringify(retObj));
// => true, true
// => true
```
#### Javascript - jsonpath.select(json: string|object, jsonpath: string)
</details>
<details><summary><b>Javascript - jsonpath.SelectorMut class</b></summary>
빌더 패턴 제약은 `Selector class`와 동일하다.
```javascript
let jsonObj = {
'school': {
'friends': [
{'name': '친구1', 'age': 20},
{'name': '친구2', 'age': 20},
],
},
'friends': [
{'name': '친구3', 'age': 30},
{'name': '친구4'},
],
};
let selector = new jsonpath.SelectorMut();
selector.path('$..[?(@.age == 20)]');
{
selector.value(jsonObj);
selector.deleteValue();
let resultObj = {
'school': {'friends': [null, null]},
'friends': [
{'name': '친구3', 'age': 30},
{'name': '친구4'},
],
};
console.log(JSON.stringify(selector.take()) !== JSON.stringify(resultObj));
// => true
}
{
selector.value(jsonObj);
selector.replaceWith((v) => {
v.age = v.age * 2;
return v;
});
let resultObj = {
'school': {
'friends': [
{'name': '친구1', 'age': 40},
{'name': '친구2', 'age': 40},
],
},
'friends': [
{'name': '친구3', 'age': 30},
{'name': '친구4'},
],
};
console.log(JSON.stringify(selector.take()) !== JSON.stringify(resultObj));
// => true
}
```
</details>
<details><summary><b>Javascript - jsonpath.select(json: string|object, jsonpath: string)</b></summary>
```javascript
let jsonObj = {
@ -409,7 +565,9 @@ console.log(
// => true, true
```
#### Javascript - jsonpath.compile(jsonpath: string)
</details>
<details><summary><b>Javascript - jsonpath.compile(jsonpath: string)</b></summary>
```javascript
let template = jsonpath.compile('$..friends[0]');
@ -468,8 +626,10 @@ console.log(
// => true, true
```
#### Javascript - jsonpath.selector(json: string|object)
</details>
<details><summary><b>Javascript - jsonpath.selector(json: string|object)</b></summary>
```javascript
let jsonObj = {
"school": {
@ -509,14 +669,11 @@ console.log(
// => true, true
```
#### Javascript - allocJson, deallocJson (Webassembly Only)
wasm-bindgen은 Javascript와 Webassembly간 값을 주고받을 때 JSON 객체는 String으로 변환되기 때문에, 반복해서 사용되는 JSON 객체는 Webassembly 영역에 생성해 두면 성능에 도움이 된다.
</details>
Since wasm-bindgen converts JSON objects to String when exchanging values between Javascript and Webassembly, creating frequently used JSON objects in the WebAssembly area helps performance.
<details><summary><b>Javascript - jsonpath.deleteValue(json: string|object, path: string)</b></summary>
```javascript
const jsonpath = require('@nodejs/jsonpath-wasm');
let jsonObj = {
"school": {
"friends": [
@ -530,37 +687,58 @@ let jsonObj = {
]
};
// allocate jsonObj in webassembly
let ptr = jsonpath.allocJson(jsonObj);
let _1 = jsonpath.deleteValue(jsonObj, '$..friends[0]');
let result = jsonpath.deleteValue(_1, '$..friends[1]');
// `0` is invalid pointer
if(ptr == 0) {
console.error('invalid ptr');
}
console.log(JSON.stringify(result) !== JSON.stringify({
"school": { "friends": [null, null]},
"friends": [null, null]
}));
let path = '$..friends[0]';
let template = jsonpath.compile(path);
let selector = jsonpath.selector(jsonObj);
// create selector as pointer
let ptrSelector = jsonpath.selector(ptr);
// => true
let ret1 = selector(path)
let ret2 = ptrSelector(path)
let ret3 = template(jsonObj);
// select as pointer
let ret4 = template(ptr);
let ret5 = jsonpath.select(jsonObj, path);
// select as pointer
let ret6 = jsonpath.select(ptr, path);
console.log(
JSON.stringify(ret1) == JSON.stringify(ret2),
JSON.stringify(ret1) == JSON.stringify(ret3),
JSON.stringify(ret1) == JSON.stringify(ret4),
JSON.stringify(ret1) == JSON.stringify(ret5),
JSON.stringify(ret1) == JSON.stringify(ret6));
// => true true true true true
jsonpath.deallocJson(ptr);
```
</details>
<details><summary><b>Javascript - jsonpath.replaceWith(json: string|object, path: string, fun: function(json: object) => json: object</b></summary>
```javascript
let jsonObj = {
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]
};
let result = jsonpath.replaceWith(jsonObj, '$..friends[0]', (v) => {
v.age = v.age * 2;
return v;
});
console.log(JSON.stringify(result) === JSON.stringify({
"school": {
"friends": [
{"name": "친구1", "age": 40},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 60},
{"name": "친구4"}
]
}));
// => true
```
</details>
[Javascript - Other Examples](https://github.com/freestrings/jsonpath/wiki/Javascript-examples)

View File

@ -1,18 +1,18 @@
#![feature(test)]
extern crate bencher;
extern crate jsonpath_lib as jsonpath;
extern crate serde;
extern crate serde_json;
extern crate test;
extern crate bencher;
use std::io::Read;
use serde::Deserialize;
use serde_json::Value;
use jsonpath::{SelectorMut, Selector};
use self::test::Bencher;
use jsonpath::ref_value::model::RefValue;
use serde::ser::Serialize;
fn read_json(path: &str) -> String {
let mut f = std::fs::File::open(path).unwrap();
@ -101,28 +101,37 @@ fn bench_select_as(b: &mut Bencher) {
b.iter(move || {
for _ in 1..100 {
let _: Book = jsonpath::select_as(&json, r#"$..book[?(@.price<30 && @.category=="fiction")][0]"#).unwrap();
let _: Vec<Book> = jsonpath::select_as(&json, r#"$..book[?(@.price<30 && @.category=="fiction")][0]"#).unwrap();
}
});
}
#[bench]
fn bench_serde_ser(b: &mut Bencher) {
fn bench_delete(b: &mut Bencher) {
let json = get_json();
let mut selector = SelectorMut::new();
let _ = selector.str_path(get_path());
b.iter(move || {
for _ in 1..100 {
let _: RefValue = json.serialize(jsonpath::ref_value::ser::Serializer).unwrap().into();
let _ = selector.value(json.clone()).delete();
}
});
}
#[bench]
fn bench_serde_de(b: &mut Bencher) {
let json_string = get_string();
let json_str = json_string.as_str();
fn bench_select_to_compare_with_delete(b: &mut Bencher) {
let json = &get_json();
b.iter(move || for _ in 1..100 {
let _: RefValue = serde_json::from_str(json_str).unwrap();
let mut selector = Selector::new();
let _ = selector.str_path(get_path());
b.iter(move || {
for _ in 1..100 {
let json = json.clone();
let mut s = Selector::new();
let _ = s.compiled_path(selector.node_ref().unwrap()).value(&json);
let _ = s.select();
}
});
}

View File

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/examples" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/benches" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
</project>

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CargoProjects">
<cargoProject FILE="$PROJECT_DIR$/Cargo.toml" />
</component>
<component name="RustProjectSettings">
<option name="toolchainHomeDirectory" value="$USER_HOME$/.cargo/bin" />
</component>
</project>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/bench_bin.iml" filepath="$PROJECT_DIR$/.idea/bench_bin.iml" />
</modules>
</component>
</project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
</component>
</project>

View File

@ -1,152 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="3fa6f740-0ee1-4afb-b0ae-9239bf5ced3d" name="Default Changelist" comment="">
<change beforePath="$PROJECT_DIR$/../bench.rs" beforeDir="false" afterPath="$PROJECT_DIR$/../bench.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/main.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../bench_node_vs_rust.sh" beforeDir="false" afterPath="$PROJECT_DIR$/../bench_node_vs_rust.sh" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../javascript/bench.js" beforeDir="false" afterPath="$PROJECT_DIR$/../javascript/bench.js" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../../nodejs/lib/index.js" beforeDir="false" afterPath="$PROJECT_DIR$/../../nodejs/lib/index.js" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../../nodejs/native/src/lib.rs" beforeDir="false" afterPath="$PROJECT_DIR$/../../nodejs/native/src/lib.rs" afterDir="false" />
</list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileEditorManager">
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300" />
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/../.." />
</component>
<component name="IdeDocumentHistory">
<option name="CHANGED_PATHS">
<list>
<option value="$PROJECT_DIR$/../../src/lib.rs" />
<option value="$PROJECT_DIR$/src/main.rs" />
</list>
</option>
</component>
<component name="ProjectFrameBounds" extendedState="6">
<option name="x" value="67" />
<option name="y" value="27" />
<option name="width" value="1533" />
<option name="height" value="1053" />
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
<component name="ProjectView">
<navigator proportions="" version="1">
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="Scope" />
<pane id="PackagesPane" />
<pane id="ProjectPane">
<subPane>
<expand>
<path>
<item name="bench_bin" type="b2602c69:ProjectViewProjectNode" />
<item name="bench_bin" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="bench_bin" type="b2602c69:ProjectViewProjectNode" />
<item name="bench_bin" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
</path>
</expand>
<select />
</subPane>
</pane>
</panes>
</component>
<component name="PropertiesComponent">
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="org.rust.cargo.project.model.PROJECT_DISCOVERY" value="true" />
</component>
<component name="RunDashboard">
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="3fa6f740-0ee1-4afb-b0ae-9239bf5ced3d" name="Default Changelist" comment="" />
<created>1552690262696</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1552690262696</updated>
</task>
<servers />
</component>
<component name="ToolWindowManager">
<frame x="67" y="25" width="1853" height="1055" extended-state="6" />
<layout>
<window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.23076923" />
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
<window_info id="Designer" order="2" />
<window_info id="Favorites" order="3" side_tool="true" />
<window_info anchor="bottom" id="Message" order="0" />
<window_info anchor="bottom" id="Find" order="1" />
<window_info anchor="bottom" id="Run" order="2" weight="0.32829374" />
<window_info anchor="bottom" id="Debug" order="3" weight="0.4" />
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
<window_info anchor="bottom" id="TODO" order="6" />
<window_info anchor="bottom" id="Version Control" order="7" />
<window_info anchor="bottom" id="Terminal" order="8" />
<window_info anchor="bottom" id="Event Log" order="9" side_tool="true" />
<window_info anchor="right" id="Commander" internal_type="SLIDING" order="0" type="SLIDING" weight="0.4" />
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
<window_info anchor="right" id="Maven" order="3" />
<window_info anchor="right" id="Cargo" order="4" />
<window_info anchor="right" id="Palette&#9;" order="5" />
</layout>
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/../../src/lib.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="353">
<caret line="297" column="1" lean-forward="true" selection-start-line="297" selection-start-column="1" selection-end-line="297" selection-end-column="1" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.cargo/registry/src/github.com-1ecc6299db9ec823/serde_json-1.0.39/src/de.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="279">
<caret line="2311" column="47" selection-start-line="2311" selection-start-column="47" selection-end-line="2311" selection-end-column="47" />
</state>
</provider>
</entry>
<entry file="file://$USER_HOME$/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore/str/mod.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="197">
<caret line="3895" column="11" selection-start-line="3895" selection-start-column="11" selection-end-line="3895" selection-end-column="11" />
<folding>
<element signature="e#126082#126083#0" expanded="true" />
<element signature="e#126120#126121#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/main.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-680">
<caret line="6" column="13" selection-start-line="6" selection-start-column="13" selection-end-line="6" selection-end-column="13" />
</state>
</provider>
</entry>
</component>
</project>

View File

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

89
benches/bench_example.rs Normal file
View File

@ -0,0 +1,89 @@
#![feature(test)]
extern crate bencher;
extern crate jsonpath_lib as jsonpath;
extern crate serde;
extern crate serde_json;
extern crate test;
use std::io::Read;
use serde_json::Value;
use self::test::Bencher;
fn read_json(path: &str) -> String {
let mut f = std::fs::File::open(path).unwrap();
let mut contents = String::new();
f.read_to_string(&mut contents).unwrap();
contents
}
fn get_string() -> String {
read_json("./benches/example.json")
}
fn get_json() -> Value {
let string = get_string();
serde_json::from_str(string.as_str()).unwrap()
}
fn get_path(i: usize) -> &'static str {
let paths = vec![
"$.store.book[*].author", //0
"$..author", //1
"$.store.*", //2
"$.store..price", //3
"$..book[2]", //4
"$..book[-2]", //5
"$..book[0,1]", //6
"$..book[:2]", //7
"$..book[1:2]", //8
"$..book[-2:]", //9
"$..book[2:]", //10
"$..book[?(@.isbn)]", //11
"$.store.book[?(@.price == 10)]", //12
"$..*", //13
"$..book[ ?( (@.price < 13 || $.store.bicycle.price < @.price) && @.price <=10 ) ]", //14
"$.store.book[?( (@.price < 10 || @.price > 10) && @.price > 10 )]"
];
paths[i]
}
fn _selector(b: &mut Bencher, index: usize) {
let json = get_json();
b.iter(move || {
for _ in 1..100 {
let mut selector = jsonpath::Selector::new();
let _ = selector.str_path(get_path(index));
selector.value(&json);
let r = selector.select();
if r.is_err() {
panic!()
}
}
});
}
macro_rules! selector {
($name:ident, $i:expr) => {
#[bench]
fn $name(b: &mut Bencher) { _selector(b, $i); }
};
}
selector!(example0_1, 0);
selector!(example1_1, 1);
selector!(example2_1, 2);
selector!(example3_1, 3);
selector!(example4_1, 4);
selector!(example5_1, 5);
selector!(example6_1, 6);
selector!(example7_1, 7);
selector!(example8_1, 8);
selector!(example9_1, 9);
selector!(example_10_1, 10);
selector!(example_11_1, 11);
selector!(example_12_1, 12);
selector!(example_13_1, 13);
selector!(example_14_1, 14);
selector!(example_15_1, 15);

View File

@ -34,6 +34,7 @@ __extra () {
printf "\n"
sleep 1
cd "${DIR}"/javascript && echo "NodeJs - jsonpath-rs - compile: " && time ./bench.sh nativeCompile ${ITER}
printf "\n"
}
if [ "$1" = "extra" ]; then

View File

@ -36,9 +36,8 @@ __extra () {
cd "${DIR}"/javascript && echo "NodeJs - jsonpath-wasm - compile: " && time ./bench.sh wasmCompile ${ITER}
printf "\n"
sleep 1
cd "${DIR}"/javascript && echo "NodeJs - jsonpath-wasm - compile-alloc: " && time ./bench.sh wasmCompileAlloc ${ITER}
sleep 1
cd "${DIR}"/javascript && echo "NodeJs - jsonpath-wasm - Selector: " && time ./bench.sh wasmSelectorClass ${ITER}
printf "\n"
}
if [ "$1" = "extra" ]; then

View File

@ -42,7 +42,7 @@ function getJson() {
}
const path = '$..book[?(@.price<30 && @.category=="fiction")]';
const jp = require('jsonpath');
const jpw = require('@nodejs/jsonpath-wasm');
const jpw = require('jsonpath-wasm');
const jpwRs = require('jsonpath-rs');
function jsonpath() {
@ -85,51 +85,18 @@ function wasmCompile() {
}
}
function wasmCompileAlloc() {
let ptr = jpw.allocJson(getJson());
if (ptr == 0) {
console.error('Invalid pointer');
return;
}
try {
let template = jpw.compile(path);
for (var i = 0; i < iter; i++) {
let _ = template(ptr);
}
} finally {
jpw.deallocJson(ptr);
}
}
function wasmSelect() {
for (var i = 0; i < iter; i++) {
let _ = jpw.select(getJson(), path);
}
}
function wasmSelectAlloc() {
let ptr = jpw.allocJson(getJson());
if (ptr == 0) {
console.error('Invalid pointer');
return;
}
try {
for (var i = 0; i < iter; i++) {
let _ = jpw.select(ptr, path);
}
} finally {
jpw.deallocJson(ptr);
}
}
function wasmSelectorClass() {
let selector = new jpw.Selector();
for (var i = 0; i < iter; i++) {
selector.path(path);
selector.value(jsonStr);
let _ = selector.selectToStr();
let _ = selector.select();
}
}

104
build-wasm.sh Executable file
View File

@ -0,0 +1,104 @@
#!/bin/bash
set -e
# project_root
DIR="$(pwd)"
WASM="${DIR}"/wasm
WASM_WWW="${WASM}"/www
WASM_WWW_BENCH="${WASM}"/www_bench
WASM_BROWSER_PKG="${WASM}"/browser_pkg
WASM_NODEJS_PKG="${WASM}"/nodejs_pkg
WASM_ALL_PKG="${WASM}"/all_pkg
WASM_TEST="${WASM}"/tests
DOCS="${DIR}"/docs
DOCS_BENCH="${DOCS}"/bench
__msg () {
echo ">>>>>>>>>>$1<<<<<<<<<<"
}
__cargo_clean () {
cd "${WASM}" && cargo clean && \
cd "${DIR}" && cargo clean
}
echo
__msg "clean wasm"
rm -rf \
"${WASM_NODEJS_PKG}" \
"${WASM_BROWSER_PKG}" \
"${WASM_ALL_PKG}" \
"${WASM_WWW}"/node_modules \
"${WASM_WWW_BENCH}"/node_modules \
"${WASM_WWW}"/dist \
"${WASM_WWW_BENCH}"/dist \
"${WASM_TEST}"/node_modules
if [ "$1" = "all" ]; then
__msg "clean all wasm"
__cargo_clean
fi
__msg "npm install: wasm"
cd "${WASM_WWW}" && npm install
__msg "npm install: wasm_bench"
cd "${WASM_WWW_BENCH}" && npm install
__msg "npm install: wasm test"
cd "${WASM_TEST}" && npm install
echo
echo
__msg "wasm-pack"
cd "${WASM}" && \
wasm-pack build --release --target=nodejs --out-dir "${WASM_NODEJS_PKG}"
cd "${WASM}" && \
wasm-pack build --release --target=browser --out-dir "${WASM_BROWSER_PKG}"
# && \
# wasm-pack test --chrome --firefox --headless
__msg "wasm npm packaging"
cp -r "${WASM_BROWSER_PKG}" "${WASM_ALL_PKG}/" && \
sed "s/require[\(]'\.\/jsonpath_wasm_bg/require\('\.\/jsonpath_wasm_nodejs/" "${WASM_NODEJS_PKG}/jsonpath_wasm.js" \
> "${WASM_ALL_PKG}/jsonpath_wasm_main.js" && \
sed "s/require[\(]'\.\/jsonpath_wasm/require\('\.\/jsonpath_wasm_main/" "${WASM_NODEJS_PKG}/jsonpath_wasm_bg.js" \
> "${WASM_ALL_PKG}/jsonpath_wasm_nodejs.js" && \
jq ".files += [\"jsonpath_wasm_nodejs.js\"]" ${WASM_ALL_PKG}/package.json \
| jq ".main = \"jsonpath_wasm_main.js\"" \
| jq ".keywords += [\"jsonpath\", \"json\", \"webassembly\", \"parsing\", \"rust\"]" \
> ${WASM_ALL_PKG}/temp.json && \
mv -v "${WASM_ALL_PKG}/temp.json" "${WASM_ALL_PKG}/package.json" && \
cd "${WASM_ALL_PKG}" && npm link
echo
__msg "link"
cd "${WASM_WWW}" && \
npm link jsonpath-wasm
cd "${WASM_WWW_BENCH}" && \
npm link jsonpath-wasm
cd "${WASM_TEST}" && \
npm link jsonpath-wasm
echo
echo
__msg "wasm test"
cd "${WASM_TEST}" && npm test
if [ "$1" = "all" ] || [ "$1" = "docs" ]; then
echo
__msg "docs"
cd "${WASM_WWW}" && \
npm run build &&
rm -f "${DOCS}"/*.js "${DOCS}"/*.wasm "${DOCS}"/*.html && \
cp "${WASM_WWW}"/dist/*.* "${DOCS}"/
cd "${WASM_WWW_BENCH}" && \
npm run build &&
rm -f "${DOCS_BENCH}"/*.js "${DOCS_BENCH}"/*.wasm "${DOCS_BENCH}"/*.html && \
cp "${WASM_WWW_BENCH}"/dist/*.* "${DOCS_BENCH}"/
fi
__msg "wasm done"

View File

@ -9,6 +9,8 @@ WASM_WWW="${WASM}"/www
WASM_WWW_BENCH="${WASM}"/www_bench
WASM_BROWSER_PKG="${WASM}"/browser_pkg
WASM_NODEJS_PKG="${WASM}"/nodejs_pkg
WASM_ALL_PKG="${WASM}"/all_pkg
WASM_TEST="${WASM}"/tests
BENCHES="${DIR}"/benches
BENCHES_JS="${BENCHES}"/javascript
NODEJS="${DIR}"/nodejs
@ -31,48 +33,81 @@ __msg "clean"
rm -rf \
"${WASM_NODEJS_PKG}" \
"${WASM_BROWSER_PKG}" \
"${WASM_ALL_PKG}" \
"${BENCHES_JS}"/node_modules \
"${NODEJS}"/node_modules \
"${WASM_WWW}"/node_modules \
"${WASM_WWW_BENCH}"/node_modules \
"${WASM_WWW}"/dist \
"${WASM_WWW_BENCH}"/dist
"${WASM_WWW_BENCH}"/dist \
"${WASM_TEST}"/node_modules
if [ "$1" = "all" ]; then
__msg "clean targets"
__cargo_clean
fi
__msg "npm install"
echo
__msg "npm install: wasm"
cd "${WASM_WWW}" && npm install
__msg "npm install: wasm_bench"
cd "${WASM_WWW_BENCH}" && npm install
__msg "npm install: nodejs"
cd "${NODEJS}" && npm install
__msg "npm install: benches_js"
cd "${BENCHES_JS}" && npm install
__msg "npm install: wasm test"
cd "${WASM_TEST}" && npm install
echo
echo
__msg "nodejs test"
cd "${NODEJS}" && npm test
echo
echo
__msg "wasm-pack"
cd "${WASM}" && \
wasm-pack build --release --target=nodejs --scope nodejs --out-dir nodejs_pkg && \
cd "${WASM_NODEJS_PKG}" && npm link
wasm-pack build --release --target=nodejs --out-dir "${WASM_NODEJS_PKG}"
cd "${WASM}" && \
wasm-pack build --release --target=browser --scope browser --out-dir browser_pkg && \
cd "${WASM_BROWSER_PKG}" && npm link
wasm-pack build --release --target=browser --out-dir "${WASM_BROWSER_PKG}"
# && \
# wasm-pack test --chrome --firefox --headless
__msg "wasm npm packaging"
cp -r "${WASM_BROWSER_PKG}" "${WASM_ALL_PKG}/" && \
sed "s/require[\(]'\.\/jsonpath_wasm_bg/require\('\.\/jsonpath_wasm_nodejs/" "${WASM_NODEJS_PKG}/jsonpath_wasm.js" \
> "${WASM_ALL_PKG}/jsonpath_wasm_main.js" && \
sed "s/require[\(]'\.\/jsonpath_wasm/require\('\.\/jsonpath_wasm_main/" "${WASM_NODEJS_PKG}/jsonpath_wasm_bg.js" \
> "${WASM_ALL_PKG}/jsonpath_wasm_nodejs.js" && \
jq ".files += [\"jsonpath_wasm_nodejs.js\"]" ${WASM_ALL_PKG}/package.json \
| jq ".main = \"jsonpath_wasm_main.js\"" \
| jq ".keywords += [\"jsonpath\", \"json\", \"webassembly\", \"parsing\", \"rust\"]" \
> ${WASM_ALL_PKG}/temp.json && \
mv -v "${WASM_ALL_PKG}/temp.json" "${WASM_ALL_PKG}/package.json" && \
cd "${WASM_ALL_PKG}" && npm link
echo
__msg "link"
cd "${WASM_WWW}" && \
npm link @browser/jsonpath-wasm
npm link jsonpath-wasm
cd "${WASM_WWW_BENCH}" && \
npm link @browser/jsonpath-wasm
npm link jsonpath-wasm
cd "${BENCHES_JS}" && \
npm link @nodejs/jsonpath-wasm && \
npm link jsonpath-wasm && \
npm link jsonpath-rs
cd "${WASM_TEST}" && \
npm link jsonpath-wasm
echo
echo
__msg "wasm test"
cd "${WASM_TEST}" && npm test
echo
__msg "docs"
cd "${WASM_WWW}" && \
@ -85,4 +120,4 @@ cd "${WASM_WWW_BENCH}" && \
rm -f "${DOCS_BENCH}"/*.js "${DOCS_BENCH}"/*.wasm "${DOCS_BENCH}"/*.html && \
cp "${WASM_WWW_BENCH}"/dist/*.* "${DOCS_BENCH}"/
__msg "done"
__msg "done"

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -52,50 +52,53 @@
/******/ function promiseResolve() { return Promise.resolve(); }
/******/
/******/ var wasmImportObjects = {
/******/ "../browser_pkg/jsonpath_wasm_bg.wasm": function() {
/******/ "../all_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);
/******/ return installedModules["../all_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);
/******/ return installedModules["../all_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);
/******/ return installedModules["../all_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);
/******/ "__wbg_error_8015049cb5adfca2": function(p0i32,p1i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbg_error_8015049cb5adfca2"](p0i32,p1i32);
/******/ },
/******/ "__wbindgen_number_get": function(p0i32,p1i32) {
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_number_get"](p0i32,p1i32);
/******/ "__wbindgen_object_drop_ref": function(p0i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_object_drop_ref"](p0i32);
/******/ },
/******/ "__wbindgen_object_clone_ref": function(p0i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_object_clone_ref"](p0i32);
/******/ },
/******/ "__wbindgen_string_new": function(p0i32,p1i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_string_new"](p0i32,p1i32);
/******/ },
/******/ "__wbg_call_972de3aa550c37b2": function(p0i32,p1i32,p2i32,p3i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbg_call_972de3aa550c37b2"](p0i32,p1i32,p2i32,p3i32);
/******/ },
/******/ "__wbindgen_is_string": function(p0i32) {
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_is_string"](p0i32);
/******/ return installedModules["../all_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);
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_string_get"](p0i32,p1i32);
/******/ },
/******/ "__wbindgen_debug_string": function(p0i32,p1i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_debug_string"](p0i32,p1i32);
/******/ },
/******/ "__wbindgen_throw": function(p0i32,p1i32) {
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_throw"](p0i32,p1i32);
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_throw"](p0i32,p1i32);
/******/ },
/******/ "__wbindgen_rethrow": function(p0i32) {
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_rethrow"](p0i32);
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_rethrow"](p0i32);
/******/ },
/******/ "__wbindgen_closure_wrapper103": function(p0i32,p1i32,p2i32) {
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper103"](p0i32,p1i32,p2i32);
/******/ "__wbindgen_closure_wrapper18": function(p0i32,p1i32,p2i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper18"](p0i32,p1i32,p2i32);
/******/ },
/******/ "__wbindgen_closure_wrapper105": function(p0i32,p1i32,p2i32) {
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper105"](p0i32,p1i32,p2i32);
/******/ "__wbindgen_closure_wrapper20": function(p0i32,p1i32,p2i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper20"](p0i32,p1i32,p2i32);
/******/ }
/******/ }
/******/ };
@ -185,7 +188,7 @@
/******/
/******/ // Fetch + compile chunk loading for webassembly
/******/
/******/ var wasmModules = {"0":["../browser_pkg/jsonpath_wasm_bg.wasm"]}[chunkId] || [];
/******/ var wasmModules = {"0":["../all_pkg/jsonpath_wasm_bg.wasm"]}[chunkId] || [];
/******/
/******/ wasmModules.forEach(function(wasmModuleId) {
/******/ var installedWasmModuleData = installedWasmModules[wasmModuleId];
@ -195,7 +198,7 @@
/******/ promises.push(installedWasmModuleData);
/******/ else {
/******/ var importObject = wasmImportObjects[wasmModuleId]();
/******/ var req = fetch(__webpack_require__.p + "" + {"../browser_pkg/jsonpath_wasm_bg.wasm":"68fa958468b8cdcb12e4"}[wasmModuleId] + ".module.wasm");
/******/ var req = fetch(__webpack_require__.p + "" + {"../all_pkg/jsonpath_wasm_bg.wasm":"d60993d3a441db221b47"}[wasmModuleId] + ".module.wasm");
/******/ var promise;
/******/ if(importObject instanceof Promise && typeof WebAssembly.compileStreaming === 'function') {
/******/ promise = Promise.all([WebAssembly.compileStreaming(req), importObject]).then(function(items) {

Binary file not shown.

57
docs/bootstrap.js vendored
View File

@ -52,50 +52,53 @@
/******/ function promiseResolve() { return Promise.resolve(); }
/******/
/******/ var wasmImportObjects = {
/******/ "../browser_pkg/jsonpath_wasm_bg.wasm": function() {
/******/ "../all_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);
/******/ return installedModules["../all_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);
/******/ return installedModules["../all_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);
/******/ return installedModules["../all_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);
/******/ "__wbg_error_8015049cb5adfca2": function(p0i32,p1i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbg_error_8015049cb5adfca2"](p0i32,p1i32);
/******/ },
/******/ "__wbindgen_number_get": function(p0i32,p1i32) {
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_number_get"](p0i32,p1i32);
/******/ "__wbindgen_object_drop_ref": function(p0i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_object_drop_ref"](p0i32);
/******/ },
/******/ "__wbindgen_object_clone_ref": function(p0i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_object_clone_ref"](p0i32);
/******/ },
/******/ "__wbindgen_string_new": function(p0i32,p1i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_string_new"](p0i32,p1i32);
/******/ },
/******/ "__wbg_call_972de3aa550c37b2": function(p0i32,p1i32,p2i32,p3i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbg_call_972de3aa550c37b2"](p0i32,p1i32,p2i32,p3i32);
/******/ },
/******/ "__wbindgen_is_string": function(p0i32) {
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_is_string"](p0i32);
/******/ return installedModules["../all_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);
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_string_get"](p0i32,p1i32);
/******/ },
/******/ "__wbindgen_debug_string": function(p0i32,p1i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_debug_string"](p0i32,p1i32);
/******/ },
/******/ "__wbindgen_throw": function(p0i32,p1i32) {
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_throw"](p0i32,p1i32);
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_throw"](p0i32,p1i32);
/******/ },
/******/ "__wbindgen_rethrow": function(p0i32) {
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_rethrow"](p0i32);
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_rethrow"](p0i32);
/******/ },
/******/ "__wbindgen_closure_wrapper103": function(p0i32,p1i32,p2i32) {
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper103"](p0i32,p1i32,p2i32);
/******/ "__wbindgen_closure_wrapper18": function(p0i32,p1i32,p2i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper18"](p0i32,p1i32,p2i32);
/******/ },
/******/ "__wbindgen_closure_wrapper105": function(p0i32,p1i32,p2i32) {
/******/ return installedModules["../browser_pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper105"](p0i32,p1i32,p2i32);
/******/ "__wbindgen_closure_wrapper20": function(p0i32,p1i32,p2i32) {
/******/ return installedModules["../all_pkg/jsonpath_wasm.js"].exports["__wbindgen_closure_wrapper20"](p0i32,p1i32,p2i32);
/******/ }
/******/ }
/******/ };
@ -185,7 +188,7 @@
/******/
/******/ // Fetch + compile chunk loading for webassembly
/******/
/******/ var wasmModules = {"0":["../browser_pkg/jsonpath_wasm_bg.wasm"]}[chunkId] || [];
/******/ var wasmModules = {"0":["../all_pkg/jsonpath_wasm_bg.wasm"]}[chunkId] || [];
/******/
/******/ wasmModules.forEach(function(wasmModuleId) {
/******/ var installedWasmModuleData = installedWasmModules[wasmModuleId];
@ -195,7 +198,7 @@
/******/ promises.push(installedWasmModuleData);
/******/ else {
/******/ var importObject = wasmImportObjects[wasmModuleId]();
/******/ var req = fetch(__webpack_require__.p + "" + {"../browser_pkg/jsonpath_wasm_bg.wasm":"68fa958468b8cdcb12e4"}[wasmModuleId] + ".module.wasm");
/******/ var req = fetch(__webpack_require__.p + "" + {"../all_pkg/jsonpath_wasm_bg.wasm":"d60993d3a441db221b47"}[wasmModuleId] + ".module.wasm");
/******/ var promise;
/******/ if(importObject instanceof Promise && typeof WebAssembly.compileStreaming === 'function') {
/******/ promise = Promise.all([WebAssembly.compileStreaming(req), importObject]).then(function(items) {

Binary file not shown.

View File

@ -1,10 +1,82 @@
<!DOCTYPE html>
<html>
<head>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-3020058-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-3020058-1');
</script>
<meta charset="utf-8">
<meta property="og:image" content="https://avatars0.githubusercontent.com/u/1104423?s=400&amp;v=4"/>
<meta property="og:site_name" content="GitHub"/>
<meta property="og:type" content="object"/>
<meta property="og:title" content="freestrings/jsonpath"/>
<meta property="og:url" content="https://github.com/freestrings/jsonpath"/>
<meta property="og:description" content="JsonPath evaluator with Webassembly via Rust - freestrings/jsonpath"/>
<title>JsonPath evaluator - Webassembly via Rust</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<link href="./css/github-corner.css" rel="stylesheet" type="text/css">
<style>
/**
* GitHub Corners, page css
* Author: Tim Holman
*/
.code textarea {
border: 2px solid #eee;
outline: 0px;
height: 100%;
width: 100%;
font-family: monospace;
font-size: 10px;
}
.github-corner:hover .octo-arm {
animation: octocat-wave 560ms ease-in-out;
}
@keyframes octocat-wave {
0% {
transform: rotate(0deg);
}
20% {
transform: rotate(-25deg);
}
40% {
transform: rotate(10deg);
}
60% {
transform: rotate(-25deg);
}
80% {
transform: rotate(10deg);
}
100% {
transform: rotate(0deg);
}
}
@media (max-width: 500px) {
.github-corner:hover .octo-arm {
animation: none;
}
.github-corner .octo-arm {
animation: octocat-wave 560ms ease-in-out;
}
}
</style>
</head>
<body role="document">
<div class="container">
@ -92,7 +164,9 @@
<svg width="80" height="80" viewBox="0 0 250 250" style="position: absolute; top: 0px; right: 0px; border: 0px;" aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" fill="#151513"></path>
<path class="octo-arm" d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="#ffffff" style="transform-origin: 130px 106px;"></path>
<path class="octo-body" d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="#ffffff"></path>
<path class="octo-body"
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
fill="#ffffff"></path>
</svg>
</a>
<script src="./bootstrap.js"></script>

2
examples/browser/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
node_modules
dist

5
examples/browser/bootstrap.js vendored Normal file
View File

@ -0,0 +1,5 @@
// A dependency graph that contains any wasm must all be imported
// asynchronously. This `bootstrap.js` file does the single async import, so
// that no one else needs to worry about it again.
import("./index.js")
.catch(e => console.error("Error importing `index.js`:", e));

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>jsonpath-wasm-browser-example</title>
</head>
<body>
<script src="./bootstrap.js"></script>
</body>
</html>

32
examples/browser/index.js Normal file
View File

@ -0,0 +1,32 @@
import * as jsonpath from "jsonpath-wasm";
let jsonObj = {
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]
};
let ret = [
{"name": "친구3", "age": 30},
{"name": "친구1", "age": 20}
];
let selector = new jsonpath.Selector();
selector.path('$..friends[0]');
selector.value(jsonObj);
let selectToObj = selector.selectTo();
let selectToString = selector.selectToStr();
console.log(
JSON.stringify(ret) == JSON.stringify(selectToObj),
JSON.stringify(ret) == selectToString
);

5324
examples/browser/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
{
"name": "jsonpath-wasm-browser-example",
"version": "0.1.0",
"main": "index.js",
"scripts": {
"build": "webpack --config webpack.config.js",
"start": "webpack-dev-server"
},
"devDependencies": {
"copy-webpack-plugin": "^5.0.1",
"webpack": "^4.29.6",
"webpack-cli": "^3.3.0",
"webpack-dev-server": "^3.2.1"
}
}

View File

@ -0,0 +1,14 @@
const CopyWebpackPlugin = require("copy-webpack-plugin");
const path = require('path');
module.exports = {
entry: "./bootstrap.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bootstrap.js",
},
mode: "development",
plugins: [
new CopyWebpackPlugin(['index.html'])
]
};

2
examples/nodejs-rs/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
node_modules
dist

View File

@ -0,0 +1,31 @@
const jsonpath = require('jsonpath-rs');
let jsonObj = {
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]
};
let ret = [
{"name": "친구3", "age": 30},
{"name": "친구1", "age": 20}
];
let selector = new jsonpath.Selector();
selector.path('$..friends[0]');
selector.value(jsonObj);
let selectToObj = selector.selectTo();
let selectToString = selector.selectToStr();
console.log(
JSON.stringify(ret) == JSON.stringify(selectToObj),
JSON.stringify(ret) == selectToString
);

658
examples/nodejs-rs/package-lock.json generated Normal file
View File

@ -0,0 +1,658 @@
{
"name": "jsonpath-rs-example",
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"ansi-escape-sequences": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-4.1.0.tgz",
"integrity": "sha512-dzW9kHxH011uBsidTXd14JXgzye/YLb2LzeKZ4bsgl/Knwx8AtbSFkkGxagdNOoh0DlqHCmfiEjWKBaqjOanVw==",
"requires": {
"array-back": "^3.0.1"
},
"dependencies": {
"array-back": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz",
"integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q=="
}
}
},
"ansi-escapes": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
"integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ=="
},
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"requires": {
"color-convert": "^1.9.0"
}
},
"array-back": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz",
"integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==",
"requires": {
"typical": "^2.6.1"
}
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"builtins": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz",
"integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og="
},
"chalk": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz",
"integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==",
"requires": {
"ansi-styles": "^3.1.0",
"escape-string-regexp": "^1.0.5",
"supports-color": "^4.0.0"
}
},
"chardet": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz",
"integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I="
},
"cli-cursor": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
"integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
"requires": {
"restore-cursor": "^2.0.0"
}
},
"cli-width": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
"integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk="
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
},
"command-line-args": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-4.0.7.tgz",
"integrity": "sha512-aUdPvQRAyBvQd2n7jXcsMDz68ckBJELXNzBybCHOibUWEg0mWTnaYCSRU8h9R+aNRSvDihJtssSRCiDRpLaezA==",
"requires": {
"array-back": "^2.0.0",
"find-replace": "^1.0.3",
"typical": "^2.6.1"
}
},
"command-line-commands": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/command-line-commands/-/command-line-commands-2.0.1.tgz",
"integrity": "sha512-m8c2p1DrNd2ruIAggxd/y6DgygQayf6r8RHwchhXryaLF8I6koYjoYroVP+emeROE9DXN5b9sP1Gh+WtvTTdtQ==",
"requires": {
"array-back": "^2.0.0"
}
},
"command-line-usage": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-4.1.0.tgz",
"integrity": "sha512-MxS8Ad995KpdAC0Jopo/ovGIroV/m0KHwzKfXxKag6FHOkGsH8/lv5yjgablcRxCJJC0oJeUMuO/gmaq+Wq46g==",
"requires": {
"ansi-escape-sequences": "^4.0.0",
"array-back": "^2.0.0",
"table-layout": "^0.4.2",
"typical": "^2.6.1"
}
},
"commander": {
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
"integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
"optional": true
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"deep-extend": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"external-editor": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
"integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==",
"requires": {
"chardet": "^0.4.0",
"iconv-lite": "^0.4.17",
"tmp": "^0.0.33"
}
},
"figures": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
"integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
"requires": {
"escape-string-regexp": "^1.0.5"
}
},
"find-replace": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/find-replace/-/find-replace-1.0.3.tgz",
"integrity": "sha1-uI5zZNLZyVlVnziMZmcNYTBEH6A=",
"requires": {
"array-back": "^1.0.4",
"test-value": "^2.1.0"
},
"dependencies": {
"array-back": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz",
"integrity": "sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=",
"requires": {
"typical": "^2.6.0"
}
}
}
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"git-config": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/git-config/-/git-config-0.0.7.tgz",
"integrity": "sha1-qcij7wendsPXImE1bYtye2IgKyg=",
"requires": {
"iniparser": "~1.0.5"
}
},
"glob": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"handlebars": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz",
"integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==",
"requires": {
"neo-async": "^2.6.0",
"optimist": "^0.6.1",
"source-map": "^0.6.1",
"uglify-js": "^3.1.4"
}
},
"has-flag": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
"integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE="
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"iniparser": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/iniparser/-/iniparser-1.0.5.tgz",
"integrity": "sha1-g21r7+bfv87gvM8c+fKsxwJ/eD0="
},
"inquirer": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz",
"integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==",
"requires": {
"ansi-escapes": "^3.0.0",
"chalk": "^2.0.0",
"cli-cursor": "^2.1.0",
"cli-width": "^2.0.0",
"external-editor": "^2.0.4",
"figures": "^2.0.0",
"lodash": "^4.3.0",
"mute-stream": "0.0.7",
"run-async": "^2.2.0",
"rx-lite": "^4.0.8",
"rx-lite-aggregates": "^4.0.8",
"string-width": "^2.1.0",
"strip-ansi": "^4.0.0",
"through": "^2.3.6"
}
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
},
"is-promise": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
"integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o="
},
"jsonpath-rs": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/jsonpath-rs/-/jsonpath-rs-0.1.7.tgz",
"integrity": "sha512-BSuCWJK5PaTevsPHmFaLb9kzoc1Wh56+TBm6XH+gObIKA8Z3SQp6gUrgibGlApCYipha4IDo59StrdyVcvVPqA==",
"requires": {
"neon-cli": "^0.2.0"
}
},
"lodash": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
},
"lodash.padend": {
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz",
"integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4="
},
"mimic-fn": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
"integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
"integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": {
"minimist": "0.0.8"
},
"dependencies": {
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
}
}
},
"mute-stream": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
"integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="
},
"neo-async": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz",
"integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA=="
},
"neon-cli": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/neon-cli/-/neon-cli-0.2.0.tgz",
"integrity": "sha512-IsrxCyUcuAyWiq4Z+JnTXrjurj2SAL2VtWnCXS8iBYGJeIs1NIhFuLaM6fe7+rOyFfDcqUUTWGxZmkvUqwweRA==",
"requires": {
"chalk": "~2.1.0",
"command-line-args": "^4.0.2",
"command-line-commands": "^2.0.0",
"command-line-usage": "^4.0.0",
"git-config": "0.0.7",
"handlebars": "^4.0.3",
"inquirer": "^3.0.6",
"mkdirp": "^0.5.1",
"quickly-copy-file": "^1.0.0",
"rimraf": "^2.6.1",
"rsvp": "^4.6.1",
"semver": "^5.1.0",
"toml": "^2.3.0",
"ts-typed-json": "^0.2.2",
"validate-npm-package-license": "^3.0.1",
"validate-npm-package-name": "^3.0.0"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1"
}
},
"onetime": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
"integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
"requires": {
"mimic-fn": "^1.0.0"
}
},
"optimist": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
"integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
"requires": {
"minimist": "~0.0.1",
"wordwrap": "~0.0.2"
}
},
"os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
},
"quickly-copy-file": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/quickly-copy-file/-/quickly-copy-file-1.0.0.tgz",
"integrity": "sha1-n4/wZiMFEO50IrASFHKwk6hpCFk=",
"requires": {
"mkdirp": "~0.5.0"
}
},
"reduce-flatten": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz",
"integrity": "sha1-JYx479FT3fk8tWEjf2EYTzaW4yc="
},
"restore-cursor": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
"integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
"requires": {
"onetime": "^2.0.0",
"signal-exit": "^3.0.2"
}
},
"rimraf": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
"requires": {
"glob": "^7.1.3"
}
},
"rsvp": {
"version": "4.8.4",
"resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.4.tgz",
"integrity": "sha512-6FomvYPfs+Jy9TfXmBpBuMWNH94SgCsZmJKcanySzgNNP6LjWxBvyLTa9KaMfDDM5oxRfrKDB0r/qeRsLwnBfA=="
},
"run-async": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
"integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
"requires": {
"is-promise": "^2.1.0"
}
},
"rx-lite": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz",
"integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ="
},
"rx-lite-aggregates": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz",
"integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=",
"requires": {
"rx-lite": "*"
}
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"semver": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA=="
},
"signal-exit": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"spdx-correct": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
"integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
"requires": {
"spdx-expression-parse": "^3.0.0",
"spdx-license-ids": "^3.0.0"
}
},
"spdx-exceptions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
"integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA=="
},
"spdx-expression-parse": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
"integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
"requires": {
"spdx-exceptions": "^2.1.0",
"spdx-license-ids": "^3.0.0"
}
},
"spdx-license-ids": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz",
"integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA=="
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"requires": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
}
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"requires": {
"ansi-regex": "^3.0.0"
}
},
"supports-color": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz",
"integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=",
"requires": {
"has-flag": "^2.0.0"
}
},
"table-layout": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.4.tgz",
"integrity": "sha512-uNaR3SRMJwfdp9OUr36eyEi6LLsbcTqTO/hfTsNviKsNeyMBPICJCC7QXRF3+07bAP6FRwA8rczJPBqXDc0CkQ==",
"requires": {
"array-back": "^2.0.0",
"deep-extend": "~0.6.0",
"lodash.padend": "^4.6.1",
"typical": "^2.6.1",
"wordwrapjs": "^3.0.0"
}
},
"test-value": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/test-value/-/test-value-2.1.0.tgz",
"integrity": "sha1-Edpv9nDzRxpztiXKTz/c97t0gpE=",
"requires": {
"array-back": "^1.0.3",
"typical": "^2.6.0"
},
"dependencies": {
"array-back": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz",
"integrity": "sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=",
"requires": {
"typical": "^2.6.0"
}
}
}
},
"through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
},
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
"requires": {
"os-tmpdir": "~1.0.2"
}
},
"toml": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/toml/-/toml-2.3.6.tgz",
"integrity": "sha512-gVweAectJU3ebq//Ferr2JUY4WKSDe5N+z0FvjDncLGyHmIDoxgY/2Ie4qfEIDm4IS7OA6Rmdm7pdEEdMcV/xQ=="
},
"ts-typed-json": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/ts-typed-json/-/ts-typed-json-0.2.2.tgz",
"integrity": "sha1-UxhL7ok+RZkbc8jEY6OLWeJ81H4=",
"requires": {
"rsvp": "^3.5.0"
},
"dependencies": {
"rsvp": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz",
"integrity": "sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw=="
}
}
},
"typical": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz",
"integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0="
},
"uglify-js": {
"version": "3.5.4",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.4.tgz",
"integrity": "sha512-GpKo28q/7Bm5BcX9vOu4S46FwisbPbAmkkqPnGIpKvKTM96I85N6XHQV+k4I6FA2wxgLhcsSyHoNhzucwCflvA==",
"optional": true,
"requires": {
"commander": "~2.20.0",
"source-map": "~0.6.1"
}
},
"validate-npm-package-license": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
"integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
"requires": {
"spdx-correct": "^3.0.0",
"spdx-expression-parse": "^3.0.0"
}
},
"validate-npm-package-name": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz",
"integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=",
"requires": {
"builtins": "^1.0.3"
}
},
"wordwrap": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
"integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
},
"wordwrapjs": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz",
"integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==",
"requires": {
"reduce-flatten": "^1.0.1",
"typical": "^2.6.1"
}
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}
}
}

View File

@ -0,0 +1,9 @@
{
"name": "jsonpath-rs-example",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"jsonpath-rs": "0"
}
}

2
examples/nodejs-wasm/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
node_modules
dist

View File

@ -0,0 +1,31 @@
const jsonpath = require('jsonpath-wasm');
let jsonObj = {
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]
};
let ret = [
{"name": "친구3", "age": 30},
{"name": "친구1", "age": 20}
];
let selector = new jsonpath.Selector();
selector.path('$..friends[0]');
selector.value(jsonObj);
let selectToObj = selector.selectTo();
let selectToString = selector.selectToStr();
console.log(
JSON.stringify(ret) == JSON.stringify(selectToObj),
JSON.stringify(ret) == selectToString
);

View File

@ -0,0 +1,9 @@
{
"name": "jsonpath-wasm-nodejs-example",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"jsonpath-wasm": "0"
}
}

View File

@ -2,7 +2,7 @@
[![Build Status](https://travis-ci.org/freestrings/jsonpath.svg?branch=master)](https://travis-ci.org/freestrings/jsonpath)
It is [JsonPath](https://goessner.net/articles/JsonPath/) implementation. The core implementation is written in Rust.
It is native-addon of [jsonpath_lib](https://github.com/freestrings/jsonpath) that is [JsonPath](https://goessner.net/articles/JsonPath/) engine written in Rust.
## Notice
@ -10,18 +10,21 @@ Pre-built 바이너리는 제공하진 않고 소스를 컴파일해서 설치
Build from source instead of using pre-built binary, and if Rust is not installed, the latest version is automatically installed.
> Not yet tested in Windows
> Not yet tested in Windows.
> Supported node version is under v12.0.
## APIs
* [jsonpath.Selector](#jsonpathselector)
* [jsonpath.select(json: string|object, jsonpath: string)](#json-stringobject-jsonpath-string)
* [jsonpath.compile(jsonpath: string)](#compilejsonpath-string)
* [jsonpath.selector(json: string|object)](#selectorjson-stringobject)
* [Simple time check](https://github.com/freestrings/jsonpath/wiki/Simple-timecheck-jsonpath-native)
* [Other Examples](https://github.com/freestrings/jsonpath/wiki/Javascript-examples)
<details><summary><b>npm package</b></summary>
### jsonpath.Selector
```javascript
const jsonpath = require('jsonpath-rs');
```
</details>
<details><summary><b>Javascript - jsonpath.Selector class</b></summary>
```javascript
let jsonObj = {
@ -37,28 +40,89 @@ let jsonObj = {
]
};
let selector = new jsonpath.Selector().value(jsonObj);
let ret = [
{"name": "친구3", "age": 30},
{"name": "친구1", "age": 20}
];
let selector = new jsonpath.Selector()
.path('$..friends[0]')
.value(jsonObj);
let retObj = selector.select();
console.log(JSON.stringify(ret) == JSON.stringify(retObj));
// => true
```
</details>
<details><summary><b>Javascript - jsonpath.SelectorMut class</b></summary>
빌더 패턴 제약은 `Selector class`와 동일하다.
```javascript
let jsonObj = {
'school': {
'friends': [
{'name': '친구1', 'age': 20},
{'name': '친구2', 'age': 20},
],
},
'friends': [
{'name': '친구3', 'age': 30},
{'name': '친구4'},
],
};
let selector = new jsonpath.SelectorMut();
selector.path('$..[?(@.age == 20)]');
{
let jsonObj = selector.path('$..[?(@.age >= 30)]').selectTo();
let resultObj = [{"name": "친구3", "age": 30}];
console.log(JSON.stringify(jsonObj) === JSON.stringify(resultObj));
selector.value(jsonObj);
selector.deleteValue();
let resultObj = {
'school': {'friends': [null, null]},
'friends': [
{'name': '친구3', 'age': 30},
{'name': '친구4'},
],
};
console.log(JSON.stringify(selector.take()) !== JSON.stringify(resultObj));
// => true
}
{
let jsonObj = selector.path('$..[?(@.age == 20)]').selectTo();
let resultObj = [{"name": "친구1", "age": 20}, {"name": "친구2", "age": 20}];
console.log(JSON.stringify(jsonObj) === JSON.stringify(resultObj));
}
selector.value(jsonObj);
selector.replaceWith((v) => {
v.age = v.age * 2;
return v;
});
{
let jsonObj = selector.value({"friends": [ {"name": "친구5", "age": 20} ]}).selectTo();
let resultObj = [{"name": "친구5", "age": 20}];
console.log(JSON.stringify(jsonObj) === JSON.stringify(resultObj));
let resultObj = {
'school': {
'friends': [
{'name': '친구1', 'age': 40},
{'name': '친구2', 'age': 40},
],
},
'friends': [
{'name': '친구3', 'age': 30},
{'name': '친구4'},
],
};
console.log(JSON.stringify(selector.take()) !== JSON.stringify(resultObj));
// => true
}
```
### jsonpath.select(json: string|object, jsonpath: string)
</details>
<details><summary><b>Javascript - jsonpath.select(json: string|object, jsonpath: string)</b></summary>
```javascript
let jsonObj = {
@ -91,7 +155,9 @@ console.log(
// => true, true
```
### jsonpath.compile(jsonpath: string)
</details>
<details><summary><b>Javascript - jsonpath.compile(jsonpath: string)</b></summary>
```javascript
let template = jsonpath.compile('$..friends[0]');
@ -149,8 +215,10 @@ console.log(
// => true, true
```
</details>
### jsonpath.selector(json: string|object)
<details><summary><b>Javascript - jsonpath.selector(json: string|object)</b></summary>
```javascript
let jsonObj = {
@ -190,3 +258,77 @@ console.log(
// => true, true
```
</details>
<details><summary><b>Javascript - jsonpath.deleteValue(json: string|object, path: string)</b></summary>
```javascript
let jsonObj = {
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]
};
let _1 = jsonpath.deleteValue(jsonObj, '$..friends[0]');
let result = jsonpath.deleteValue(_1, '$..friends[1]');
console.log(JSON.stringify(result) !== JSON.stringify({
"school": { "friends": [null, null]},
"friends": [null, null]
}));
// => true
```
</details>
<details><summary><b>Javascript - jsonpath.replaceWith(json: string|object, path: string, fun: function(json: object) => json: object</b></summary>
```javascript
let jsonObj = {
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]
};
let result = jsonpath.replaceWith(jsonObj, '$..friends[0]', (v) => {
v.age = v.age * 2;
return v;
});
console.log(JSON.stringify(result) === JSON.stringify({
"school": {
"friends": [
{"name": "친구1", "age": 40},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 60},
{"name": "친구4"}
]
}));
// => true
```
</details>
[Javascript - Other Examples](https://github.com/freestrings/jsonpath/wiki/Javascript-examples)

View File

@ -7,5 +7,4 @@ if ! [ -x "$(command -v rustc)" ]; then
export PATH="$HOME/.cargo/bin:$PATH"
source "$HOME/.cargo/env"
fi
neon build --release
check-node-version --node '<12.0' && neon build --release

View File

@ -1,4 +1,12 @@
const { CompileFn, SelectorFn, selectStr, Selector: _Selector } = require('../native');
const {
CompileFn,
SelectorFn,
selectStr,
deleteValue: _deleteValue,
replaceWith: _replaceWith,
Selector: _Selector,
SelectorMut: _SelectorMut
} = require('../native');
function compile(path) {
let compile = new CompileFn(path);
@ -27,6 +35,30 @@ function select(json, path) {
return JSON.parse(selectStr(json, path));
}
function deleteValue(json, path) {
if(typeof json != 'string') {
json = JSON.stringify(json)
}
return JSON.parse(_deleteValue(json, path));
}
function replaceWith(json, path, fun) {
if(typeof json != 'string') {
json = JSON.stringify(json)
}
let result = _replaceWith(json, path, (v) => {
let result = fun(JSON.parse(v));
if(typeof result != 'string') {
result = JSON.stringify(result)
}
return result;
});
if(typeof result == 'string') {
result = JSON.parse(result);
}
return result;
}
class Selector {
constructor() {
this._selector = new _Selector();
@ -42,23 +74,76 @@ class Selector {
if(typeof json != 'string') {
json = JSON.stringify(json)
}
this._selector.value_from_str(json);
this._selector.value(json);
return this;
}
selectToStr() {
return this._selector.select_to_str();
select() {
return JSON.parse(this._selector.select());
}
}
class SelectorMut {
constructor() {
return this;
}
selectTo() {
return JSON.parse(this.selectToStr());
path(path) {
this._path = path;
return this;
}
value(json) {
if(typeof json != 'string') {
json = JSON.stringify(json)
}
this._json = json;
return this;
}
deleteValue() {
let selector = new _SelectorMut();
if(!this._path) {
selector.emptyPathError();
return;
}
if(!this._json) {
selector.emptyValueError();
return;
}
this._json = deleteValue(this._json, this._path);
return this;
}
replaceWith(fun) {
let selector = new _SelectorMut();
if(!this._path) {
selector.emptyPathError();
return;
}
if(!this._json) {
selector.emptyValueError();
return;
}
this._json = replaceWith(this._json, this._path, fun);
return this;
}
take() {
let json = this._json;
delete this._json;
return json;
}
}
module.exports = {
compile,
selector,
select,
Selector
deleteValue,
replaceWith,
Selector,
SelectorMut
};

View File

@ -1,6 +1,6 @@
[package]
name = "jsonpath4nodejs"
version = "0.1.1"
version = "0.2.2"
authors = ["Changseok Han <freestrings@gmail.com>"]
description = "jsonpath_lib bindings for nodejs"
keywords = ["library", "jsonpath", "json", "nodejs"]
@ -14,7 +14,8 @@ exclude = ["artifacts.json", "index.node"]
neon-build = "0.2.0"
[dependencies]
jsonpath_lib = "0.1.9"
jsonpath_lib = "0.2.2"
#jsonpath_lib = { path = "../../" }
neon = "0.2.0"
neon-serde = "0.1.1"
serde_json = { version = "1.0", features = ["preserve_order"] }

View File

@ -4,13 +4,9 @@ extern crate neon;
extern crate neon_serde;
extern crate serde_json;
use jsonpath::filter::value_filter::JsonValueFilter;
use jsonpath::parser::parser::{Node, NodeVisitor, Parser};
use jsonpath::ref_value::model::{RefValue, RefValueWrapper};
use jsonpath::Selector;
use jsonpath::{JsonPathError, Node, Parser, Selector};
use neon::prelude::*;
use serde_json::Value;
use std::ops::Deref;
///
/// `neon_serde::from_value` has very poor performance.
@ -22,7 +18,7 @@ fn select(mut ctx: FunctionContext) -> JsResult<JsValue> {
match jsonpath::select(&json, path.as_str()) {
Ok(value) => Ok(neon_serde::to_value(&mut ctx, &value)?),
Err(e) => panic!("{:?}", e)
Err(e) => panic!("{:?}", e),
}
}
@ -31,100 +27,191 @@ fn select_str(mut ctx: FunctionContext) -> JsResult<JsValue> {
let path = ctx.argument::<JsString>(1)?.value();
match jsonpath::select_as_str(&json_val, path.as_str()) {
Ok(value) => Ok(JsString::new(&mut ctx, &value).upcast()),
Err(e) => panic!("{:?}", e)
Err(e) => panic!("{:?}", e),
}
}
pub struct CompileFn {
node: Node
fn delete(mut ctx: FunctionContext) -> JsResult<JsValue> {
let json_val = ctx.argument::<JsString>(0)?.value();
let json: Value = match serde_json::from_str(&json_val) {
Ok(value) => value,
Err(e) => panic!("{:?}", JsonPathError::Serde(e.to_string())),
};
let path = ctx.argument::<JsString>(1)?.value();
match jsonpath::delete(json, &path) {
Ok(value) => Ok(JsString::new(
&mut ctx,
match serde_json::to_string(&value) {
Ok(value) => value,
Err(e) => panic!("{:?}", JsonPathError::Serde(e.to_string())),
},
)
.upcast()),
Err(e) => panic!("{:?}", e),
}
}
pub struct SelectorFn {
json: RefValueWrapper
fn replace_with(mut ctx: FunctionContext) -> JsResult<JsValue> {
let json_val = ctx.argument::<JsString>(0)?.value();
let json: Value = match serde_json::from_str(&json_val) {
Ok(value) => value,
Err(e) => panic!("{:?}", JsonPathError::Serde(e.to_string())),
};
let path = ctx.argument::<JsString>(1)?.value();
let fun = ctx.argument::<JsFunction>(2)?;
match jsonpath::replace_with(json, &path, &mut |v| {
let json_str = JsString::new(
&mut ctx,
match serde_json::to_string(v) {
Ok(value) => value,
Err(e) => panic!("{:?}", JsonPathError::Serde(e.to_string())),
},
);
let null = ctx.null();
let args = vec![ctx.string(json_str.value())];
let result = match fun.call(&mut ctx, null, args) {
Ok(result) => result,
Err(e) => panic!("{:?}", e),
};
let json_str = match result.downcast::<JsString>() {
Ok(v) => v.value(),
Err(e) => panic!("{:?}", JsonPathError::Serde(e.to_string())),
};
match serde_json::from_str(&json_str) {
Ok(v) => v,
Err(e) => panic!("{:?}", JsonPathError::Serde(e.to_string())),
}
}) {
Ok(value) => Ok(JsString::new(
&mut ctx,
match serde_json::to_string(&value) {
Ok(value) => value,
Err(e) => panic!("{:?}", JsonPathError::Serde(e.to_string())),
},
)
.upcast()),
Err(e) => panic!("{:?}", e),
}
}
pub struct SelectorCls {
selector: Selector
node: Option<Node>,
value: Option<Value>,
}
impl SelectorCls {
fn path(&mut self, path: &str) {
let node = match Parser::compile(path) {
Ok(node) => node,
Err(e) => panic!("{:?}", e),
};
self.node = Some(node);
}
fn value(&mut self, json_str: &str) {
let value: Value = match serde_json::from_str(&json_str) {
Ok(value) => value,
Err(e) => panic!("{:?}", JsonPathError::Serde(e.to_string())),
};
self.value = Some(value);
}
fn select(&self) -> String {
let node = match &self.node {
Some(node) => node,
None => panic!("{:?}", JsonPathError::EmptyPath),
};
let value = match &self.value {
Some(value) => value,
None => panic!("{:?}", JsonPathError::EmptyValue),
};
let mut selector = Selector::new();
selector.compiled_path(node);
selector.value(&value);
match selector.select_as_str() {
Ok(ret) => ret,
Err(e) => panic!("{:?}", e),
}
}
}
pub struct SelectorMutCls {}
declare_types! {
pub class JsCompileFn for CompileFn {
pub class JsCompileFn for SelectorCls {
init(mut ctx) {
let path = ctx.argument::<JsString>(0)?.value();
let mut parser = Parser::new(path.as_str());
let node = match parser.compile() {
let node = match Parser::compile(path.as_str()) {
Ok(node) => node,
Err(e) => panic!("{:?}", e)
};
Ok(CompileFn { node })
Ok(SelectorCls { node: Some(node), value: None })
}
method template(mut ctx) {
let this = ctx.this();
let node = {
let guard = ctx.lock();
let this = this.borrow(&guard);
this.node.clone()
};
let mut this = ctx.this();
let json_str = ctx.argument::<JsString>(0)?.value();
let ref_value: RefValue = match serde_json::from_str(&json_str) {
Ok(ref_value) => ref_value,
Err(e) => panic!("{:?}", e)
{
let guard = ctx.lock();
let mut this = this.borrow_mut(&guard);
let value: Value = match serde_json::from_str(&json_str) {
Ok(value) => value,
Err(e) => panic!("{:?}", JsonPathError::Serde(e.to_string()))
};
this.value = Some(value);
};
let mut jf = JsonValueFilter::new_from_value(ref_value.into());
jf.visit(node);
match serde_json::to_string(&jf.take_value().deref()) {
Ok(json_str) => Ok(JsString::new(&mut ctx, &json_str).upcast()),
Err(e) => panic!("{:?}", e)
}
let result_str = {
let guard = ctx.lock();
let this = this.borrow(&guard);
this.select()
};
Ok(JsString::new(&mut ctx, &result_str).upcast())
}
}
pub class JsSelectorFn for SelectorFn {
pub class JsSelectorFn for SelectorCls {
init(mut ctx) {
let json_str = ctx.argument::<JsString>(0)?.value();
let ref_value: RefValue = match serde_json::from_str(&json_str) {
Ok(ref_value) => ref_value,
Err(e) => panic!("{:?}", e)
let value: Value = match serde_json::from_str(&json_str) {
Ok(value) => value,
Err(e) => panic!("{:?}", JsonPathError::Serde(e.to_string()))
};
Ok(SelectorFn { json: ref_value.into() })
Ok(SelectorCls { node: None, value: Some(value) })
}
method select(mut ctx) {
let this = ctx.this();
let json = {
let guard = ctx.lock();
let this = this.borrow(&guard);
this.json.clone()
};
let mut this = ctx.this();
let path = ctx.argument::<JsString>(0)?.value();
let mut parser = Parser::new(path.as_str());
{
let guard = ctx.lock();
let mut this = this.borrow_mut(&guard);
this.path(&path);
}
let node = match parser.compile() {
Ok(node) => node,
Err(e) => panic!("{:?}", e)
let result_str = {
let guard = ctx.lock();
let this = this.borrow(&guard);
this.select()
};
let mut jf = JsonValueFilter::new_from_value(json);
jf.visit(node);
match serde_json::to_string(&jf.take_value().deref()) {
Ok(json_str) => Ok(JsString::new(&mut ctx, &json_str).upcast()),
Err(e) => panic!("{:?}", e)
}
Ok(JsString::new(&mut ctx, &result_str).upcast())
}
}
pub class JsSelector for SelectorCls {
init(mut _ctx) {
Ok(SelectorCls { selector: Selector::new() })
Ok(SelectorCls { node: None, value: None })
}
method path(mut ctx) {
@ -134,44 +221,64 @@ declare_types! {
{
let guard = ctx.lock();
let mut this = this.borrow_mut(&guard);
let _ = this.selector.path(&path);
let _ = this.path(&path);
}
Ok(JsUndefined::new().upcast())
}
method value_from_str(mut ctx) {
method value(mut ctx) {
let mut this = ctx.this();
let json_str = ctx.argument::<JsString>(0)?.value();
{
let guard = ctx.lock();
let mut this = this.borrow_mut(&guard);
let _ = this.selector.value_from_str(&json_str);
let _ = this.value(&json_str);
}
Ok(JsUndefined::new().upcast())
}
method select_to_str(mut ctx) {
let mut this = ctx.this();
method select(mut ctx) {
let this = ctx.this();
let result = {
let result_str = {
let guard = ctx.lock();
let mut this = this.borrow_mut(&guard);
this.selector.select_to_str()
let this = this.borrow(&guard);
this.select()
};
match result {
Ok(json_str) => Ok(JsString::new(&mut ctx, &json_str).upcast()),
Err(e) => panic!("{:?}", e)
}
Ok(JsString::new(&mut ctx, &result_str).upcast())
}
}
pub class JsSelectorMut for SelectorMutCls {
init(mut _ctx) {
Ok(SelectorMutCls {})
}
method emptyPathError(mut _ctx) {
panic!("{:?}", JsonPathError::EmptyPath);
}
method emptyValueError(mut _ctx) {
panic!("{:?}", JsonPathError::EmptyValue);
}
}
}
register_module!(mut m, {
m.export_class::<JsCompileFn>("CompileFn").expect("CompileFn class error");
m.export_class::<JsSelectorFn>("SelectorFn").expect("SelectorFn class error");
m.export_class::<JsSelector>("Selector").expect("Selector class error");
m.export_class::<JsCompileFn>("CompileFn")
.expect("CompileFn class error");
m.export_class::<JsSelectorFn>("SelectorFn")
.expect("SelectorFn class error");
m.export_class::<JsSelector>("Selector")
.expect("Selector class error");
m.export_class::<JsSelectorMut>("SelectorMut")
.expect("SelectorMut class error");
m.export_function("select", select)?;
m.export_function("deleteValue", delete)?;
m.export_function("replaceWith", replace_with)?;
m.export_function("selectStr", select_str)?;
Ok(())
});
});

1433
nodejs/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,20 @@
{
"name": "jsonpath-rs",
"version": "0.1.7",
"version": "0.2.2",
"description": "It is JsonPath implementation. The core implementation is written in Rust",
"author": "Changseok Han <freestrings@gmail.com>",
"license": "MIT",
"keywords": [
"jsonpath",
"rust-addon",
"native-addon",
"rust-binding",
"rust",
"rustlang",
"json"
"json",
"parsing"
],
"main": "lib/index.js",
"dependencies": {
"check-node-version": "*",
"neon-cli": "^0.2.0"
},
"scripts": {
@ -21,10 +22,14 @@
"test": "mocha"
},
"devDependencies": {
"mocha": "^6.0.2"
"mocha": "^6.1.4"
},
"repository": {
"type": "git",
"url": "git+https://github.com/freestrings/jsonpath.git"
},
"engineStrict": true,
"engines": {
"node": ">=8.0 <12.0"
}
}

View File

@ -359,7 +359,7 @@ describe('compile test', () => {
it('basic', (done) => {
let template = jsonpath.compile('$.a');
let result = template({'a': 1});
if (result === 1) {
if (result[0] === 1) {
done();
}
});
@ -369,7 +369,7 @@ describe('selector test', () => {
it('basic', (done) => {
let selector = jsonpath.selector({'a': 1});
let result = selector('$.a');
if (result === 1) {
if (result[0] === 1) {
done();
}
});
@ -378,7 +378,7 @@ describe('selector test', () => {
describe('select test', () => {
it('basic', (done) => {
let result = jsonpath.select({'a': 1}, '$.a');
if (result === 1) {
if (result[0] === 1) {
done();
}
});
@ -398,27 +398,194 @@ describe('filter test', () => {
run (done, i, list[i]);
})
}
it('object equal', (done) => {
let selector = new jsonpath.Selector();
selector.path('$..[?(@.a == 1)]');
selector.value({
'a': 1,
'b': {'a': 1},
'c': {'a': 1},
});
let result = selector.select();
if (JSON.stringify(result) === JSON.stringify([{'a': 1}, {'a': 1}])) {
done();
}
});
it('escaped single quote notation', (done) => {
let result = jsonpath.select({"single'quote":"value"}, "$['single\\'quote']");
if (JSON.stringify(result) === JSON.stringify(["value"])) {
done();
}
});
it('escaped double quote notation', (done) => {
let result = jsonpath.select({"single\"quote":"value"}, "$['single\"quote']");
if (JSON.stringify(result) === JSON.stringify(["value"])) {
done();
}
});
it('array range with step - $[::]', (done) => {
let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[::]");
if (JSON.stringify(result) === JSON.stringify(["first", "second", "third", "forth", "fifth"])) {
done();
}
});
it('array range with step - $[::2]', (done) => {
let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[::2]");
if (JSON.stringify(result) === JSON.stringify(["first", "third", "fifth"])) {
done();
}
});
it('array range with step - $[1: :]', (done) => {
let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[1: :]");
if (JSON.stringify(result) === JSON.stringify(["second", "third", "forth", "fifth"])) {
done();
}
});
it('array range with step - $[1:2:]', (done) => {
let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[1:2:]");
if (JSON.stringify(result) === JSON.stringify(["second"])) {
done();
}
});
it('array range with step - $[1::2]', (done) => {
let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[1::2]");
if (JSON.stringify(result) === JSON.stringify(["second", "forth"])) {
done();
}
});
it('array range with step - $[0:3:1]', (done) => {
let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[0:3:1]");
if (JSON.stringify(result) === JSON.stringify(["first", "second", "third"])) {
done();
}
});
it('array range with step - $[0:3:2]', (done) => {
let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[0:3:2]");
if (JSON.stringify(result) === JSON.stringify(["first", "third"])) {
done();
}
});
it('array keys', (done) => {
let result = jsonpath.select({
"key1": "value1",
"key2": 2
}, "$['key1', 'key2']");
if (JSON.stringify(result) === JSON.stringify(["value1", 2])) {
done();
}
});
});
describe('SelectorMut test', () => {
it('delete', (done) => {
let jsonObjNew = JSON.parse(JSON.stringify(jsonObj));
let result = jsonpath.deleteValue(jsonObjNew, '$.store.book');
if (JSON.stringify(result) === JSON.stringify({
'store': {
'book': null,
'bicycle': {
'color': 'red',
'price': 19.95,
},
},
'expensive': 10,
})) {
done();
}
});
it('replaceWith', (done) => {
let jsonObjNew = JSON.parse(JSON.stringify(jsonObj));
let result = jsonpath.replaceWith(jsonObjNew, '$.store.book', (v) => {
let ret = v[0];
ret.price = 9;
return ret;
});
if (JSON.stringify(result) === JSON.stringify({
'store': {
'book': {
'category': 'reference',
'author': 'Nigel Rees',
'title': 'Sayings of the Century',
'price': 9,
},
'bicycle': {
'color': 'red',
'price': 19.95,
},
},
'expensive': 10,
})) {
done();
}
});
it('SeletorMut delete', (done) => {
let jsonObjNew = JSON.parse(JSON.stringify(jsonObj));
let selector = new jsonpath.SelectorMut();
selector.path('$.store.book').value(jsonObjNew).deleteValue();
let result = selector.take();
if (JSON.stringify(result) === JSON.stringify({
'store': {
'book': null,
'bicycle': {
'color': 'red',
'price': 19.95,
},
},
'expensive': 10,
})) {
done();
}
});
it('SeletorMut replaceWith', (done) => {
let jsonObjNew = JSON.parse(JSON.stringify(jsonObj));
let selector = new jsonpath.SelectorMut();
selector.path('$.store.book').value(jsonObjNew).replaceWith((v) => {
let ret = v[0];
ret.price = 9;
return ret;
});
let result = selector.take();
if (JSON.stringify(result) === JSON.stringify({
'store': {
'book': {
'category': 'reference',
'author': 'Nigel Rees',
'title': 'Sayings of the Century',
'price': 9,
},
'bicycle': {
'color': 'red',
'price': 19.95,
},
},
'expensive': 10,
})) {
done();
}
});
});
describe('Selector test', () => {
it('basic selectTo', (done) => {
let result = new jsonpath.Selector().path('$.a').value({'a': 1}).selectTo();
if (result === 1) {
done();
}
});
it('basic selectToStr', (done) => {
let result = new jsonpath.Selector().path('$.a').value({'a': 1}).selectToStr();
if (result === '1') {
done();
}
});
it('select', (done) => {
let selector = new jsonpath.Selector().value(jsonObj);
for(var i in list) {
if(JSON.stringify(list[i]) !== selector.path(i).selectToStr()) {
if(JSON.stringify(list[i]) !== JSON.stringify(selector.path(i).select())) {
throw `fail: ${i}`;
}
}
@ -444,7 +611,7 @@ describe('README test', () => {
let selector = new jsonpath.Selector().value(jsonObj);
{
let jsonObj = selector.path('$..[?(@.age >= 30)]').selectTo();
let jsonObj = selector.path('$..[?(@.age >= 30)]').select();
let resultObj = [{"name": "친구3", "age": 30}];
if(JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) {
throw 'jsonpath.Selector: $..[?(@.age >= 30)]';
@ -452,7 +619,7 @@ describe('README test', () => {
}
{
let jsonObj = selector.path('$..[?(@.age == 20)]').selectTo();
let jsonObj = selector.path('$..[?(@.age == 20)]').select();
let resultObj = [{"name": "친구1", "age": 20}, {"name": "친구2", "age": 20}];
if(JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) {
throw 'jsonpath.Selector: $..[?(@.age >= 20)]';
@ -460,7 +627,7 @@ describe('README test', () => {
}
{
let jsonObj = selector.value({"friends": [ {"name": "친구5", "age": 20} ]}).selectTo();
let jsonObj = selector.value({"friends": [ {"name": "친구5", "age": 20} ]}).select();
let resultObj = [{"name": "친구5", "age": 20}];
if(JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) {
throw 'jsonpath.Selector: change value';
@ -470,6 +637,64 @@ describe('README test', () => {
done();
});
it('jsonpath.SelectorMut', (done) => {
let jsonObj = {
'school': {
'friends': [
{'name': '친구1', 'age': 20},
{'name': '친구2', 'age': 20},
],
},
'friends': [
{'name': '친구3', 'age': 30},
{'name': '친구4'},
],
};
let selector = new jsonpath.SelectorMut();
selector.path('$..[?(@.age == 20)]');
{
selector.value(jsonObj).deleteValue();
let resultObj = {
'school': {'friends': [null, null]},
'friends': [
{'name': '친구3', 'age': 30},
{'name': '친구4'},
],
};
if (JSON.stringify(selector.take()) !== JSON.stringify(resultObj)) {
throw 'jsonpath.SelectorMut.deleteValue';
}
}
{
selector.value(jsonObj).replaceWith((v) => {
v.age = v.age * 2;
return v;
});
let resultObj = {
'school': {
'friends': [
{'name': '친구1', 'age': 40},
{'name': '친구2', 'age': 40},
],
},
'friends': [
{'name': '친구3', 'age': 30},
{'name': '친구4'},
],
};
if (JSON.stringify(selector.take()) !== JSON.stringify(resultObj)) {
throw 'jsonpath.SelectorMut.replaceWith';
}
}
done();
});
it('jsonpath.select(json: string|object, jsonpath: string)', (done) => {
let jsonObj = {
"school": {
@ -599,4 +824,73 @@ describe('README test', () => {
done();
});
it('jsonpath.deleteValue(json: string|object, path: string)', (done) => {
let jsonObj = {
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]
};
let _1 = jsonpath.deleteValue(jsonObj, '$..friends[0]');
let result = jsonpath.deleteValue(_1, '$..friends[1]');
if(JSON.stringify(result) === JSON.stringify({
"school": { "friends": [null, null]},
"friends": [null, null]
})) {
done();
}
});
it('jsonpath.replaceWith(json: string|object, path: string, fun: function(json: object) => json: object', (done) => {
let jsonObj = {
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]
};
let result = jsonpath.replaceWith(jsonObj, '$..friends[0]', (v) => {
v.age = v.age * 2;
return v;
});
if(JSON.stringify(result) === JSON.stringify({
"school": {
"friends": [
{"name": "친구1", "age": 40},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 60},
{"name": "친구4"}
]
})) {
done();
}
});
});
describe('ISSUE test', () => {
it('Results do not match other implementations #6', (done) => {
let result = jsonpath.select(["first", "second"], "$[:]");
if (JSON.stringify(result) === JSON.stringify(["first", "second"])) {
done();
}
});
});

9
profiling.sh Executable file
View File

@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -e
valgrind \
--tool=callgrind \
--dump-instr=yes \
--collect-jumps=yes \
--simulate-cache=yes $1 -- $2

View File

@ -1,191 +0,0 @@
#[derive(Debug)]
pub enum CmpType {
Eq,
Ne,
Gt,
Ge,
Lt,
Le,
}
#[derive(Debug)]
pub enum CmpCondType {
And,
Or,
}
pub trait PrivCmp {
fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool;
fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool;
fn cmp_string(&self, v1: &String, v2: &String) -> bool;
}
pub trait IntoType {
fn into_type(&self) -> CmpType;
}
pub struct CmpEq;
impl PrivCmp for CmpEq {
fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool {
v1 == v2
}
fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool {
v1 == v2
}
fn cmp_string(&self, v1: &String, v2: &String) -> bool {
v1 == v2
}
}
impl IntoType for CmpEq {
fn into_type(&self) -> CmpType {
CmpType::Eq
}
}
pub struct CmpNe;
impl PrivCmp for CmpNe {
fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool {
v1 != v2
}
fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool {
v1 != v2
}
fn cmp_string(&self, v1: &String, v2: &String) -> bool {
v1 != v2
}
}
impl IntoType for CmpNe {
fn into_type(&self) -> CmpType {
CmpType::Ne
}
}
pub struct CmpGt;
impl PrivCmp for CmpGt {
fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool {
v1 > v2
}
fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool {
v1 > v2
}
fn cmp_string(&self, v1: &String, v2: &String) -> bool {
v1 > v2
}
}
impl IntoType for CmpGt {
fn into_type(&self) -> CmpType {
CmpType::Gt
}
}
pub struct CmpGe;
impl PrivCmp for CmpGe {
fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool {
v1 >= v2
}
fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool {
v1 >= v2
}
fn cmp_string(&self, v1: &String, v2: &String) -> bool {
v1 >= v2
}
}
impl IntoType for CmpGe {
fn into_type(&self) -> CmpType {
CmpType::Ge
}
}
pub struct CmpLt;
impl PrivCmp for CmpLt {
fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool {
v1 < v2
}
fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool {
v1 < v2
}
fn cmp_string(&self, v1: &String, v2: &String) -> bool {
v1 < v2
}
}
impl IntoType for CmpLt {
fn into_type(&self) -> CmpType {
CmpType::Lt
}
}
pub struct CmpLe;
impl PrivCmp for CmpLe {
fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool {
v1 <= v2
}
fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool {
v1 <= v2
}
fn cmp_string(&self, v1: &String, v2: &String) -> bool {
v1 <= v2
}
}
impl IntoType for CmpLe {
fn into_type(&self) -> CmpType {
CmpType::Le
}
}
pub struct CmpAnd;
impl PrivCmp for CmpAnd {
fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool {
*v1 && *v2
}
fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool {
v1 > &0_f64 && v2 > &0_f64
}
fn cmp_string(&self, v1: &String, v2: &String) -> bool {
!v1.is_empty() && !v2.is_empty()
}
}
pub struct CmpOr;
impl PrivCmp for CmpOr {
fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool {
*v1 || *v2
}
fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool {
v1 > &0_f64 || v2 > &0_f64
}
fn cmp_string(&self, v1: &String, v2: &String) -> bool {
!v1.is_empty() || !v2.is_empty()
}
}

View File

@ -1,4 +0,0 @@
mod cmp;
mod term;
pub mod value_filter;
pub mod value_wrapper;

View File

@ -1,154 +0,0 @@
use super::cmp::*;
use super::value_filter::ValueFilterKey;
use super::value_wrapper::*;
#[derive(Debug)]
pub enum TermContext {
Constants(ExprTerm),
Json(Option<ValueFilterKey>, ValueWrapper),
}
impl TermContext {
fn cmp<F: PrivCmp + IntoType>(&self, other: &TermContext, cmp_fn: F, default: bool) -> TermContext {
match self {
TermContext::Constants(et) => {
match other {
TermContext::Constants(oet) => {
trace!("const-const");
TermContext::Constants(ExprTerm::Bool(et.cmp(oet, cmp_fn, default)))
}
TermContext::Json(key, v) => {
trace!("const-json");
TermContext::Json(None, v.take_with(key, et, cmp_fn, true))
}
}
}
TermContext::Json(key, v) => {
match other {
TermContext::Json(key_other, ov) => {
trace!("json-json");
fn is_json(t: &TermContext) -> bool {
match t {
TermContext::Json(_, _) => true,
_ => false
}
}
let mut c = v.into_term(key);
let mut oc = ov.into_term(key_other);
if is_json(&c) && is_json(&oc) {
v.cmp(&ov, cmp_fn.into_type())
} else {
c.cmp(&mut oc, cmp_fn, default)
}
}
TermContext::Constants(et) => {
trace!("json-const");
TermContext::Json(None, v.take_with(key, et, cmp_fn, false))
}
}
}
}
}
fn cmp_cond(&self, other: &TermContext, cmp_cond_type: CmpCondType) -> TermContext {
match self {
TermContext::Constants(et) => {
match other {
TermContext::Constants(oet) => {
match cmp_cond_type {
CmpCondType::Or => {
TermContext::Constants(ExprTerm::Bool(et.cmp(oet, CmpOr, false)))
}
CmpCondType::And => {
TermContext::Constants(ExprTerm::Bool(et.cmp(oet, CmpAnd, false)))
}
}
}
TermContext::Json(_, v) => {
TermContext::Json(None, ValueWrapper::new(v.get_val().clone(), false))
}
}
}
TermContext::Json(_, v) => {
match other {
TermContext::Json(_, ov) => {
match cmp_cond_type {
CmpCondType::Or => TermContext::Json(None, v.union(ov)),
CmpCondType::And => TermContext::Json(None, v.intersect(ov)),
}
}
_ => {
TermContext::Json(None, ValueWrapper::new(v.get_val().clone(), false))
}
}
}
}
}
pub fn eq(&self, other: &TermContext) -> TermContext {
trace!("eq");
self.cmp(other, CmpEq, false)
}
pub fn ne(&self, other: &TermContext) -> TermContext {
trace!("ne");
self.cmp(other, CmpNe, true)
}
pub fn gt(&self, other: &TermContext) -> TermContext {
trace!("gt");
self.cmp(other, CmpGt, false)
}
pub fn ge(&self, other: &TermContext) -> TermContext {
trace!("ge");
self.cmp(other, CmpGe, false)
}
pub fn lt(&self, other: &TermContext) -> TermContext {
trace!("lt");
self.cmp(other, CmpLt, false)
}
pub fn le(&self, other: &TermContext) -> TermContext {
trace!("le");
self.cmp(other, CmpLe, false)
}
pub fn and(&self, other: &TermContext) -> TermContext {
self.cmp_cond(other, CmpCondType::And)
}
pub fn or(&self, other: &TermContext) -> TermContext {
self.cmp_cond(other, CmpCondType::Or)
}
}
#[derive(Debug)]
pub enum ExprTerm {
String(String),
Number(f64),
Bool(bool),
}
impl ExprTerm {
fn cmp<F: PrivCmp>(&self, other: &ExprTerm, cmp_fn: F, default: bool) -> bool {
match self {
ExprTerm::Bool(v1) => match other {
ExprTerm::Bool(v2) => cmp_fn.cmp_bool(v1, v2),
_ => default
}
ExprTerm::Number(v1) => match other {
ExprTerm::Number(v2) => cmp_fn.cmp_f64(v1, v2),
_ => default
}
ExprTerm::String(v1) => match other {
ExprTerm::String(v2) => cmp_fn.cmp_string(v1, v2),
_ => default
}
}
}
}

View File

@ -1,634 +0,0 @@
use std::error::Error;
use std::ops::Deref;
use std::result::Result;
use serde_json::Value;
use filter::term::*;
use filter::value_wrapper::*;
use parser::parser::{FilterToken, NodeVisitor, ParseToken};
use ref_value::model::*;
trait ArrayIndex {
fn index(&self, v: &RefValueWrapper) -> usize;
fn take_value(&self, v: &RefValueWrapper) -> RefValueWrapper {
let idx = self.index(v);
match v.get(idx) {
Some(v) => v.clone(),
_ => RefValue::Null.into()
}
}
}
impl ArrayIndex for f64 {
fn index(&self, v: &RefValueWrapper) -> usize {
if v.is_array() && self < &0_f64 {
(v.as_array().unwrap().len() as f64 + self) as usize
} else {
*self as usize
}
}
}
impl ArrayIndex for isize {
fn index(&self, v: &RefValueWrapper) -> usize {
if v.is_array() && self < &0_isize {
(v.as_array().unwrap().len() as isize + self) as usize
} else {
*self as usize
}
}
}
impl ArrayIndex for usize {
fn index(&self, _: &RefValueWrapper) -> usize {
*self as usize
}
}
#[derive(Debug, Clone)]
pub enum ValueFilterKey {
Num(usize),
String(String),
All,
}
#[derive(Debug)]
pub struct ValueFilter {
val_wrapper: ValueWrapper,
last_key: Option<ValueFilterKey>,
filter_mode: bool,
}
impl ValueFilter {
pub fn new(v: RefValueWrapper, is_leaves: bool, filter_mode: bool) -> Self {
ValueFilter { val_wrapper: ValueWrapper::new(v, is_leaves), last_key: None, filter_mode }
}
fn iter_to_value_vec<'a, I: Iterator<Item=&'a RefValueWrapper>>(iter: I) -> Vec<RefValueWrapper> {
iter
.map(|v| v.clone())
.filter(|v| !v.is_null())
.collect()
}
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.clone()
} else {
let idx = key.index(v);
v.get(idx).unwrap().clone()
}
} else {
key.take_value(v)
}
}
fn get_nested_object(v: &RefValueWrapper, key: &String, filter_mode: bool) -> RefValueWrapper {
if v.is_object() && v.as_object().unwrap().contains_key(key) {
if filter_mode {
v.clone()
} else {
v.get(key.clone()).unwrap().clone()
}
} else {
RefValue::Null.into()
}
}
fn collect_all(key: Option<&String>, v: &RefValueWrapper, buf: &mut Vec<RefValueWrapper>) {
match v.deref() {
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);
}
}
RefValue::Object(v) => {
for (k, v) in v.into_iter() {
if match key {
Some(map_key) => map_key == k,
_ => true
} {
buf.push(v.clone());
}
}
for (_, v) in v.into_iter() {
Self::collect_all(key, v, buf);
}
}
_ => {}
}
}
pub fn step_leaves_all(&mut self) -> &ValueWrapper {
debug!("step_leaves_all");
let mut buf = Vec::new();
Self::collect_all(None, &self.val_wrapper.get_val(), &mut buf);
trace!("step_leaves_all - {:?}", buf);
self.last_key = Some(ValueFilterKey::All);
self.val_wrapper = ValueWrapper::new(RefValue::Array(buf).into(), true);
&self.val_wrapper
}
pub fn step_leaves_str(&mut self, key: &str) -> &ValueWrapper {
self.step_leaves_string(&key.to_string())
}
pub fn step_leaves_string(&mut self, key: &String) -> &ValueWrapper {
debug!("step_leaves_string");
let mut buf = Vec::new();
Self::collect_all(Some(key), &self.val_wrapper.get_val(), &mut buf);
trace!("step_leaves_string - {:?}", buf);
self.last_key = Some(ValueFilterKey::String(key.clone()));
self.val_wrapper = ValueWrapper::new(RefValue::Array(buf).into(), true);
&self.val_wrapper
}
pub fn step_in_all(&mut self) -> &ValueWrapper {
debug!("step_in_all");
let vec = match self.val_wrapper.get_val().deref() {
RefValue::Object(ref map) => {
Self::iter_to_value_vec(map.values())
}
RefValue::Array(ref list) => {
Self::iter_to_value_vec(list.iter())
}
RefValue::Null => Vec::new(),
_ => vec![self.val_wrapper.get_val().clone()]
};
self.last_key = Some(ValueFilterKey::All);
self.val_wrapper.replace(RefValue::Array(vec).into());
trace!("step_in_all - {:?}", self.val_wrapper.get_val());
&self.val_wrapper
}
pub fn step_in_num(&mut self, key: &f64) -> &ValueWrapper {
debug!("step_in_num");
trace!("step_in_num - before: leaves {}, filterMode {} - {:?}"
, self.val_wrapper.is_leaves()
, self.filter_mode
, self.val_wrapper.get_val());
let v = if self.val_wrapper.is_leaves() {
let filter_mode = self.filter_mode;
match self.val_wrapper.get_val().deref() {
RefValue::Array(ref vec) => {
let mut ret = Vec::new();
for v in vec {
let wrapper = Self::get_nested_array(v, *key, filter_mode);
if !wrapper.is_null() {
ret.push(wrapper.clone());
}
}
RefValue::Array(ret).into()
}
_ => key.take_value(&self.val_wrapper.get_val())
}
} else {
key.take_value(&self.val_wrapper.get_val())
};
self.last_key = Some(ValueFilterKey::Num(key.index(&v)));
self.val_wrapper.replace(v);
trace!("step_in_num - after: {:?}", self.val_wrapper.get_val());
&self.val_wrapper
}
pub fn step_in_str(&mut self, key: &str) -> &ValueWrapper {
self.step_in_string(&key.to_string())
}
pub fn step_in_string(&mut self, key: &String) -> &ValueWrapper {
debug!("step_in_string");
trace!("step_in_string - before: {},{},{:?}"
, self.val_wrapper.is_leaves()
, self.filter_mode
, self.val_wrapper.get_val());
let filter_mode = self.filter_mode;
let is_leaves = self.val_wrapper.is_leaves();
let val = match self.val_wrapper.get_val().deref() {
RefValue::Array(ref vec) if is_leaves => {
let mut buf = Vec::new();
for mut v in vec {
if v.is_array() {
let vec = v.as_array().unwrap();
let mut ret = Vec::new();
for v in vec {
let nested_wrapper = Self::get_nested_object(v, key, filter_mode);
if !nested_wrapper.is_null() {
ret.push(nested_wrapper.clone());
}
}
buf.append(&mut ret);
} else if v.is_object() {
let nested_wrapper = Self::get_nested_object(v, key, filter_mode);
if !nested_wrapper.is_null() {
buf.push(nested_wrapper.clone());
}
} else {
match v.get(key.clone()) {
Some(v) => buf.push(v.clone()),
_ => {}
}
}
}
RefValue::Array(buf).into()
}
RefValue::Array(ref vec) if !is_leaves => {
let mut ret = Vec::new();
for v in vec {
let wrapper = Self::get_nested_object(v, key, filter_mode);
if !wrapper.is_null() {
ret.push(wrapper.clone());
}
}
RefValue::Array(ret).into()
}
_ => {
match self.val_wrapper.get_val().get(key.clone()) {
Some(v) => v.clone(),
_ => RefValue::Null.into()
}
}
};
self.last_key = Some(ValueFilterKey::String(key.clone()));
self.val_wrapper.replace(val);
trace!("step_in_string - after: {},{},{:?}"
, self.val_wrapper.is_leaves()
, self.filter_mode
, self.val_wrapper.get_val());
&self.val_wrapper
}
}
pub struct JsonValueFilter {
json: RefValueWrapper,
filter_stack: Vec<ValueFilter>,
token_stack: Vec<ParseToken>,
term_stack: Vec<TermContext>,
}
impl JsonValueFilter {
pub fn new(json: &str) -> Result<Self, String> {
let json: RefValue = serde_json::from_str(json)
.map_err(|e| e.description().to_string())?;
Ok(JsonValueFilter::new_from_value(json.into()))
}
pub fn new_from_value(json: RefValueWrapper) -> Self {
JsonValueFilter {
json,
filter_stack: Vec::new(),
token_stack: Vec::new(),
term_stack: Vec::new(),
}
}
fn is_peek_token_array(&self) -> bool {
if let Some(ParseToken::Array) = self.token_stack.last() {
true
} else {
false
}
}
fn push_value_filter(&mut self, from_current: bool) {
if from_current {
self.filter_stack.last()
.map(|vf| {
ValueFilter::new(vf.val_wrapper.get_val().clone(), vf.val_wrapper.is_leaves(), from_current)
})
.and_then(|vf| {
Some(self.filter_stack.push(vf))
});
} else {
self.filter_stack.push({
ValueFilter::new(self.json.clone(), false, from_current)
});
}
}
fn replace_filter_stack(&mut self, v: RefValueWrapper, is_leaves: bool) {
if self.filter_stack.is_empty() {
self.filter_stack.push(ValueFilter::new(v, is_leaves, false));
} else {
match self.filter_stack.last_mut() {
Some(vf) => {
vf.val_wrapper.set_leaves(is_leaves);
if v.is_null() {
vf.val_wrapper.replace(v);
} else if v.is_array() && v.as_array().unwrap().is_empty() {
vf.val_wrapper.replace(RefValue::Null.into());
} else if vf.val_wrapper.is_array() {
vf.val_wrapper.replace(v);
}
}
_ => {}
}
}
}
pub fn into_value(&self) -> Value {
match self.filter_stack.last() {
Some(v) => v.val_wrapper.into_value(),
_ => Value::Null
}
}
pub fn take_value(&mut self) -> RefValueWrapper {
match self.filter_stack.last_mut() {
Some(v) => v.val_wrapper.get_val().clone(),
_ => RefValue::Null.into()
}
}
fn token_union<F: ArrayIndex>(&mut self, indices: Vec<F>) {
self.token_stack.pop();
match self.filter_stack.last_mut() {
Some(ref mut vf) if vf.val_wrapper.is_array() && vf.val_wrapper.is_leaves() => {
let mut ret = Vec::new();
if let RefValue::Array(val) = vf.val_wrapper.get_val().deref() {
for mut v in val {
for i in &indices {
let v = i.take_value(v);
if !v.is_null() {
ret.push(v.clone());
}
}
}
}
vf.val_wrapper.replace(RefValue::Array(ret).into());
}
Some(ref mut vf) if vf.val_wrapper.is_array() && !vf.val_wrapper.is_leaves() => {
let mut ret = Vec::new();
for i in indices {
let wrapper = i.take_value(&vf.val_wrapper.get_val());
if !wrapper.is_null() {
ret.push(wrapper.clone());
}
}
vf.val_wrapper.replace(RefValue::Array(ret).into());
}
_ => {}
}
}
fn token_range(&mut self, from: Option<isize>, to: Option<isize>) {
self.token_stack.pop();
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 RefValue::Array(v) = val.deref() {
v.len()
} else {
0
}
}
};
(from, to)
}
fn _range(from: usize, to: usize, v: &RefValueWrapper) -> Vec<RefValueWrapper> {
trace!("range - {}:{}", from, to);
(from..to).into_iter()
.map(|i| i.take_value(v))
.filter(|v| !v.is_null())
.map(|v| v.clone())
.collect()
}
match self.filter_stack.last_mut() {
Some(ref mut vf) if vf.val_wrapper.is_array() && vf.val_wrapper.is_leaves() => {
let mut buf = Vec::new();
if let RefValue::Array(vec) = vf.val_wrapper.get_val().deref() {
for mut v in vec {
let (from, to) = _from_to(from, to, v);
let mut v: Vec<RefValueWrapper> = _range(from, to, v);
buf.append(&mut v);
}
}
vf.val_wrapper.replace(RefValue::Array(buf).into());
}
Some(ref mut vf) if vf.val_wrapper.is_array() && !vf.val_wrapper.is_leaves() => {
let (from, to) = _from_to(from, to, &vf.val_wrapper.get_val());
let vec: Vec<RefValueWrapper> = _range(from, to, vf.val_wrapper.get_val());
vf.val_wrapper.replace(RefValue::Array(vec).into());
}
_ => {}
}
}
fn token_key(&mut self, key: String) {
match self.filter_stack.last_mut() {
Some(vf) => {
match self.token_stack.pop() {
Some(ParseToken::In) | Some(ParseToken::Array) => {
vf.step_in_string(&key);
}
Some(ParseToken::Leaves) => {
vf.step_leaves_string(&key);
}
_ => {
self.term_stack.push(TermContext::Constants(ExprTerm::String(key)));
}
}
}
_ => {}
}
}
fn token_all(&mut self) {
match self.filter_stack.last_mut() {
Some(vf) => {
match self.token_stack.pop() {
Some(ParseToken::In) => {
vf.step_in_all();
}
Some(ParseToken::Leaves) => {
vf.step_leaves_all();
}
_ => {}
}
}
_ => {}
}
}
fn token_end_array(&mut self) {
trace!("array_eof - term_stack: {:?}", self.term_stack);
trace!("array_eof - filter_stack: {:?}", self.filter_stack);
match self.term_stack.pop() {
Some(TermContext::Constants(ExprTerm::Number(v))) => {
match self.filter_stack.last_mut() {
Some(vf) => {
vf.step_in_num(&v);
}
_ => {}
}
}
Some(TermContext::Constants(ExprTerm::Bool(false))) => {
self.replace_filter_stack(RefValue::Null.into(), false);
}
Some(TermContext::Json(_, vw)) => {
self.replace_filter_stack(vw.get_val().clone(), vw.is_leaves());
}
_ => {
match self.filter_stack.pop() {
Some(mut vf) => {
let is_leaves = vf.val_wrapper.is_leaves();
match vf.val_wrapper.get_val().deref() {
RefValue::Null | RefValue::Bool(false) => {
self.replace_filter_stack(RefValue::Null.into(), is_leaves);
}
_ => {
self.replace_filter_stack(vf.val_wrapper.get_val().clone(), is_leaves);
}
}
}
_ => {}
}
}
}
}
fn token_op(&mut self, ft: &FilterToken) {
let right = self.term_stack.pop();
let left = self.term_stack.pop();
trace!("left {:?}", left);
trace!("right {:?}", right);
if left.is_some() && right.is_some() {
let left = left.unwrap();
let right = right.unwrap();
let tc = match ft {
FilterToken::Equal => left.eq(&right),
FilterToken::NotEqual => left.ne(&right),
FilterToken::Greater => left.gt(&right),
FilterToken::GreaterOrEqual => left.ge(&right),
FilterToken::Little => left.lt(&right),
FilterToken::LittleOrEqual => left.le(&right),
FilterToken::And => left.and(&right),
FilterToken::Or => left.or(&right),
};
self.term_stack.push(tc);
}
trace!("filter - {:?}", self.term_stack)
}
}
impl NodeVisitor for JsonValueFilter {
fn visit_token(&mut self, token: ParseToken) {
debug!("visit_token: {:?}", token);
match token {
ParseToken::Absolute
| ParseToken::Relative => {
if self.is_peek_token_array() {
self.token_stack.pop();
}
self.push_value_filter(ParseToken::Relative == token);
}
ParseToken::In
| ParseToken::Leaves => {
self.token_stack.push(token);
}
ParseToken::Array => {
if let Some(ParseToken::Leaves) = self.token_stack.last() {
self.token_all();
}
self.token_stack.push(token);
}
ParseToken::ArrayEof => {
self.token_end_array();
}
ParseToken::All => {
self.token_all();
}
ParseToken::Key(key) => {
self.token_key(key);
}
ParseToken::Filter(ref ft) => {
self.token_op(ft);
}
ParseToken::Number(v) => {
self.term_stack.push(TermContext::Constants(ExprTerm::Number(v)))
}
ParseToken::Range(from, to) => {
self.token_range(from, to);
}
ParseToken::Union(v) => {
self.token_union(v);
}
ParseToken::Eof => {
debug!("visit_token eof");
}
}
}
fn end_term(&mut self) {
debug!("end_term");
if let Some(ParseToken::Array) = self.token_stack.last() {
self.token_stack.pop();
}
trace!("end_term - term_stack {:?}", self.term_stack);
trace!("end_term - token_stack {:?}", self.token_stack);
trace!("end_term - filter_stack {:?}", self.filter_stack);
if self.token_stack.is_empty() && self.filter_stack.len() > 1 {
match self.filter_stack.pop() {
Some(vf) => {
self.term_stack.push(TermContext::Json(vf.last_key, vf.val_wrapper));
}
_ => {}
}
}
if match self.token_stack.last() {
Some(ParseToken::Key(_))
| Some(ParseToken::Number(_)) => true,
_ => false
} {
match self.token_stack.pop() {
Some(ParseToken::Key(ref v)) if v.eq_ignore_ascii_case("true") => {
self.term_stack.push(TermContext::Constants(ExprTerm::Bool(true)))
}
Some(ParseToken::Key(ref v)) if v.eq_ignore_ascii_case("false") => {
self.term_stack.push(TermContext::Constants(ExprTerm::Bool(false)))
}
Some(ParseToken::Key(v)) => {
self.term_stack.push(TermContext::Constants(ExprTerm::String(v)))
}
Some(ParseToken::Number(v)) => {
self.term_stack.push(TermContext::Constants(ExprTerm::Number(v)))
}
_ => {}
}
}
}
}

View File

@ -1,284 +0,0 @@
use std::ops::Deref;
use indexmap::IndexSet;
use serde_json::Value;
use ref_value::model::*;
use super::cmp::*;
use super::term::*;
use super::value_filter::*;
#[derive(Debug)]
pub struct ValueWrapper {
val: RefValueWrapper,
is_leaves: bool,
}
impl ValueWrapper {
pub fn new(val: RefValueWrapper, leaves: bool) -> Self {
ValueWrapper { val, is_leaves: leaves }
}
pub fn is_leaves(&self) -> bool {
self.is_leaves
}
pub fn set_leaves(&mut self, is_leaves: bool) {
self.is_leaves = is_leaves;
}
pub fn cmp(&self, other: &ValueWrapper, cmp_type: CmpType) -> TermContext {
match cmp_type {
CmpType::Eq => {
TermContext::Json(None, self.intersect(other))
}
CmpType::Ne => {
TermContext::Json(None, self.except(other))
}
CmpType::Gt | CmpType::Ge | CmpType::Lt | CmpType::Le => {
TermContext::Constants(ExprTerm::Bool(false))
}
}
}
fn cmp_with_term<F: PrivCmp>(val: &RefValueWrapper, et: &ExprTerm, cmp_fn: &F, default: bool, reverse: bool) -> bool {
match val.deref() {
RefValue::Bool(ref v1) => {
match et {
ExprTerm::Bool(v2) => if reverse { cmp_fn.cmp_bool(v2, v1) } else { cmp_fn.cmp_bool(v1, v2) },
_ => default
}
}
RefValue::Number(ref v1) => match et {
ExprTerm::Number(v2) => if reverse { cmp_fn.cmp_f64(v2, &v1.as_f64().unwrap()) } else { cmp_fn.cmp_f64(&v1.as_f64().unwrap(), v2) },
_ => default
},
RefValue::String(ref v1) => {
match et {
ExprTerm::String(v2) => if reverse { cmp_fn.cmp_string(v2, v1) } else { cmp_fn.cmp_string(v1, v2) },
_ => default
}
}
_ => default
}
}
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.deref() {
RefValue::Object(map) => {
match map.get(key) {
Some(val) => fun(val),
_ => false
}
}
_ => false
}
}
match self.val.deref() {
RefValue::Array(vec) => {
let mut set = IndexSet::new();
for v in vec {
if _filter_with_object(v, key, |vv| {
Self::cmp_with_term(vv, et, cmp, false, reverse)
}) {
set.insert(v.clone());
}
}
let ret = set.into_iter().collect();
Some(ValueWrapper::new(RefValue::Array(ret).into(), false))
}
_ => None
}
}
fn take_with_key_type<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)
}
_ => None
}
}
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) {
RefValue::Array(vec) => {
let mut set = IndexSet::new();
for v in vec {
if Self::cmp_with_term(v, et, &cmp, false, reverse) {
set.insert(v.clone());
}
}
let ret = set.into_iter().collect();
ValueWrapper::new(RefValue::Array(ret).into(), false)
}
_ => {
if Self::cmp_with_term(&self.val, et, &cmp, false, reverse) {
ValueWrapper::new(self.val.clone(), false)
} else {
ValueWrapper::new(RefValue::Null.into(), false)
}
}
}
}
}
}
pub fn replace(&mut self, val: RefValueWrapper) {
let is_null = match val.deref() {
RefValue::Array(v) => 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 {
RefValue::Null.into()
} else {
val
};
}
pub fn get_val(&self) -> &RefValueWrapper {
&self.val
}
pub fn into_value(&self) -> Value {
self.get_val().into()
}
pub fn is_array(&self) -> bool {
self.val.is_array()
}
fn into_hashset(&self) -> IndexSet<RefValueWrapper> {
trace!("into_hashset");
let mut hashset = IndexSet::new();
match self.val.deref() {
RefValue::Array(ref v1) => {
for v in v1 {
hashset.insert(v.clone());
}
}
_ => {
hashset.insert(self.val.clone());
}
}
hashset
}
pub fn except(&self, other: &Self) -> Self {
trace!("except");
let hashset = self.into_hashset();
let mut ret: IndexSet<RefValueWrapper> = IndexSet::new();
match other.val.deref() {
RefValue::Array(ref v1) => {
for v in v1 {
if !hashset.contains(v) {
ret.insert(v.clone());
}
}
}
_ => {
if !hashset.contains(&other.val) {
ret.insert(other.val.clone());
}
}
}
let vec = ret.into_iter().map(|v| v.clone()).collect();
ValueWrapper::new(RefValue::Array(vec).into(), false)
}
pub fn intersect(&self, other: &Self) -> Self {
trace!("intersect");
let hashset = self.into_hashset();
let mut ret: IndexSet<RefValueWrapper> = IndexSet::new();
match other.val.deref() {
RefValue::Array(ref v1) => {
for v in v1 {
if hashset.contains(v) {
ret.insert(v.clone());
}
}
}
_ => {
if hashset.contains(&other.val) {
ret.insert(other.val.clone());
}
}
}
let vec = ret.into_iter().map(|v| v.clone()).collect();
ValueWrapper::new(RefValue::Array(vec).into(), false)
}
pub fn union(&self, other: &Self) -> Self {
trace!("union");
let mut hashset = self.into_hashset();
match other.val.deref() {
RefValue::Array(ref v1) => {
for v in v1 {
if !hashset.contains(v) {
hashset.insert(v.clone());
}
}
}
_ => {
if !hashset.contains(&other.val) {
hashset.insert(other.val.clone());
}
}
}
let mut vw = ValueWrapper::new(RefValue::Null.into(), false);
let list = hashset.into_iter().map(|val| val.clone()).collect();
vw.replace(RefValue::Array(list).into());
vw
}
pub fn into_term(&self, key: &Option<ValueFilterKey>) -> TermContext {
match self.val.deref() {
RefValue::String(ref s) => TermContext::Constants(ExprTerm::String(s.clone())),
RefValue::Number(ref n) => TermContext::Constants(ExprTerm::Number(n.as_f64().unwrap())),
RefValue::Bool(b) => TermContext::Constants(ExprTerm::Bool(*b)),
_ => TermContext::Json(match key {
Some(vk) => Some(vk.clone()),
_ => None
}, ValueWrapper::new(self.val.clone(), false))
}
}
pub fn filter(&self, key: &Option<ValueFilterKey>) -> Self {
trace!("filter");
let v = match self.val.deref() {
RefValue::Array(ref vec) => {
let mut ret = Vec::new();
for v in vec {
if let Some(ValueFilterKey::String(k)) = key {
if v.get(k.clone()).is_some() {
ret.push(v.clone());
}
}
}
RefValue::Array(ret).into()
}
RefValue::Object(ref map) => {
match key {
Some(ValueFilterKey::String(k)) => match map.get(k) {
Some(v) => v.clone(),
_ => RefValue::Null.into()
},
_ => RefValue::Null.into()
}
}
_ => self.val.clone()
};
ValueWrapper::new(v, false)
}
}

View File

@ -1,271 +1,226 @@
//! JsonPath implementation for Rust
//! JsonPath implementation written in Rust.
//!
//! # Example
//! ```
//! extern crate jsonpath_lib as jsonpath;
//! #[macro_use] extern crate serde_json;
//! extern crate jsonpath_lib as jsonpath;
//! #[macro_use] extern crate serde_json;
//! let json_obj = json!({
//! "store": {
//! "book": [
//! {
//! "category": "reference",
//! "author": "Nigel Rees",
//! "title": "Sayings of the Century",
//! "price": 8.95
//! },
//! {
//! "category": "fiction",
//! "author": "Evelyn Waugh",
//! "title": "Sword of Honour",
//! "price": 12.99
//! },
//! {
//! "category": "fiction",
//! "author": "Herman Melville",
//! "title": "Moby Dick",
//! "isbn": "0-553-21311-3",
//! "price": 8.99
//! },
//! {
//! "category": "fiction",
//! "author": "J. R. R. Tolkien",
//! "title": "The Lord of the Rings",
//! "isbn": "0-395-19395-8",
//! "price": 22.99
//! }
//! ],
//! "bicycle": {
//! "color": "red",
//! "price": 19.95
//! }
//! },
//! "expensive": 10
//! });
//!
//! let json_obj = json!({
//! "store": {
//! "book": [
//! {
//! "category": "reference",
//! "author": "Nigel Rees",
//! "title": "Sayings of the Century",
//! "price": 8.95
//! },
//! {
//! "category": "fiction",
//! "author": "Evelyn Waugh",
//! "title": "Sword of Honour",
//! "price": 12.99
//! },
//! {
//! "category": "fiction",
//! "author": "Herman Melville",
//! "title": "Moby Dick",
//! "isbn": "0-553-21311-3",
//! "price": 8.99
//! },
//! {
//! "category": "fiction",
//! "author": "J. R. R. Tolkien",
//! "title": "The Lord of the Rings",
//! "isbn": "0-395-19395-8",
//! "price": 22.99
//! }
//! ],
//! "bicycle": {
//! "color": "red",
//! "price": 19.95
//! }
//! },
//! "expensive": 10
//! });
//! let mut selector = jsonpath::selector(&json_obj);
//!
//! let mut selector = jsonpath::selector(&json_obj);
//! assert_eq!(selector("$.store.book[*].author").unwrap(),
//! vec![
//! "Nigel Rees", "Evelyn Waugh", "Herman Melville", "J. R. R. Tolkien"
//! ]);
//!
//! //
//! // $.store.book[*].author
//! //
//! let json = selector("$.store.book[*].author").unwrap();
//! let ret = json!(["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]);
//! assert_eq!(json, ret);
//! assert_eq!(selector("$..author").unwrap(),
//! vec![
//! "Nigel Rees", "Evelyn Waugh", "Herman Melville", "J. R. R. Tolkien"
//! ]);
//!
//! //
//! // $..author
//! //
//! let json = selector("$..author").unwrap();
//! let ret = json!(["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]);
//! assert_eq!(json, ret);
//! assert_eq!(selector("$.store.*").unwrap(),
//! vec![
//! &json!([
//! { "category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95 },
//! { "category": "fiction", "author": "Evelyn Waugh", "title": "Sword of Honour", "price": 12.99 },
//! { "category": "fiction", "author": "Herman Melville", "title": "Moby Dick", "isbn": "0-553-21311-3", "price": 8.99 },
//! { "category": "fiction", "author": "J. R. R. Tolkien", "title": "The Lord of the Rings", "isbn": "0-395-19395-8", "price": 22.99 }
//! ]),
//! &json!({ "color": "red", "price": 19.95 })
//! ]);
//!
//! //
//! // $.store.*
//! //
//! let json = selector("$.store.*").unwrap();
//! let ret = json!([
//! [
//! {"category" : "reference", "author" : "Nigel Rees","title" : "Sayings of the Century", "price" : 8.95},
//! {"category" : "fiction", "author" : "Evelyn Waugh","title" : "Sword of Honour","price" : 12.99},
//! {"category" : "fiction", "author" : "Herman Melville","title" : "Moby Dick","isbn" : "0-553-21311-3","price" : 8.99},
//! {"category" : "fiction", "author" : "J. R. R. Tolkien","title" : "The Lord of the Rings","isbn" : "0-395-19395-8","price" : 22.99}
//! ],
//! {"color" : "red","price" : 19.95},
//! ]);
//! assert_eq!(ret, json);
//! assert_eq!(selector("$.store..price").unwrap(),
//! vec![
//! 8.95, 12.99, 8.99, 22.99, 19.95
//! ]);
//!
//! //
//! // $.store..price
//! //
//! let json = selector("$.store..price").unwrap();
//! let ret = json!([8.95, 12.99, 8.99, 22.99, 19.95]);
//! assert_eq!(ret, json);
//! assert_eq!(selector("$..book[2]").unwrap(),
//! vec![
//! &json!({
//! "category" : "fiction",
//! "author" : "Herman Melville",
//! "title" : "Moby Dick",
//! "isbn" : "0-553-21311-3",
//! "price" : 8.99
//! })
//! ]);
//!
//! //
//! // $..book[2]
//! //
//! let json = selector("$..book[2]").unwrap();
//! let ret = json!([{
//! "category" : "fiction",
//! "author" : "Herman Melville",
//! "title" : "Moby Dick",
//! "isbn" : "0-553-21311-3",
//! "price" : 8.99
//! }]);
//! assert_eq!(ret, json);
//! assert_eq!(selector("$..book[-2]").unwrap(),
//! vec![
//! &json!({
//! "category" : "fiction",
//! "author" : "Herman Melville",
//! "title" : "Moby Dick",
//! "isbn" : "0-553-21311-3",
//! "price" : 8.99
//! })
//! ]);
//!
//! //
//! // $..book[-2]
//! //
//! let json = selector("$..book[-2]").unwrap();
//! let ret = json!([{
//! "category" : "fiction",
//! "author" : "Herman Melville",
//! "title" : "Moby Dick",
//! "isbn" : "0-553-21311-3",
//! "price" : 8.99
//! }]);
//! assert_eq!(ret, json);
//! assert_eq!(selector("$..book[0,1]").unwrap(),
//! vec![
//! &json!({"category" : "reference","author" : "Nigel Rees","title" : "Sayings of the Century","price" : 8.95}),
//! &json!({"category" : "fiction","author" : "Evelyn Waugh","title" : "Sword of Honour","price" : 12.99})
//! ]);
//!
//! //
//! // $..book[0,1]
//! //
//! let json = selector("$..book[0,1]").unwrap();
//! let ret = json!([
//! {"category" : "reference","author" : "Nigel Rees","title" : "Sayings of the Century","price" : 8.95},
//! {"category" : "fiction","author" : "Evelyn Waugh","title" : "Sword of Honour","price" : 12.99}
//! ]);
//! assert_eq!(ret, json);
//! assert_eq!(selector("$..book[:2]").unwrap(),
//! vec![
//! &json!({"category" : "reference","author" : "Nigel Rees","title" : "Sayings of the Century","price" : 8.95}),
//! &json!({"category" : "fiction","author" : "Evelyn Waugh","title" : "Sword of Honour","price" : 12.99})
//! ]);
//!
//! //
//! // $..book[:2]
//! //
//! let json = selector("$..book[:2]").unwrap();
//! let ret = json!([
//! {"category" : "reference","author" : "Nigel Rees","title" : "Sayings of the Century","price" : 8.95},
//! {"category" : "fiction","author" : "Evelyn Waugh","title" : "Sword of Honour","price" : 12.99}
//! ]);
//! assert_eq!(ret, json);
//! assert_eq!(selector("$..book[:2]").unwrap(),
//! vec![
//! &json!({"category" : "reference","author" : "Nigel Rees","title" : "Sayings of the Century","price" : 8.95}),
//! &json!({"category" : "fiction","author" : "Evelyn Waugh","title" : "Sword of Honour","price" : 12.99})
//! ]);
//!
//! //
//! // $..book[2:]
//! //
//! let json = selector("$..book[2:]").unwrap();
//! let ret = json!([
//! {"category" : "fiction","author" : "Herman Melville","title" : "Moby Dick","isbn" : "0-553-21311-3","price" : 8.99},
//! {"category" : "fiction","author" : "J. R. R. Tolkien","title" : "The Lord of the Rings","isbn" : "0-395-19395-8","price" : 22.99}
//! ]);
//! assert_eq!(ret, json);
//! assert_eq!(selector("$..book[?(@.isbn)]").unwrap(),
//! vec![
//! &json!({"category" : "fiction","author" : "Herman Melville","title" : "Moby Dick","isbn" : "0-553-21311-3","price" : 8.99}),
//! &json!({"category" : "fiction","author" : "J. R. R. Tolkien","title" : "The Lord of the Rings","isbn" : "0-395-19395-8","price" : 22.99})
//! ]);
//!
//! //
//! // $..book[?(@.isbn)]
//! //
//! let json = selector("$..book[?(@.isbn)]").unwrap();
//! let ret = json!([
//! {"category" : "fiction","author" : "Herman Melville","title" : "Moby Dick","isbn" : "0-553-21311-3","price" : 8.99},
//! {"category" : "fiction","author" : "J. R. R. Tolkien","title" : "The Lord of the Rings","isbn" : "0-395-19395-8","price" : 22.99}
//! ]);
//! assert_eq!(ret, json);
//!
//! //
//! // $.store.book[?(@.price < 10)]
//! //
//! let json = selector("$.store.book[?(@.price < 10)]").unwrap();
//! let ret = json!([
//! {"category" : "reference","author" : "Nigel Rees","title" : "Sayings of the Century","price" : 8.95},
//! {"category" : "fiction","author" : "Herman Melville","title" : "Moby Dick","isbn" : "0-553-21311-3","price" : 8.99}
//! ]);
//! assert_eq!(ret, json);
//! assert_eq!(selector("$.store.book[?(@.price < 10)]").unwrap(),
//! vec![
//! &json!({"category" : "reference","author" : "Nigel Rees","title" : "Sayings of the Century","price" : 8.95}),
//! &json!({"category" : "fiction","author" : "Herman Melville","title" : "Moby Dick","isbn" : "0-553-21311-3","price" : 8.99})
//! ]);
//! ```
extern crate array_tool;
extern crate core;
extern crate env_logger;
extern crate indexmap;
#[macro_use]
extern crate log;
#[macro_use]
extern crate serde;
extern crate serde_json;
use core::borrow::BorrowMut;
use std::result;
use serde_json::Value;
#[doc(hidden)]
pub mod parser;
#[doc(hidden)]
pub mod filter;
#[doc(hidden)]
pub mod ref_value;
#[doc(hidden)]
pub mod select;
pub use parser::parser::{Node, Parser};
pub use select::JsonPathError;
pub use select::{Selector, SelectorMut};
pub use select::Selector;
#[doc(hidden)]
mod parser;
#[doc(hidden)]
mod select;
/// It is a high-order function. it compile a JsonPath and then returns a function. this return-function can be reused for different JsonObjects.
/// It is a high-order function. it compile a jsonpath and then returns a closure that has JSON as argument. if you need to reuse a jsonpath, it is good for performance.
///
/// ```rust
/// extern crate jsonpath_lib as jsonpath;
/// #[macro_use] extern crate serde_json;
///
/// let mut template = jsonpath::compile("$..friends[0]");
/// let mut first_firend = jsonpath::compile("$..friends[0]");
///
/// let json_obj = json!({
/// "school": {
/// "friends": [
/// {"name": "친구1", "age": 20},
/// {"name": "친구2", "age": 20}
/// ]
/// },
/// "friends": [
/// {"name": "친구3", "age": 30},
/// {"name": "친구4"}
/// "school": {
/// "friends": [
/// {"name": "친구1", "age": 20},
/// {"name": "친구2", "age": 20}
/// ]
/// },
/// "friends": [
/// {"name": "친구3", "age": 30},
/// {"name": "친구4"}
/// ]});
///
/// let json = template(&json_obj).unwrap();
/// let ret = json!([
/// {"name": "친구3", "age": 30},
/// {"name": "친구1", "age": 20}
/// let json = first_firend(&json_obj).unwrap();
///
/// assert_eq!(json, vec![
/// &json!({"name": "친구3", "age": 30}),
/// &json!({"name": "친구1", "age": 20})
/// ]);
/// assert_eq!(json, ret);
/// ```
pub fn compile<'a>(path: &'a str) -> impl FnMut(&Value) -> result::Result<Value, String> + 'a {
let mut selector = select::Selector::new();
let _ = selector.path(path);
let mut selector = Box::new(selector);
move |json| {
let s: &mut select::Selector = selector.borrow_mut();
let _ = s.value(json.into());
s.select_to_value()
pub fn compile(path: &str) -> impl FnMut(&Value) -> Result<Vec<&Value>, JsonPathError> {
let node = Parser::compile(path);
move |json| match &node {
Ok(node) => {
let mut selector = Selector::new();
selector.compiled_path(node).value(json).select()
}
Err(e) => Err(JsonPathError::Path(e.to_string())),
}
}
/// It is a high-order function that return a function. this return-function has a jsonpath as argument and return a serde_json::value::Value. so you can use different JsonPath for one JsonObject.
/// It is a high-order function. it returns a closure that has a jsonpath string as argument. you can use diffenent jsonpath for one JSON object.
///
/// ```rust
/// extern crate jsonpath_lib as jsonpath;
/// #[macro_use] extern crate serde_json;
///
/// let json_obj = json!({
/// "school": {
/// "friends": [
/// {"name": "친구1", "age": 20},
/// {"name": "친구2", "age": 20}
/// ]
/// },
/// "friends": [
/// {"name": "친구3", "age": 30},
/// {"name": "친구4"}
/// "school": {
/// "friends": [
/// {"name": "친구1", "age": 20},
/// {"name": "친구2", "age": 20}
/// ]
/// },
/// "friends": [
/// {"name": "친구3", "age": 30},
/// {"name": "친구4"}
/// ]});
///
/// let mut selector = jsonpath::selector(&json_obj);
///
/// let json = selector("$..friends[0]").unwrap();
/// let ret = json!([
/// {"name": "친구3", "age": 30},
/// {"name": "친구1", "age": 20}
///
/// assert_eq!(json, vec![
/// &json!({"name": "친구3", "age": 30}),
/// &json!({"name": "친구1", "age": 20})
/// ]);
/// assert_eq!(json, ret);
///
/// let json = selector("$..friends[1]").unwrap();
/// let ret = json!([
/// {"name": "친구4"},
/// {"name": "친구2", "age": 20}
///
/// assert_eq!(json, vec![
/// &json!({"name": "친구4"}),
/// &json!({"name": "친구2", "age": 20})
/// ]);
/// assert_eq!(json, ret);
/// ```
pub fn selector<'a>(json: &Value) -> impl FnMut(&'a str) -> result::Result<Value, String> {
let mut selector = select::Selector::new();
let _ = selector.value(json.into());
let mut selector = Box::new(selector);
move |path: &'a str| {
let s: &mut select::Selector = selector.borrow_mut();
s.path(path)?.select_to_value()
}
pub fn selector<'a>(json: &'a Value) -> impl FnMut(&'a str) -> Result<Vec<&Value>, JsonPathError> {
let mut selector = Selector::new();
let _ = selector.value(json);
move |path: &str| selector.str_path(path)?.reset_value().select()
}
/// It is a high-order function that returns a function. this return-function has a jsonpath as argument and return a serde::Deserialize. so you can use different JsonPath for one JsonObject.
/// It is the same to `selector` function. but it deserialize the result as given type `T`.
///
/// ```rust
/// extern crate jsonpath_lib as jsonpath;
@ -275,26 +230,27 @@ pub fn selector<'a>(json: &Value) -> impl FnMut(&'a str) -> result::Result<Value
/// use serde::{Deserialize, Serialize};
///
/// let json_obj = json!({
/// "school": {
/// "friends": [
/// {"name": "친구1", "age": 20},
/// {"name": "친구2", "age": 20}
/// ]
/// },
/// "friends": [
/// {"name": "친구3", "age": 30},
/// {"name": "친구4"}
/// "school": {
/// "friends": [
/// {"name": "친구1", "age": 20},
/// {"name": "친구2", "age": 20}
/// ]
/// },
/// "friends": [
/// {"name": "친구3", "age": 30},
/// {"name": "친구4"}
/// ]});
///
/// #[derive(Serialize, Deserialize, PartialEq, Debug)]
/// #[derive(Deserialize, PartialEq, Debug)]
/// struct Friend {
/// name: String,
/// age: Option<u8>,
/// }
///
/// let mut selector = jsonpath::selector_as::<Vec<Friend>>(&json_obj);
/// let mut selector = jsonpath::selector_as::<Friend>(&json_obj);
///
/// let json = selector("$..friends[0]").unwrap();
///
/// let ret = vec!(
/// Friend { name: "친구3".to_string(), age: Some(30) },
/// Friend { name: "친구1".to_string(), age: Some(20) }
@ -302,67 +258,52 @@ pub fn selector<'a>(json: &Value) -> impl FnMut(&'a str) -> result::Result<Value
/// assert_eq!(json, ret);
///
/// let json = selector("$..friends[1]").unwrap();
///
/// let ret = vec!(
/// Friend { name: "친구4".to_string(), age: None },
/// Friend { name: "친구2".to_string(), age: Some(20) }
/// );
///
/// assert_eq!(json, ret);
/// ```
pub fn selector_as<T: serde::de::DeserializeOwned>(json: &Value) -> impl FnMut(&str) -> result::Result<T, String> {
let mut selector = select::Selector::new();
let _ = selector.value(json.into());
move |path: &str| {
selector.path(path)?.select_to()
}
pub fn selector_as<T: serde::de::DeserializeOwned>(
json: &Value,
) -> impl FnMut(&str) -> Result<Vec<T>, JsonPathError> + '_ {
let mut selector = Selector::new();
let _ = selector.value(json);
move |path: &str| selector.str_path(path)?.reset_value().select_as()
}
#[deprecated(since = "0.1.4", note = "Please use the selector function instead")]
pub fn reader<'a>(json: &Value) -> impl FnMut(&'a str) -> result::Result<Value, String> {
selector(json)
}
/// This function compile a jsonpath everytime and it convert `serde_json's Value` to `jsonpath's RefValue` everytime and then it return a `serde_json::value::Value`.
/// It is a simple select function. but it compile the jsonpath argument every time.
///
/// ```rust
/// extern crate jsonpath_lib as jsonpath;
/// #[macro_use] extern crate serde_json;
///
/// let json_obj = json!({
/// "school": {
/// "friends": [
/// {"name": "친구1", "age": 20},
/// {"name": "친구2", "age": 20}
/// ]
/// },
/// "friends": [
/// {"name": "친구3", "age": 30},
/// {"name": "친구4"}
/// "school": {
/// "friends": [
/// {"name": "친구1", "age": 20},
/// {"name": "친구2", "age": 20}
/// ]
/// },
/// "friends": [
/// {"name": "친구3", "age": 30},
/// {"name": "친구4"}
/// ]});
///
/// let json = jsonpath::select(&json_obj, "$..friends[0]").unwrap();
///
/// let ret = json!([
/// {"name": "친구3", "age": 30},
/// {"name": "친구1", "age": 20}
/// assert_eq!(json, vec![
/// &json!({"name": "친구3", "age": 30}),
/// &json!({"name": "친구1", "age": 20})
/// ]);
/// assert_eq!(json, ret);
/// ```
pub fn select(json: &Value, path: &str) -> result::Result<Value, String> {
let mut selector = select::Selector::new();
selector.path(path)?.value(json.into())?.select_to_value()
pub fn select<'a>(json: &'a Value, path: &'a str) -> Result<Vec<&'a Value>, JsonPathError> {
Selector::new().str_path(path)?.value(json).select()
}
#[deprecated(since = "0.1.4", note = "Please use the select function instead")]
pub fn read(json: &Value, path: &str) -> result::Result<Value, String> {
select(json, path)
}
#[deprecated(since = "0.1.7", note = "Please use the select_as_str function instead")]
pub fn select_str(json: &str, path: &str) -> result::Result<String, String> {
select_as_str(json, path)
}
/// This function compile a jsonpath everytime and it convert `&str` to `jsonpath's RefValue` everytime and then it return a json string.
/// It is the same to `select` function but it return the result as string.
///
/// ```rust
/// extern crate jsonpath_lib as jsonpath;
@ -385,14 +326,13 @@ pub fn select_str(json: &str, path: &str) -> result::Result<String, String> {
///
/// assert_eq!(ret, r#"[{"name":"친구3","age":30},{"name":"친구1","age":20}]"#);
/// ```
pub fn select_as_str(json: &str, path: &str) -> result::Result<String, String> {
select::Selector::new()
.path(path)?
.value_from_str(json)?
.select_to_str()
pub fn select_as_str(json_str: &str, path: &str) -> Result<String, JsonPathError> {
let json = serde_json::from_str(json_str).map_err(|e| JsonPathError::Serde(e.to_string()))?;
let ret = Selector::new().str_path(path)?.value(&json).select()?;
serde_json::to_string(&ret).map_err(|e| JsonPathError::Serde(e.to_string()))
}
/// This function compile a jsonpath everytime and it convert `&str` to `jsonpath's RefValue` everytime and then it return a deserialized-instance of type `T`.
/// It is the same to `select` function but it deserialize the the result as given type `T`.
///
/// ```rust
/// extern crate jsonpath_lib as jsonpath;
@ -408,7 +348,7 @@ pub fn select_as_str(json: &str, path: &str) -> result::Result<String, String> {
/// phones: Vec<String>,
/// }
///
/// let ret: Person = jsonpath::select_as(r#"
/// let ret: Vec<Person> = jsonpath::select_as(r#"
/// {
/// "person":
/// {
@ -428,11 +368,111 @@ pub fn select_as_str(json: &str, path: &str) -> result::Result<String, String> {
/// phones: vec!["+44 1234567".to_string(), "+44 2345678".to_string()],
/// };
///
/// assert_eq!(person, ret);
/// assert_eq!(ret[0], person);
/// ```
pub fn select_as<T: serde::de::DeserializeOwned>(json: &str, path: &str) -> result::Result<T, String> {
select::Selector::new()
.path(path)?
.value_from_str(json)?
.select_to()
}
pub fn select_as<T: serde::de::DeserializeOwned>(
json_str: &str,
path: &str,
) -> Result<Vec<T>, JsonPathError> {
let json = serde_json::from_str(json_str).map_err(|e| JsonPathError::Serde(e.to_string()))?;
Selector::new().str_path(path)?.value(&json).select_as()
}
/// Delete(= replace with null) the JSON property using the jsonpath.
///
/// ```rust
/// extern crate jsonpath_lib as jsonpath;
/// #[macro_use] extern crate serde_json;
///
/// let json_obj = json!({
/// "school": {
/// "friends": [
/// {"name": "친구1", "age": 20},
/// {"name": "친구2", "age": 20}
/// ]
/// },
/// "friends": [
/// {"name": "친구3", "age": 30},
/// {"name": "친구4"}
/// ]});
///
/// let ret = jsonpath::delete(json_obj, "$..[?(20 == @.age)]").unwrap();
///
/// assert_eq!(ret, json!({
/// "school": {
/// "friends": [
/// null,
/// null
/// ]
/// },
/// "friends": [
/// {"name": "친구3", "age": 30},
/// {"name": "친구4"}
/// ]}));
/// ```
pub fn delete(value: Value, path: &str) -> Result<Value, JsonPathError> {
let mut selector = SelectorMut::new();
let ret = selector
.str_path(path)?
.value(value)
.delete()?
.take()
.unwrap_or(Value::Null);
Ok(ret)
}
/// Select JSON properties using a jsonpath and transform the result and then replace it. via closure that implements `FnMut` you can transform the selected results.
///
/// ```rust
/// extern crate jsonpath_lib as jsonpath;
/// #[macro_use] extern crate serde_json;
///
/// use serde_json::Value;
///
/// let json_obj = json!({
/// "school": {
/// "friends": [
/// {"name": "친구1", "age": 20},
/// {"name": "친구2", "age": 20}
/// ]
/// },
/// "friends": [
/// {"name": "친구3", "age": 30},
/// {"name": "친구4"}
/// ]});
///
/// let ret = jsonpath::replace_with(json_obj, "$..[?(@.age == 20)].age", &mut |v| {
/// let age = if let Value::Number(n) = v {
/// n.as_u64().unwrap() * 2
/// } else {
/// 0
/// };
///
/// json!(age)
/// }).unwrap();
///
/// assert_eq!(ret, json!({
/// "school": {
/// "friends": [
/// {"name": "친구1", "age": 40},
/// {"name": "친구2", "age": 40}
/// ]
/// },
/// "friends": [
/// {"name": "친구3", "age": 30},
/// {"name": "친구4"}
/// ]}));
/// ```
pub fn replace_with<F>(value: Value, path: &str, fun: &mut F) -> Result<Value, JsonPathError>
where
F: FnMut(&Value) -> Value,
{
let mut selector = SelectorMut::new();
let ret = selector
.str_path(path)?
.value(value)
.replace_with(fun)?
.take()
.unwrap_or(Value::Null);
Ok(ret)
}

View File

@ -1,3 +1,792 @@
pub mod parser;
mod path_reader;
pub mod tokenizer;
pub mod parser;
pub(crate) mod tokenizer;
#[cfg(test)]
mod parser_tests {
use parser::parser::{FilterToken, NodeVisitor, ParseToken, Parser};
struct NodeVisitorTestImpl<'a> {
input: &'a str,
stack: Vec<ParseToken>,
}
impl<'a> NodeVisitorTestImpl<'a> {
fn new(input: &'a str) -> Self {
NodeVisitorTestImpl {
input,
stack: Vec::new(),
}
}
fn start(&mut self) -> Result<Vec<ParseToken>, String> {
let node = Parser::compile(self.input)?;
self.visit(&node);
Ok(self.stack.split_off(0))
}
}
impl<'a> NodeVisitor for NodeVisitorTestImpl<'a> {
fn visit_token(&mut self, token: &ParseToken) {
self.stack.push(token.clone());
}
}
fn setup() {
let _ = env_logger::try_init();
}
fn run(input: &str) -> Result<Vec<ParseToken>, String> {
let mut interpreter = NodeVisitorTestImpl::new(input);
interpreter.start()
}
#[test]
fn parse_path() {
setup();
assert_eq!(
run("$.aa"),
Ok(vec![
ParseToken::Absolute,
ParseToken::In,
ParseToken::Key("aa".to_owned())
])
);
assert_eq!(
run("$.00.a"),
Ok(vec![
ParseToken::Absolute,
ParseToken::In,
ParseToken::Key("00".to_owned()),
ParseToken::In,
ParseToken::Key("a".to_owned())
])
);
assert_eq!(
run("$.00.韓창.seok"),
Ok(vec![
ParseToken::Absolute,
ParseToken::In,
ParseToken::Key("00".to_owned()),
ParseToken::In,
ParseToken::Key("韓창".to_owned()),
ParseToken::In,
ParseToken::Key("seok".to_owned())
])
);
assert_eq!(
run("$.*"),
Ok(vec![ParseToken::Absolute, ParseToken::In, ParseToken::All])
);
assert_eq!(
run("$..*"),
Ok(vec![
ParseToken::Absolute,
ParseToken::Leaves,
ParseToken::All
])
);
assert_eq!(
run("$..[0]"),
Ok(vec![
ParseToken::Absolute,
ParseToken::Leaves,
ParseToken::Array,
ParseToken::Number(0.0),
ParseToken::ArrayEof
])
);
match run("$.") {
Ok(_) => panic!(),
_ => {}
}
match run("$..") {
Ok(_) => panic!(),
_ => {}
}
match run("$. a") {
Ok(_) => panic!(),
_ => {}
}
}
#[test]
fn parse_array_sytax() {
setup();
assert_eq!(
run("$.book[?(@.isbn)]"),
Ok(vec![
ParseToken::Absolute,
ParseToken::In,
ParseToken::Key("book".to_string()),
ParseToken::Array,
ParseToken::Relative,
ParseToken::In,
ParseToken::Key("isbn".to_string()),
ParseToken::ArrayEof
])
);
//
// Array도 컨텍스트 In으로 간주 할거라서 중첩되면 하나만
//
assert_eq!(
run("$.[*]"),
Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::All,
ParseToken::ArrayEof
])
);
assert_eq!(
run("$.a[*]"),
Ok(vec![
ParseToken::Absolute,
ParseToken::In,
ParseToken::Key("a".to_owned()),
ParseToken::Array,
ParseToken::All,
ParseToken::ArrayEof
])
);
assert_eq!(
run("$.a[*].가"),
Ok(vec![
ParseToken::Absolute,
ParseToken::In,
ParseToken::Key("a".to_owned()),
ParseToken::Array,
ParseToken::All,
ParseToken::ArrayEof,
ParseToken::In,
ParseToken::Key("".to_owned())
])
);
assert_eq!(
run("$.a[0][1]"),
Ok(vec![
ParseToken::Absolute,
ParseToken::In,
ParseToken::Key("a".to_owned()),
ParseToken::Array,
ParseToken::Number(0_f64),
ParseToken::ArrayEof,
ParseToken::Array,
ParseToken::Number(1_f64),
ParseToken::ArrayEof
])
);
assert_eq!(
run("$.a[1,2]"),
Ok(vec![
ParseToken::Absolute,
ParseToken::In,
ParseToken::Key("a".to_owned()),
ParseToken::Array,
ParseToken::Union(vec![1, 2]),
ParseToken::ArrayEof
])
);
assert_eq!(
run("$.a[10:]"),
Ok(vec![
ParseToken::Absolute,
ParseToken::In,
ParseToken::Key("a".to_owned()),
ParseToken::Array,
ParseToken::Range(Some(10), None, None),
ParseToken::ArrayEof
])
);
assert_eq!(
run("$.a[:11]"),
Ok(vec![
ParseToken::Absolute,
ParseToken::In,
ParseToken::Key("a".to_owned()),
ParseToken::Array,
ParseToken::Range(None, Some(11), None),
ParseToken::ArrayEof
])
);
assert_eq!(
run("$.a[-12:13]"),
Ok(vec![
ParseToken::Absolute,
ParseToken::In,
ParseToken::Key("a".to_owned()),
ParseToken::Array,
ParseToken::Range(Some(-12), Some(13), None),
ParseToken::ArrayEof
])
);
assert_eq!(
run(r#"$[0:3:2]"#),
Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Range(Some(0), Some(3), Some(2)),
ParseToken::ArrayEof
])
);
assert_eq!(
run(r#"$[:3:2]"#),
Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Range(None, Some(3), Some(2)),
ParseToken::ArrayEof
])
);
assert_eq!(
run(r#"$[:]"#),
Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Range(None, None, None),
ParseToken::ArrayEof
])
);
assert_eq!(
run(r#"$[::]"#),
Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Range(None, None, None),
ParseToken::ArrayEof
])
);
assert_eq!(
run(r#"$[::2]"#),
Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Range(None, None, Some(2)),
ParseToken::ArrayEof
])
);
assert_eq!(
run(r#"$["a", 'b']"#),
Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Keys(vec!["a".to_string(), "b".to_string()]),
ParseToken::ArrayEof
])
);
assert_eq!(
run("$.a[?(1>2)]"),
Ok(vec![
ParseToken::Absolute,
ParseToken::In,
ParseToken::Key("a".to_owned()),
ParseToken::Array,
ParseToken::Number(1_f64),
ParseToken::Number(2_f64),
ParseToken::Filter(FilterToken::Greater),
ParseToken::ArrayEof
])
);
assert_eq!(
run("$.a[?($.b>3)]"),
Ok(vec![
ParseToken::Absolute,
ParseToken::In,
ParseToken::Key("a".to_owned()),
ParseToken::Array,
ParseToken::Absolute,
ParseToken::In,
ParseToken::Key("b".to_owned()),
ParseToken::Number(3_f64),
ParseToken::Filter(FilterToken::Greater),
ParseToken::ArrayEof
])
);
assert_eq!(
run("$[?($.c>@.d && 1==2)]"),
Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Absolute,
ParseToken::In,
ParseToken::Key("c".to_owned()),
ParseToken::Relative,
ParseToken::In,
ParseToken::Key("d".to_owned()),
ParseToken::Filter(FilterToken::Greater),
ParseToken::Number(1_f64),
ParseToken::Number(2_f64),
ParseToken::Filter(FilterToken::Equal),
ParseToken::Filter(FilterToken::And),
ParseToken::ArrayEof
])
);
assert_eq!(
run("$[?($.c>@.d&&(1==2||3>=4))]"),
Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Absolute,
ParseToken::In,
ParseToken::Key("c".to_owned()),
ParseToken::Relative,
ParseToken::In,
ParseToken::Key("d".to_owned()),
ParseToken::Filter(FilterToken::Greater),
ParseToken::Number(1_f64),
ParseToken::Number(2_f64),
ParseToken::Filter(FilterToken::Equal),
ParseToken::Number(3_f64),
ParseToken::Number(4_f64),
ParseToken::Filter(FilterToken::GreaterOrEqual),
ParseToken::Filter(FilterToken::Or),
ParseToken::Filter(FilterToken::And),
ParseToken::ArrayEof
])
);
assert_eq!(
run("$[?(@.a<@.b)]"),
Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Relative,
ParseToken::In,
ParseToken::Key("a".to_owned()),
ParseToken::Relative,
ParseToken::In,
ParseToken::Key("b".to_owned()),
ParseToken::Filter(FilterToken::Little),
ParseToken::ArrayEof
])
);
assert_eq!(
run("$[*][*][*]"),
Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::All,
ParseToken::ArrayEof,
ParseToken::Array,
ParseToken::All,
ParseToken::ArrayEof,
ParseToken::Array,
ParseToken::All,
ParseToken::ArrayEof
])
);
assert_eq!(
run("$['a']['bb']"),
Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Key("a".to_string()),
ParseToken::ArrayEof,
ParseToken::Array,
ParseToken::Key("bb".to_string()),
ParseToken::ArrayEof
])
);
assert_eq!(
run("$.a[?(@.e==true)]"),
Ok(vec![
ParseToken::Absolute,
ParseToken::In,
ParseToken::Key("a".to_string()),
ParseToken::Array,
ParseToken::Relative,
ParseToken::In,
ParseToken::Key("e".to_string()),
ParseToken::Bool(true),
ParseToken::Filter(FilterToken::Equal),
ParseToken::ArrayEof
])
);
assert_eq!(
run("$[:]"),
Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Range(None, None, None),
ParseToken::ArrayEof
])
);
assert_eq!(
run(r#"$['single\'quote']"#),
Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Key("single'quote".to_string()),
ParseToken::ArrayEof
])
);
assert_eq!(
run(r#"$["single\"quote"]"#),
Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Key(r#"single"quote"#.to_string()),
ParseToken::ArrayEof
])
);
match run("$[") {
Ok(_) => panic!(),
_ => {}
}
match run("$[]") {
Ok(_) => panic!(),
_ => {}
}
match run("$[a]") {
Ok(_) => panic!(),
_ => {}
}
match run("$[?($.a)]") {
Ok(_) => panic!(),
_ => {}
}
match run("$[?(@.a > @.b]") {
Ok(_) => panic!(),
_ => {}
}
match run("$[?(@.a < @.b&&(@.c < @.d)]") {
Ok(_) => panic!(),
_ => {}
}
}
#[test]
fn parse_array_float() {
setup();
assert_eq!(
run("$[?(1.1<2.1)]"),
Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Number(1.1),
ParseToken::Number(2.1),
ParseToken::Filter(FilterToken::Little),
ParseToken::ArrayEof
])
);
match run("$[1.1]") {
Ok(_) => panic!(),
_ => {}
}
match run("$[?(1.1<.2)]") {
Ok(_) => panic!(),
_ => {}
}
match run("$[?(1.1<2.)]") {
Ok(_) => panic!(),
_ => {}
}
match run("$[?(1.1<2.a)]") {
Ok(_) => panic!(),
_ => {}
}
}
}
#[cfg(test)]
mod tokenizer_tests {
use parser::tokenizer::{Token, TokenError, TokenReader, Tokenizer};
fn setup() {
let _ = env_logger::try_init();
}
fn collect_token(input: &str) -> (Vec<Token>, Option<TokenError>) {
let mut tokenizer = Tokenizer::new(input);
let mut vec = vec![];
loop {
match tokenizer.next_token() {
Ok(t) => vec.push(t),
Err(e) => return (vec, Some(e)),
}
}
}
fn run(input: &str, expected: (Vec<Token>, Option<TokenError>)) {
let (vec, err) = collect_token(input.clone());
assert_eq!((vec, err), expected, "\"{}\"", input);
}
#[test]
fn peek() {
let mut tokenizer = TokenReader::new("$.a");
match tokenizer.next_token() {
Ok(t) => assert_eq!(Token::Absolute(0), t),
_ => panic!(),
}
match tokenizer.peek_token() {
Ok(t) => assert_eq!(&Token::Dot(1), t),
_ => panic!(),
}
match tokenizer.peek_token() {
Ok(t) => assert_eq!(&Token::Dot(1), t),
_ => panic!(),
}
match tokenizer.next_token() {
Ok(t) => assert_eq!(Token::Dot(1), t),
_ => panic!(),
}
}
#[test]
fn token() {
setup();
run(
"$.01.a",
(
vec![
Token::Absolute(0),
Token::Dot(1),
Token::Key(2, "01".to_string()),
Token::Dot(4),
Token::Key(5, "a".to_string()),
],
Some(TokenError::Eof),
),
);
run(
"$. []",
(
vec![
Token::Absolute(0),
Token::Dot(1),
Token::Whitespace(2, 2),
Token::OpenArray(5),
Token::CloseArray(6),
],
Some(TokenError::Eof),
),
);
run(
"$..",
(
vec![Token::Absolute(0), Token::Dot(1), Token::Dot(2)],
Some(TokenError::Eof),
),
);
run(
"$..ab",
(
vec![
Token::Absolute(0),
Token::Dot(1),
Token::Dot(2),
Token::Key(3, "ab".to_string()),
],
Some(TokenError::Eof),
),
);
run(
"$..가 [",
(
vec![
Token::Absolute(0),
Token::Dot(1),
Token::Dot(2),
Token::Key(3, "".to_string()),
Token::Whitespace(6, 0),
Token::OpenArray(7),
],
Some(TokenError::Eof),
),
);
run(
"[-1, 2 ]",
(
vec![
Token::OpenArray(0),
Token::Key(1, "-1".to_string()),
Token::Comma(3),
Token::Whitespace(4, 0),
Token::Key(5, "2".to_string()),
Token::Whitespace(6, 0),
Token::CloseArray(7),
],
Some(TokenError::Eof),
),
);
run(
"[ 1 2 , 3 \"abc\" : -10 ]",
(
vec![
Token::OpenArray(0),
Token::Whitespace(1, 0),
Token::Key(2, "1".to_string()),
Token::Whitespace(3, 0),
Token::Key(4, "2".to_string()),
Token::Whitespace(5, 0),
Token::Comma(6),
Token::Whitespace(7, 0),
Token::Key(8, "3".to_string()),
Token::Whitespace(9, 0),
Token::DoubleQuoted(10, "abc".to_string()),
Token::Whitespace(15, 0),
Token::Split(16),
Token::Whitespace(17, 0),
Token::Key(18, "-10".to_string()),
Token::Whitespace(21, 0),
Token::CloseArray(22),
],
Some(TokenError::Eof),
),
);
run(
"?(@.a가 <41.01)",
(
vec![
Token::Question(0),
Token::OpenParenthesis(1),
Token::At(2),
Token::Dot(3),
Token::Key(4, "a가".to_string()),
Token::Whitespace(8, 0),
Token::Little(9),
Token::Key(10, "41".to_string()),
Token::Dot(12),
Token::Key(13, "01".to_string()),
Token::CloseParenthesis(15),
],
Some(TokenError::Eof),
),
);
run(
"?(@.a <4a.01)",
(
vec![
Token::Question(0),
Token::OpenParenthesis(1),
Token::At(2),
Token::Dot(3),
Token::Key(4, "a".to_string()),
Token::Whitespace(5, 0),
Token::Little(6),
Token::Key(7, "4a".to_string()),
Token::Dot(9),
Token::Key(10, "01".to_string()),
Token::CloseParenthesis(12),
],
Some(TokenError::Eof),
),
);
run(
"?($.c>@.d)",
(
vec![
Token::Question(0),
Token::OpenParenthesis(1),
Token::Absolute(2),
Token::Dot(3),
Token::Key(4, "c".to_string()),
Token::Greater(5),
Token::At(6),
Token::Dot(7),
Token::Key(8, "d".to_string()),
Token::CloseParenthesis(9),
],
Some(TokenError::Eof),
),
);
run(
"$[:]",
(
vec![
Token::Absolute(0),
Token::OpenArray(1),
Token::Split(2),
Token::CloseArray(3),
],
Some(TokenError::Eof),
),
);
run(
r#"$['single\'quote']"#,
(
vec![
Token::Absolute(0),
Token::OpenArray(1),
Token::SingleQuoted(2, "single\'quote".to_string()),
Token::CloseArray(17),
],
Some(TokenError::Eof),
),
);
run(
r#"$["double\"quote"]"#,
(
vec![
Token::Absolute(0),
Token::OpenArray(1),
Token::DoubleQuoted(2, "double\"quote".to_string()),
Token::CloseArray(17),
],
Some(TokenError::Eof),
),
);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -12,10 +12,7 @@ pub struct PathReader<'a> {
impl<'a> PathReader<'a> {
pub fn new(input: &'a str) -> Self {
PathReader {
input,
pos: 0,
}
PathReader { input, pos: 0 }
}
pub fn peek_char(&self) -> Result<(usize, char), ReaderError> {
@ -24,8 +21,8 @@ impl<'a> PathReader<'a> {
}
pub fn take_while<F>(&mut self, fun: F) -> Result<(usize, String), ReaderError>
where
F: Fn(&char) -> bool
where
F: Fn(&char) -> bool,
{
let mut char_len: usize = 0;
let mut ret = String::new();
@ -53,4 +50,4 @@ impl<'a> PathReader<'a> {
pub fn current_pos(&self) -> usize {
self.pos
}
}
}

View File

@ -1,31 +1,30 @@
use std::io::Write;
use std::result::Result;
use super::path_reader::{PathReader, ReaderError};
const ABSOLUTE: &'static str = "$";
const DOT: &'static str = ".";
const AT: &'static str = "@";
const OPEN_ARRAY: &'static str = "[";
const CLOSE_ARRAY: &'static str = "]";
const ASTERISK: &'static str = "*";
const QUESTION: &'static str = "?";
const COMMA: &'static str = ",";
const SPLIT: &'static str = ":";
const OPEN_PARENTHESIS: &'static str = "(";
const CLOSE_PARENTHESIS: &'static str = ")";
const KEY: &'static str = "Key";
const DOUBLE_QUOTA: &'static str = "\"";
const SINGLE_QUOTA: &'static str = "'";
const EQUAL: &'static str = "==";
const GREATER_OR_EQUAL: &'static str = ">=";
const GREATER: &'static str = ">";
const LITTLE: &'static str = "<";
const LITTLE_OR_EQUAL: &'static str = "<=";
const NOT_EQUAL: &'static str = "!=";
const AND: &'static str = "&&";
const OR: &'static str = "||";
const WHITESPACE: &'static str = " ";
pub const ABSOLUTE: &'static str = "$";
pub const DOT: &'static str = ".";
pub const AT: &'static str = "@";
pub const OPEN_ARRAY: &'static str = "[";
pub const CLOSE_ARRAY: &'static str = "]";
pub const ASTERISK: &'static str = "*";
pub const QUESTION: &'static str = "?";
pub const COMMA: &'static str = ",";
pub const SPLIT: &'static str = ":";
pub const OPEN_PARENTHESIS: &'static str = "(";
pub const CLOSE_PARENTHESIS: &'static str = ")";
pub const KEY: &'static str = "Key";
pub const DOUBLE_QUOTE: &'static str = "\"";
pub const SINGLE_QUOTE: &'static str = "'";
pub const EQUAL: &'static str = "==";
pub const GREATER_OR_EQUAL: &'static str = ">=";
pub const GREATER: &'static str = ">";
pub const LITTLE: &'static str = "<";
pub const LITTLE_OR_EQUAL: &'static str = "<=";
pub const NOT_EQUAL: &'static str = "!=";
pub const AND: &'static str = "&&";
pub const OR: &'static str = "||";
pub const WHITESPACE: &'static str = " ";
const CH_DOLLA: char = '$';
const CH_DOT: char = '.';
@ -44,8 +43,8 @@ const CH_PIPE: char = '|';
const CH_LITTLE: char = '<';
const CH_GREATER: char = '>';
const CH_EXCLAMATION: char = '!';
const CH_SINGLE_QUOTA: char = '\'';
const CH_DOUBLE_QUOTA: char = '"';
const CH_SINGLE_QUOTE: char = '\'';
const CH_DOUBLE_QUOTE: char = '"';
#[derive(Debug, Clone, PartialEq)]
pub enum TokenError {
@ -55,7 +54,7 @@ pub enum TokenError {
fn to_token_error(read_err: ReaderError) -> TokenError {
match read_err {
ReaderError::Eof => TokenError::Eof
ReaderError::Eof => TokenError::Eof,
}
}
@ -91,6 +90,10 @@ impl Token {
self.to_simple() == other.to_simple()
}
pub fn simple_eq(&self, str_token: &str) -> bool {
self.to_simple() == str_token
}
fn to_simple(&self) -> &'static str {
match self {
Token::Absolute(_) => ABSOLUTE,
@ -105,8 +108,8 @@ impl Token {
Token::OpenParenthesis(_) => OPEN_PARENTHESIS,
Token::CloseParenthesis(_) => CLOSE_PARENTHESIS,
Token::Key(_, _) => KEY,
Token::DoubleQuoted(_, _) => DOUBLE_QUOTA,
Token::SingleQuoted(_, _) => SINGLE_QUOTA,
Token::DoubleQuoted(_, _) => DOUBLE_QUOTE,
Token::SingleQuoted(_, _) => SINGLE_QUOTE,
Token::Equal(_) => EQUAL,
Token::GreaterOrEqual(_) => GREATER_OR_EQUAL,
Token::Greater(_) => GREATER,
@ -115,7 +118,7 @@ impl Token {
Token::NotEqual(_) => NOT_EQUAL,
Token::And(_) => AND,
Token::Or(_) => OR,
Token::Whitespace(_, _) => WHITESPACE
Token::Whitespace(_, _) => WHITESPACE,
}
}
}
@ -133,7 +136,7 @@ fn simple_matched_token(ch: char, pos: usize) -> Option<Token> {
CH_QUESTION => Some(Token::Question(pos)),
CH_COMMA => Some(Token::Comma(pos)),
CH_SEMICOLON => Some(Token::Split(pos)),
_ => None
_ => None,
}
}
@ -143,20 +146,42 @@ pub struct Tokenizer<'a> {
impl<'a> Tokenizer<'a> {
pub fn new(input: &'a str) -> Self {
trace!("input: {}", input);
Tokenizer {
input: PathReader::new(input),
}
}
fn single_quota(&mut self, pos: usize, ch: char) -> Result<Token, TokenError> {
let (_, val) = self.input.take_while(|c| *c != ch).map_err(to_token_error)?;
self.input.next_char().map_err(to_token_error)?;
fn quote(&mut self, ch: char) -> Result<String, TokenError> {
let (_, mut val) = self
.input
.take_while(|c| *c != ch)
.map_err(to_token_error)?;
if let Some('\\') = val.chars().last() {
self.input.next_char().map_err(to_token_error)?;
let _ = val.pop();
let (_, mut val_remain) = self
.input
.take_while(|c| *c != ch)
.map_err(to_token_error)?;
self.input.next_char().map_err(to_token_error)?;
val.push(ch);
val.push_str(val_remain.as_str());
} else {
self.input.next_char().map_err(to_token_error)?;
}
Ok(val)
}
fn single_quote(&mut self, pos: usize, ch: char) -> Result<Token, TokenError> {
let val = self.quote(ch)?;
Ok(Token::SingleQuoted(pos, val))
}
fn double_quota(&mut self, pos: usize, ch: char) -> Result<Token, TokenError> {
let (_, val) = self.input.take_while(|c| *c != ch).map_err(to_token_error)?;
self.input.next_char().map_err(to_token_error)?;
fn double_quote(&mut self, pos: usize, ch: char) -> Result<Token, TokenError> {
let val = self.quote(ch)?;
Ok(Token::DoubleQuoted(pos, val))
}
@ -167,7 +192,7 @@ impl<'a> Tokenizer<'a> {
self.input.next_char().map_err(to_token_error)?;
Ok(Token::Equal(pos))
}
_ => Err(TokenError::Position(pos))
_ => Err(TokenError::Position(pos)),
}
}
@ -178,7 +203,7 @@ impl<'a> Tokenizer<'a> {
self.input.next_char().map_err(to_token_error)?;
Ok(Token::NotEqual(pos))
}
_ => Err(TokenError::Position(pos))
_ => Err(TokenError::Position(pos)),
}
}
@ -211,7 +236,7 @@ impl<'a> Tokenizer<'a> {
let _ = self.input.next_char().map_err(to_token_error);
Ok(Token::And(pos))
}
_ => Err(TokenError::Position(pos))
_ => Err(TokenError::Position(pos)),
}
}
@ -222,27 +247,31 @@ impl<'a> Tokenizer<'a> {
self.input.next_char().map_err(to_token_error)?;
Ok(Token::Or(pos))
}
_ => Err(TokenError::Position(pos))
_ => Err(TokenError::Position(pos)),
}
}
fn whitespace(&mut self, pos: usize, _: char) -> Result<Token, TokenError> {
let (_, vec) = self.input.take_while(|c| c.is_whitespace()).map_err(to_token_error)?;
let (_, vec) = self
.input
.take_while(|c| c.is_whitespace())
.map_err(to_token_error)?;
Ok(Token::Whitespace(pos, vec.len()))
}
fn other(&mut self, pos: usize, ch: char) -> Result<Token, TokenError> {
let fun = |c: &char| {
match simple_matched_token(*c, pos) {
Some(_) => false,
_ if c == &CH_LITTLE
|| c == &CH_GREATER
|| c == &CH_EQUAL
|| c == &CH_AMPERSAND
|| c == &CH_PIPE
|| c == &CH_EXCLAMATION => false,
_ => !c.is_whitespace()
let fun = |c: &char| match simple_matched_token(*c, pos) {
Some(_) => false,
_ if c == &CH_LITTLE
|| c == &CH_GREATER
|| c == &CH_EQUAL
|| c == &CH_AMPERSAND
|| c == &CH_PIPE
|| c == &CH_EXCLAMATION =>
{
false
}
_ => !c.is_whitespace(),
};
let (_, mut vec) = self.input.take_while(fun).map_err(to_token_error)?;
vec.insert(0, ch);
@ -253,20 +282,18 @@ impl<'a> Tokenizer<'a> {
let (pos, ch) = self.input.next_char().map_err(to_token_error)?;
match simple_matched_token(ch, pos) {
Some(t) => Ok(t),
None => {
match ch {
CH_SINGLE_QUOTA => self.single_quota(pos, ch),
CH_DOUBLE_QUOTA => self.double_quota(pos, ch),
CH_EQUAL => self.equal(pos, ch),
CH_GREATER => self.greater(pos, ch),
CH_LITTLE => self.little(pos, ch),
CH_AMPERSAND => self.and(pos, ch),
CH_PIPE => self.or(pos, ch),
CH_EXCLAMATION => self.not_equal(pos, ch),
_ if ch.is_whitespace() => self.whitespace(pos, ch),
_ => self.other(pos, ch),
}
}
None => match ch {
CH_SINGLE_QUOTE => self.single_quote(pos, ch),
CH_DOUBLE_QUOTE => self.double_quote(pos, ch),
CH_EQUAL => self.equal(pos, ch),
CH_GREATER => self.greater(pos, ch),
CH_LITTLE => self.little(pos, ch),
CH_AMPERSAND => self.and(pos, ch),
CH_PIPE => self.or(pos, ch),
CH_EXCLAMATION => self.not_equal(pos, ch),
_ if ch.is_whitespace() => self.whitespace(pos, ch),
_ => self.other(pos, ch),
},
}
}
@ -275,7 +302,7 @@ impl<'a> Tokenizer<'a> {
}
}
pub struct PreloadedTokenizer<'a> {
pub struct TokenReader<'a> {
origin_input: &'a str,
err: TokenError,
err_pos: usize,
@ -283,7 +310,7 @@ pub struct PreloadedTokenizer<'a> {
curr_pos: Option<usize>,
}
impl<'a> PreloadedTokenizer<'a> {
impl<'a> TokenReader<'a> {
pub fn new(input: &'a str) -> Self {
let mut tokenizer = Tokenizer::new(input);
let mut tokens = vec![];
@ -293,7 +320,7 @@ impl<'a> PreloadedTokenizer<'a> {
tokens.insert(0, (tokenizer.current_pos(), t));
}
Err(e) => {
return PreloadedTokenizer {
return TokenReader {
origin_input: input.clone(),
err: e,
err_pos: tokenizer.current_pos(),
@ -305,6 +332,13 @@ impl<'a> PreloadedTokenizer<'a> {
}
}
pub fn peek_is(&self, simple_token: &str) -> bool {
match self.peek_token() {
Ok(t) => t.simple_eq(simple_token),
_ => false,
}
}
pub fn peek_token(&self) -> Result<&Token, TokenError> {
match self.tokens.last() {
Some((_, t)) => {
@ -333,23 +367,13 @@ impl<'a> PreloadedTokenizer<'a> {
}
pub fn err_msg_with_pos(&self, pos: usize) -> String {
let mut w = Vec::new();
writeln!(&mut w, "{}", self.origin_input).unwrap();
writeln!(&mut w, "{}", "^".repeat(pos)).unwrap();
match std::str::from_utf8(&w[..]) {
Ok(s) => s.to_owned(),
Err(_) => panic!("Invalid UTF-8")
}
format!("{}\n{}", self.origin_input, "^".repeat(pos))
}
pub fn err_msg(&self) -> String {
match self.curr_pos {
Some(pos) => {
self.err_msg_with_pos(pos)
}
_ => {
self.err_msg_with_pos(self.err_pos)
}
Some(pos) => self.err_msg_with_pos(pos),
_ => self.err_msg_with_pos(self.err_pos),
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +0,0 @@
pub mod model;
pub mod de;
pub mod ser;
pub mod serde_error;

View File

@ -1,273 +0,0 @@
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::sync::Arc;
use indexmap::map::IndexMap;
use serde::ser::Serialize;
use serde_json::{Number, Value};
type TypeRefValue = Arc<Box<RefValue>>;
#[derive(Debug, PartialEq)]
pub struct RefValueWrapper {
data: TypeRefValue
}
impl Eq for RefValueWrapper {}
impl Deref for RefValueWrapper {
type Target = RefValue;
fn deref(&self) -> &Self::Target {
&(**self.data)
}
}
impl Hash for RefValueWrapper {
fn hash<H: Hasher>(&self, state: &mut H) {
self.deref().hash(state)
}
}
impl Clone for RefValueWrapper {
fn clone(&self) -> Self {
RefValueWrapper {
data: self.data.clone()
}
}
}
///
/// serde_json::Value 참고
///
pub trait RefIndex {
fn index_into<'v>(&self, v: &'v RefValue) -> Option<&'v RefValueWrapper>;
fn index_into_mut<'v>(&self, v: &'v mut RefValue) -> Option<&'v mut RefValueWrapper>;
fn index_or_insert<'v>(&self, v: &'v mut RefValue) -> &'v mut RefValueWrapper;
}
impl RefIndex for usize {
fn index_into<'v>(&self, v: &'v RefValue) -> Option<&'v RefValueWrapper> {
match *v {
RefValue::Array(ref vec) => vec.get(*self),
_ => None,
}
}
fn index_into_mut<'v>(&self, v: &'v mut RefValue) -> Option<&'v mut RefValueWrapper> {
match *v {
RefValue::Array(ref mut vec) => vec.get_mut(*self),
_ => None,
}
}
fn index_or_insert<'v>(&self, v: &'v mut RefValue) -> &'v mut RefValueWrapper {
match *v {
RefValue::Array(ref mut vec) => {
let len = vec.len();
vec.get_mut(*self).unwrap_or_else(|| {
panic!(
"cannot access index {} of JSON array of length {}",
self, len
)
})
}
_ => panic!("cannot access index {} of JSON {:?}", self, v),
}
}
}
impl RefIndex for str {
fn index_into<'v>(&self, v: &'v RefValue) -> Option<&'v RefValueWrapper> {
match *v {
RefValue::Object(ref map) => map.get(self),
_ => None,
}
}
fn index_into_mut<'v>(&self, v: &'v mut RefValue) -> Option<&'v mut RefValueWrapper> {
match *v {
RefValue::Object(ref mut map) => map.get_mut(self),
_ => None,
}
}
fn index_or_insert<'v>(&self, v: &'v mut RefValue) -> &'v mut RefValueWrapper {
if let RefValue::Null = *v {
*v = RefValue::Object(IndexMap::new());
}
match *v {
RefValue::Object(ref mut map) => {
map.entry(self.to_owned()).or_insert(RefValue::Null.into())
}
_ => panic!("cannot access key {:?} in JSON {:?}", self, v),
}
}
}
impl RefIndex for String {
fn index_into<'v>(&self, v: &'v RefValue) -> Option<&'v RefValueWrapper> {
self[..].index_into(v)
}
fn index_into_mut<'v>(&self, v: &'v mut RefValue) -> Option<&'v mut RefValueWrapper> {
self[..].index_into_mut(v)
}
fn index_or_insert<'v>(&self, v: &'v mut RefValue) -> &'v mut RefValueWrapper {
self[..].index_or_insert(v)
}
}
#[derive(Debug, PartialEq)]
pub enum RefValue {
Null,
Bool(bool),
Number(Number),
String(String),
Array(Vec<RefValueWrapper>),
Object(IndexMap<String, RefValueWrapper>),
}
static REF_VALUE_NULL: &'static str = "$jsonpath::ref_value::model::RefValue::Null";
impl Hash for RefValue {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
RefValue::Null => {
REF_VALUE_NULL.hash(state)
}
RefValue::Bool(b) => {
b.hash(state)
}
RefValue::Number(n) => {
if n.is_f64() {
n.as_f64().unwrap().to_string().hash(state)
} else if n.is_i64() {
n.as_i64().unwrap().hash(state);
} else {
n.as_u64().unwrap().hash(state);
}
}
RefValue::String(s) => {
s.hash(state)
}
RefValue::Object(map) => {
for (_, v) in map {
v.hash(state);
}
}
RefValue::Array(v) => {
for i in v {
i.hash(state);
}
}
}
}
}
impl Eq for RefValue {}
impl RefValue {
pub fn get<I: RefIndex>(&self, index: I) -> Option<&RefValueWrapper> {
index.index_into(self)
}
pub fn is_object(&self) -> bool {
self.as_object().is_some()
}
pub fn as_object(&self) -> Option<&IndexMap<String, RefValueWrapper>> {
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<RefValueWrapper>> {
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 {
RefValueWrapper {
data: Arc::new(Box::new(self))
}
}
}
impl Into<RefValue> for &Value {
fn into(self) -> RefValue {
match self.serialize(super::ser::Serializer) {
Ok(v) => v,
Err(e) => panic!("Error Value into RefValue: {:?}", e)
}
}
}
impl Into<RefValueWrapper> for &Value {
fn into(self) -> RefValueWrapper {
match self.serialize(super::ser::Serializer) {
Ok(v) => v.into(),
Err(e) => panic!("Error Value into RefValue: {:?}", e)
}
}
}
impl Into<Value> for &RefValueWrapper {
fn into(self) -> Value {
match serde_json::to_value(self.deref()) {
Ok(v) => v,
Err(e) => panic!("Error RefValueWrapper into Value: {:?}", e)
}
}
}

View File

@ -1,624 +0,0 @@
use std::result::Result;
use indexmap::IndexMap;
use serde::{self, Serialize};
use serde::ser::Impossible;
use ref_value::model::{RefValue, RefValueWrapper};
use super::serde_error::SerdeError;
///
/// see `serde_json/value/ser.rs`
///
impl Serialize for RefValue {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ::serde::Serializer,
{
match *self {
RefValue::Null => serializer.serialize_unit(),
RefValue::Bool(b) => serializer.serialize_bool(b),
RefValue::Number(ref n) => n.serialize(serializer),
RefValue::String(ref s) => serializer.serialize_str(s),
RefValue::Array(ref v) => {
use std::ops::Deref;
let v: Vec<&RefValue> = v.iter().map(|v| v.deref()).collect();
v.serialize(serializer)
}
RefValue::Object(ref m) => {
use serde::ser::SerializeMap;
use std::ops::Deref;
let mut map = try!(serializer.serialize_map(Some(m.len())));
for (k, v) in m {
try!(map.serialize_key(k));
try!(map.serialize_value(v.deref()));
}
map.end()
}
}
}
}
pub struct Serializer;
impl serde::Serializer for Serializer {
type Ok = RefValue;
type Error = SerdeError;
type SerializeSeq = SerializeVec;
type SerializeTuple = SerializeVec;
type SerializeTupleStruct = SerializeVec;
type SerializeTupleVariant = SerializeTupleVariant;
type SerializeMap = SerializeMap;
type SerializeStruct = SerializeMap;
type SerializeStructVariant = SerializeStructVariant;
#[inline]
fn serialize_bool(self, value: bool) -> Result<RefValue, Self::Error> {
Ok(RefValue::Bool(value))
}
#[inline]
fn serialize_i8(self, value: i8) -> Result<RefValue, Self::Error> {
self.serialize_i64(value as i64)
}
#[inline]
fn serialize_i16(self, value: i16) -> Result<RefValue, Self::Error> {
self.serialize_i64(value as i64)
}
#[inline]
fn serialize_i32(self, value: i32) -> Result<RefValue, Self::Error> {
self.serialize_i64(value as i64)
}
fn serialize_i64(self, value: i64) -> Result<RefValue, Self::Error> {
Ok(RefValue::Number(value.into()))
}
#[inline]
fn serialize_u8(self, value: u8) -> Result<RefValue, Self::Error> {
self.serialize_u64(value as u64)
}
#[inline]
fn serialize_u16(self, value: u16) -> Result<RefValue, Self::Error> {
self.serialize_u64(value as u64)
}
#[inline]
fn serialize_u32(self, value: u32) -> Result<RefValue, Self::Error> {
self.serialize_u64(value as u64)
}
#[inline]
fn serialize_u64(self, value: u64) -> Result<RefValue, Self::Error> {
Ok(RefValue::Number(value.into()))
}
#[inline]
fn serialize_f32(self, value: f32) -> Result<RefValue, Self::Error> {
self.serialize_f64(value as f64)
}
#[inline]
fn serialize_f64(self, value: f64) -> Result<RefValue, Self::Error> {
Ok(serde_json::Number::from_f64(value).map_or(RefValue::Null, RefValue::Number))
}
#[inline]
fn serialize_char(self, value: char) -> Result<RefValue, Self::Error> {
let mut s = String::new();
s.push(value);
self.serialize_str(&s)
}
#[inline]
fn serialize_str(self, value: &str) -> Result<RefValue, Self::Error> {
Ok(RefValue::String(value.to_owned()))
}
fn serialize_bytes(self, value: &[u8]) -> Result<RefValue, Self::Error> {
let vec = value.iter().map(|&b| RefValue::Number(b.into()).into()).collect();
Ok(RefValue::Array(vec))
}
#[inline]
fn serialize_unit(self) -> Result<RefValue, Self::Error> {
Ok(RefValue::Null)
}
#[inline]
fn serialize_unit_struct(self, _name: &'static str) -> Result<RefValue, Self::Error> {
self.serialize_unit()
}
#[inline]
fn serialize_unit_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
) -> Result<RefValue, Self::Error> {
self.serialize_str(variant)
}
#[inline]
fn serialize_newtype_struct<T: ?Sized>(
self,
_name: &'static str,
value: &T,
) -> Result<RefValue, Self::Error>
where
T: Serialize,
{
value.serialize(self)
}
fn serialize_newtype_variant<T: ?Sized>(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
value: &T,
) -> Result<RefValue, Self::Error>
where
T: Serialize,
{
let mut values: IndexMap<String, RefValueWrapper> = IndexMap::new();
values.insert(String::from(variant), {
value.serialize(Serializer)?.into()
});
Ok(RefValue::Object(values))
}
#[inline]
fn serialize_none(self) -> Result<RefValue, Self::Error> {
self.serialize_unit()
}
#[inline]
fn serialize_some<T: ?Sized>(self, value: &T) -> Result<RefValue, Self::Error>
where
T: Serialize,
{
value.serialize(self)
}
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
Ok(SerializeVec {
vec: Vec::with_capacity(len.unwrap_or(0)),
})
}
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
self.serialize_seq(Some(len))
}
fn serialize_tuple_struct(
self,
_name: &'static str,
len: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
self.serialize_seq(Some(len))
}
fn serialize_tuple_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
Ok(SerializeTupleVariant {
name: String::from(variant),
vec: Vec::with_capacity(len),
})
}
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
Ok(SerializeMap::Map {
map: IndexMap::new(),
next_key: None,
})
}
fn serialize_struct(
self,
name: &'static str,
len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
match name {
_ => self.serialize_map(Some(len)),
}
}
fn serialize_struct_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
Ok(SerializeStructVariant {
name: String::from(variant),
map: IndexMap::new(),
})
}
}
pub struct SerializeVec {
vec: Vec<RefValueWrapper>,
}
pub struct SerializeTupleVariant {
name: String,
vec: Vec<RefValueWrapper>,
}
pub enum SerializeMap {
Map {
map: IndexMap<String, RefValueWrapper>,
next_key: Option<String>,
},
}
pub struct SerializeStructVariant {
name: String,
map: IndexMap<String, RefValueWrapper>,
}
impl serde::ser::SerializeSeq for SerializeVec {
type Ok = RefValue;
type Error = SerdeError;
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: Serialize,
{
self.vec.push({
value.serialize(Serializer)?.into()
});
Ok(())
}
fn end(self) -> Result<RefValue, Self::Error> {
Ok(RefValue::Array(self.vec))
}
}
impl serde::ser::SerializeTuple for SerializeVec {
type Ok = RefValue;
type Error = SerdeError;
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: Serialize,
{
serde::ser::SerializeSeq::serialize_element(self, value)
}
fn end(self) -> Result<RefValue, Self::Error> {
serde::ser::SerializeSeq::end(self)
}
}
impl serde::ser::SerializeTupleStruct for SerializeVec {
type Ok = RefValue;
type Error = SerdeError;
fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: Serialize,
{
serde::ser::SerializeSeq::serialize_element(self, value)
}
fn end(self) -> Result<RefValue, Self::Error> {
serde::ser::SerializeSeq::end(self)
}
}
impl serde::ser::SerializeTupleVariant for SerializeTupleVariant {
type Ok = RefValue;
type Error = SerdeError;
fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: Serialize,
{
self.vec.push({
let a: RefValue = value.serialize(Serializer)?;
a.into()
});
Ok(())
}
fn end(self) -> Result<RefValue, Self::Error> {
let mut object: IndexMap<String, RefValueWrapper> = IndexMap::new();
object.insert(self.name, RefValue::Array(self.vec).into());
Ok(RefValue::Object(object))
}
}
impl serde::ser::SerializeMap for SerializeMap {
type Ok = RefValue;
type Error = SerdeError;
fn serialize_key<T: ?Sized>(&mut self, key: &T) -> Result<(), Self::Error>
where
T: Serialize,
{
match *self {
SerializeMap::Map {
ref mut next_key, ..
} => {
*next_key = Some(key.serialize(MapKeySerializer)?);
Ok(())
}
}
}
fn serialize_value<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: Serialize,
{
match *self {
SerializeMap::Map {
ref mut map,
ref mut next_key,
} => {
let key = next_key.take();
// Panic because this indicates a bug in the program rather than an
// expected failure.
let key = key.expect("serialize_value called before serialize_key");
map.insert(key, {
let a: RefValue = value.serialize(Serializer)?;
a.into()
});
Ok(())
}
}
}
fn end(self) -> Result<RefValue, Self::Error> {
match self {
SerializeMap::Map { map, .. } => Ok(RefValue::Object(map)),
}
}
}
struct MapKeySerializer;
fn key_must_be_a_string() -> SerdeError {
SerdeError::from_str("key must be string")
}
impl serde::Serializer for MapKeySerializer {
type Ok = String;
type Error = SerdeError;
type SerializeSeq = Impossible<String, Self::Error>;
type SerializeTuple = Impossible<String, Self::Error>;
type SerializeTupleStruct = Impossible<String, Self::Error>;
type SerializeTupleVariant = Impossible<String, Self::Error>;
type SerializeMap = Impossible<String, Self::Error>;
type SerializeStruct = Impossible<String, Self::Error>;
type SerializeStructVariant = Impossible<String, Self::Error>;
#[inline]
fn serialize_unit_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
) -> Result<Self::Ok, Self::Error> {
Ok(variant.to_owned())
}
#[inline]
fn serialize_newtype_struct<T: ?Sized>(
self,
_name: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: Serialize,
{
value.serialize(self)
}
fn serialize_bool(self, _value: bool) -> Result<Self::Ok, Self::Error> {
Err(key_must_be_a_string())
}
fn serialize_i8(self, value: i8) -> Result<Self::Ok, Self::Error> {
Ok(value.to_string())
}
fn serialize_i16(self, value: i16) -> Result<Self::Ok, Self::Error> {
Ok(value.to_string())
}
fn serialize_i32(self, value: i32) -> Result<Self::Ok, Self::Error> {
Ok(value.to_string())
}
fn serialize_i64(self, value: i64) -> Result<Self::Ok, Self::Error> {
Ok(value.to_string())
}
fn serialize_u8(self, value: u8) -> Result<Self::Ok, Self::Error> {
Ok(value.to_string())
}
fn serialize_u16(self, value: u16) -> Result<Self::Ok, Self::Error> {
Ok(value.to_string())
}
fn serialize_u32(self, value: u32) -> Result<Self::Ok, Self::Error> {
Ok(value.to_string())
}
fn serialize_u64(self, value: u64) -> Result<Self::Ok, Self::Error> {
Ok(value.to_string())
}
fn serialize_f32(self, _value: f32) -> Result<Self::Ok, Self::Error> {
Err(key_must_be_a_string())
}
fn serialize_f64(self, _value: f64) -> Result<Self::Ok, Self::Error> {
Err(key_must_be_a_string())
}
#[inline]
fn serialize_char(self, value: char) -> Result<Self::Ok, Self::Error> {
Ok({
let mut s = String::new();
s.push(value);
s
})
}
#[inline]
fn serialize_str(self, value: &str) -> Result<Self::Ok, Self::Error> {
Ok(value.to_owned())
}
fn serialize_bytes(self, _value: &[u8]) -> Result<Self::Ok, Self::Error> {
Err(key_must_be_a_string())
}
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
Err(key_must_be_a_string())
}
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
Err(key_must_be_a_string())
}
fn serialize_newtype_variant<T: ?Sized>(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: Serialize,
{
Err(key_must_be_a_string())
}
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
Err(key_must_be_a_string())
}
fn serialize_some<T: ?Sized>(self, _value: &T) -> Result<Self::Ok, Self::Error>
where
T: Serialize,
{
Err(key_must_be_a_string())
}
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
Err(key_must_be_a_string())
}
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
Err(key_must_be_a_string())
}
fn serialize_tuple_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
Err(key_must_be_a_string())
}
fn serialize_tuple_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
Err(key_must_be_a_string())
}
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
Err(key_must_be_a_string())
}
fn serialize_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
Err(key_must_be_a_string())
}
fn serialize_struct_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
Err(key_must_be_a_string())
}
}
impl serde::ser::SerializeStruct for SerializeMap {
type Ok = RefValue;
type Error = SerdeError;
fn serialize_field<T: ?Sized>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: Serialize,
{
match *self {
SerializeMap::Map { .. } => {
serde::ser::SerializeMap::serialize_key(self, key)?;
serde::ser::SerializeMap::serialize_value(self, value)
}
}
}
fn end(self) -> Result<RefValue, Self::Error> {
match self {
SerializeMap::Map { .. } => serde::ser::SerializeMap::end(self),
}
}
}
impl serde::ser::SerializeStructVariant for SerializeStructVariant {
type Ok = RefValue;
type Error = SerdeError;
fn serialize_field<T: ?Sized>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: Serialize,
{
self.map.insert(String::from(key), {
let a: RefValue = value.serialize(Serializer)?;
a.into()
});
Ok(())
}
fn end(self) -> Result<RefValue, Self::Error> {
let mut object: IndexMap<String, RefValueWrapper> = IndexMap::new();
object.insert(self.name, RefValue::Object(self.map).into());
Ok(RefValue::Object(object))
}
}

View File

@ -1,38 +0,0 @@
use std::fmt;
#[derive(Debug)]
pub struct SerdeError {
msg: String,
}
impl<'a> SerdeError {
pub fn new(msg: String) -> Self {
SerdeError { msg: msg }
}
pub fn from_str(msg: &str) -> Self {
SerdeError { msg: msg.to_string() }
}
}
impl serde::de::Error for SerdeError {
#[cold]
fn custom<T: fmt::Display>(msg: T) -> SerdeError {
SerdeError { msg: msg.to_string() }
}
}
impl serde::ser::Error for SerdeError {
#[cold]
fn custom<T: fmt::Display>(msg: T) -> SerdeError {
SerdeError { msg: msg.to_string() }
}
}
impl std::error::Error for SerdeError {}
impl fmt::Display for SerdeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.msg)
}
}

File diff suppressed because it is too large Load Diff

56
tests/common.rs Normal file
View File

@ -0,0 +1,56 @@
extern crate env_logger;
extern crate jsonpath_lib as jsonpath;
extern crate serde_json;
use std::io::Read;
use serde_json::Value;
use self::jsonpath::Selector;
#[allow(dead_code)]
pub fn setup() {
let _ = env_logger::try_init();
}
#[allow(dead_code)]
pub fn read_json(path: &str) -> Value {
let mut f = std::fs::File::open(path).unwrap();
let mut contents = String::new();
f.read_to_string(&mut contents).unwrap();
serde_json::from_str(&contents).unwrap()
}
#[allow(dead_code)]
pub fn read_contents(path: &str) -> String {
let mut f = std::fs::File::open(path).unwrap();
let mut contents = String::new();
f.read_to_string(&mut contents).unwrap();
contents
}
#[allow(dead_code)]
pub fn select_and_then_compare<'a>(path: &str, json: Value, target: Value) {
let mut selector = Selector::new();
let result = selector
.str_path(path)
.unwrap()
.value(&json)
.select_as::<Value>()
.unwrap();
assert_eq!(
result,
match target {
Value::Array(vec) => vec.clone(),
_ => panic!("Give me the Array!"),
},
"{}",
path
);
}
#[allow(dead_code)]
pub fn compare_result<'a>(result: Vec<&Value>, target: Value) {
let result = serde_json::to_value(result).unwrap();
assert_eq!(result, target);
}

View File

@ -1,362 +1,426 @@
extern crate env_logger;
extern crate jsonpath_lib as jsonpath;
#[macro_use]
extern crate serde_json;
use std::io::Read;
use serde_json::Value;
use jsonpath::filter::value_filter::{ValueFilter, JsonValueFilter};
use jsonpath::parser::parser::Parser;
fn setup() {
let _ = env_logger::try_init();
}
use common::{read_json, select_and_then_compare, setup};
fn new_value_filter(file: &str) -> ValueFilter {
let string = read_json(file);
let json: Value = serde_json::from_str(string.as_str()).unwrap();
ValueFilter::new((&json).into(), false, false)
}
fn do_filter(path: &str, file: &str) -> JsonValueFilter {
let string = read_json(file);
let mut jf = JsonValueFilter::new(string.as_str()).unwrap();
let mut parser = Parser::new(path);
parser.parse(&mut jf).unwrap();
jf
}
fn read_json(path: &str) -> String {
let mut f = std::fs::File::open(path).unwrap();
let mut contents = String::new();
f.read_to_string(&mut contents).unwrap();
contents
}
#[test]
fn step_in() {
setup();
let mut jf = new_value_filter("./benches/data_obj.json");
{
let current = jf.step_in_str("friends");
assert_eq!(current.is_array(), true);
}
let mut jf = new_value_filter("./benches/data_array.json");
{
let current = jf.step_in_num(&1.0);
assert_eq!(current.get_val().is_object(), true);
}
{
let current = jf.step_in_str("friends");
assert_eq!(current.is_array(), true);
}
let mut jf = new_value_filter("./benches/data_obj.json");
{
jf.step_in_str("school");
jf.step_in_str("friends");
jf.step_in_all();
let current = jf.step_in_str("name");
let friends = json!([
"Millicent Norman",
"Vincent Cannon",
"Gray Berry"
]);
assert_eq!(friends, current.into_value());
}
let mut jf = new_value_filter("./benches/data_obj.json");
{
let current = jf.step_leaves_str("name");
let names = json!([
"Leonor Herman",
"Millicent Norman",
"Vincent Cannon",
"Gray Berry",
"Vincent Cannon",
"Gray Berry"
]);
assert_eq!(names, current.into_value());
}
}
mod common;
#[test]
fn array() {
setup();
let friends = json!([
select_and_then_compare(
"$.school.friends[1, 2]",
read_json("./benches/data_obj.json"),
json!([
{"id": 1, "name": "Vincent Cannon" },
{"id": 2, "name": "Gray Berry"}
]);
]),
);
let jf = do_filter("$.school.friends[1, 2]", "./benches/data_obj.json");
assert_eq!(friends, jf.into_value());
select_and_then_compare(
"$.school.friends[1: ]",
read_json("./benches/data_obj.json"),
json!([
{"id": 1, "name": "Vincent Cannon" },
{"id": 2, "name": "Gray Berry"}
]),
);
let jf = do_filter("$.school.friends[1:]", "./benches/data_obj.json");
assert_eq!(friends, jf.into_value());
let jf = do_filter("$.school.friends[:-2]", "./benches/data_obj.json");
let friends = json!([
select_and_then_compare(
"$.school.friends[:-2]",
read_json("./benches/data_obj.json"),
json!([
{"id": 0, "name": "Millicent Norman"}
]);
assert_eq!(friends, jf.into_value());
]),
);
let jf = do_filter("$..friends[2].name", "./benches/data_obj.json");
let friends = json!(["Gray Berry", "Gray Berry"]);
assert_eq!(friends, jf.into_value());
select_and_then_compare(
"$..friends[2].name",
read_json("./benches/data_obj.json"),
json!(["Gray Berry", "Gray Berry"]),
);
let jf = do_filter("$..friends[*].name", "./benches/data_obj.json");
let friends = json!(["Vincent Cannon","Gray Berry","Millicent Norman","Vincent Cannon","Gray Berry"]);
assert_eq!(friends, jf.into_value());
select_and_then_compare(
"$..friends[*].name",
read_json("./benches/data_obj.json"),
json!([
"Vincent Cannon",
"Gray Berry",
"Millicent Norman",
"Vincent Cannon",
"Gray Berry"
]),
);
let jf = do_filter("$['school']['friends'][*].['name']", "./benches/data_obj.json");
let friends = json!(["Millicent Norman","Vincent Cannon","Gray Berry"]);
assert_eq!(friends, jf.into_value());
select_and_then_compare(
"$['school']['friends'][*].['name']",
read_json("./benches/data_obj.json"),
json!(["Millicent Norman", "Vincent Cannon", "Gray Berry"]),
);
let jf = do_filter("$['school']['friends'][0].['name']", "./benches/data_obj.json");
let friends = json!("Millicent Norman");
assert_eq!(friends, jf.into_value());
select_and_then_compare(
"$['school']['friends'][0].['name']",
read_json("./benches/data_obj.json"),
json!(["Millicent Norman"]),
);
select_and_then_compare(
r#"$.["eyeColor", "name"]"#,
read_json("./benches/data_obj.json"),
json!(["blue", "Leonor Herman"]),
);
}
#[test]
fn return_type() {
setup();
let friends = json!({
select_and_then_compare(
"$.school",
read_json("./benches/data_obj.json"),
json!([{
"friends": [
{"id": 0, "name": "Millicent Norman"},
{"id": 1, "name": "Vincent Cannon" },
{"id": 2, "name": "Gray Berry"}
]
});
}]),
);
let jf = do_filter("$.school", "./benches/data_obj.json");
assert_eq!(friends, jf.into_value());
select_and_then_compare(
"$.school[?(@.friends[0])]",
read_json("./benches/data_obj.json"),
json!([{
"friends": [
{"id": 0, "name": "Millicent Norman"},
{"id": 1, "name": "Vincent Cannon" },
{"id": 2, "name": "Gray Berry"}
]
}]),
);
let jf = do_filter("$.school[?(@.friends[0])]", "./benches/data_obj.json");
assert_eq!(friends, jf.into_value());
select_and_then_compare(
"$.school[?(@.friends[10])]",
read_json("./benches/data_obj.json"),
json!([{
"friends": [
{"id": 0, "name": "Millicent Norman"},
{"id": 1, "name": "Vincent Cannon" },
{"id": 2, "name": "Gray Berry"}
]
}]),
);
let jf = do_filter("$.school[?(@.friends[10])]", "./benches/data_obj.json");
assert_eq!(Value::Null, jf.into_value());
select_and_then_compare(
"$.school[?(1==1)]",
read_json("./benches/data_obj.json"),
json!([{
"friends": [
{"id": 0, "name": "Millicent Norman"},
{"id": 1, "name": "Vincent Cannon" },
{"id": 2, "name": "Gray Berry"}
]
}]),
);
let jf = do_filter("$.school[?(1==1)]", "./benches/data_obj.json");
assert_eq!(friends, jf.into_value());
let jf = do_filter("$.school.friends[?(1==1)]", "./benches/data_obj.json");
let friends = json!([
select_and_then_compare(
"$.school.friends[?(1==1)]",
read_json("./benches/data_obj.json"),
json!([[
{"id": 0, "name": "Millicent Norman"},
{"id": 1, "name": "Vincent Cannon" },
{"id": 2, "name": "Gray Berry"}
]);
assert_eq!(friends, jf.into_value());
]]),
);
}
#[test]
fn op_default() {
setup();
let jf = do_filter("$.school[?(@.friends == @.friends)]", "./benches/data_obj.json");
let friends = json!({
"friends": [
{"id": 0, "name": "Millicent Norman"},
{"id": 1, "name": "Vincent Cannon" },
{"id": 2, "name": "Gray Berry"}
]
});
assert_eq!(friends, jf.into_value());
select_and_then_compare(
"$.school[?(@.friends == @.friends)]",
read_json("./benches/data_obj.json"),
json!([{
"friends": [
{"id": 0, "name": "Millicent Norman"},
{"id": 1, "name": "Vincent Cannon" },
{"id": 2, "name": "Gray Berry"}
]
}]),
);
let jf = do_filter("$.friends[?(@.name)]", "./benches/data_obj.json");
let friends = json!([
select_and_then_compare(
"$.friends[?(@.name)]",
read_json("./benches/data_obj.json"),
json!([
{ "id" : 1, "name" : "Vincent Cannon" },
{ "id" : 2, "name" : "Gray Berry" }
]);
assert_eq!(friends, jf.into_value());
]),
);
let jf = do_filter("$.friends[?(@.id >= 2)]", "./benches/data_obj.json");
let friends = json!([
select_and_then_compare(
"$.friends[?(@.id >= 2)]",
read_json("./benches/data_obj.json"),
json!([
{ "id" : 2, "name" : "Gray Berry" }
]);
assert_eq!(friends, jf.into_value());
]),
);
let jf = do_filter("$.friends[?(@.id >= 2 || @.id == 1)]", "./benches/data_obj.json");
let friends = json!([
select_and_then_compare(
"$.friends[?(@.id >= 2 || @.id == 1)]",
read_json("./benches/data_obj.json"),
json!([
{ "id" : 2, "name" : "Gray Berry" },
{ "id" : 1, "name" : "Vincent Cannon" }
]);
assert_eq!(friends, jf.into_value());
]),
);
let jf = do_filter("$.friends[?( (@.id >= 2 || @.id == 1) && @.id == 0)]", "./benches/data_obj.json");
assert_eq!(Value::Null, jf.into_value());
select_and_then_compare(
"$.friends[?( (@.id >= 2 || @.id == 1) && @.id == 0)]",
read_json("./benches/data_obj.json"),
json!([Value::Null]),
);
let jf = do_filter("$..friends[?(@.id == $.index)].id", "./benches/data_obj.json");
let friends = json!([0, 0]);
assert_eq!(friends, jf.into_value());
select_and_then_compare(
"$..friends[?(@.id == $.index)].id",
read_json("./benches/data_obj.json"),
json!([0, 0]),
);
let jf = do_filter("$..book[?($.store.bicycle.price < @.price)].price", "./benches/example.json");
let friends = json!([22.99]);
assert_eq!(friends, jf.into_value());
select_and_then_compare(
"$..book[?($.store.bicycle.price < @.price)].price",
read_json("./benches/example.json"),
json!([22.99]),
);
let jf = do_filter("$..book[?( (@.price == 12.99 || @.category == 'reference') && @.price > 10)].price", "./benches/example.json");
let friends = json!([12.99]);
assert_eq!(friends, jf.into_value());
select_and_then_compare(
"$..book[?( (@.price == 12.99 || @.category == 'reference') && @.price > 10)].price",
read_json("./benches/example.json"),
json!([12.99]),
);
let ref value = json!([
{ "name": "이름1", "age": 40, "phone": "+33 12341234" },
{ "name": "이름2", "age": 42, "phone": "++44 12341234" }
]);
let mut jf = JsonValueFilter::new_from_value(value.into());
let mut parser = Parser::new("$..[?(@.age > 40)]");
parser.parse(&mut jf).unwrap();
let friends = json!([
{ "name" : "이름2", "age" : 42, "phone" : "++44 12341234" }
]);
assert_eq!(friends, jf.into_value());
select_and_then_compare(
"$..[?(@.age > 40)]",
json!([
{ "name": "이름1", "age": 40, "phone": "+33 12341234" },
{ "name": "이름2", "age": 42, "phone": "++44 12341234" }
]),
json!([
{ "name" : "이름2", "age" : 42, "phone" : "++44 12341234" }
]),
);
let ref value = json!({
"school": {
select_and_then_compare(
"$..[?(@.age >= 30)]",
json!({
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]});
let mut jf = JsonValueFilter::new_from_value(value.into());
let mut parser = Parser::new("$..[?(@.age >= 30)]");
parser.parse(&mut jf).unwrap();
let friends = json!([{ "name" : "친구3", "age" : 30 }]);
assert_eq!(friends, jf.into_value());
{"name": "친구3", "age": 30},
{"name": "친구4"}
]}),
json!([
{ "name" : "친구3", "age" : 30 }
]),
);
}
#[test]
fn op_number() {
setup();
let json = json!({ "a": 1 });
let ret = jsonpath::select(&json, "$.[?(@.a == 1)]").unwrap();
assert_eq!(json, ret);
let ret = jsonpath::select(&json, "$.[?(@.a != 2)]").unwrap();
assert_eq!(json, ret);
let ret = jsonpath::select(&json, "$.[?(@.a < 2)]").unwrap();
assert_eq!(json, ret);
let ret = jsonpath::select(&json, "$.[?(@.a <= 1)]").unwrap();
assert_eq!(json, ret);
let ret = jsonpath::select(&json, "$.[?(@.a > 0)]").unwrap();
assert_eq!(json, ret);
let ret = jsonpath::select(&json, "$.[?(@.a >= 0)]").unwrap();
assert_eq!(json, ret);
select_and_then_compare("$.[?(@.a == 1)]", json!({ "a": 1 }), json!([{ "a": 1 }]));
select_and_then_compare("$.[?(@.a != 2)]", json!({ "a": 1 }), json!([{ "a": 1 }]));
select_and_then_compare("$.[?(@.a < 2)]", json!({ "a": 1 }), json!([{ "a": 1 }]));
select_and_then_compare("$.[?(@.a <= 1)]", json!({ "a": 1 }), json!([{ "a": 1 }]));
select_and_then_compare("$.[?(@.a > 0)]", json!({ "a": 1 }), json!([{ "a": 1 }]));
select_and_then_compare("$.[?(@.a >= 0)]", json!({ "a": 1 }), json!([{ "a": 1 }]));
}
#[test]
fn op_string() {
setup();
let json = json!({ "a": "b" });
let ret = jsonpath::select(&json, r#"$.[?(@.a == "b")]"#).unwrap();
assert_eq!(json!({ "a": "b" }), ret);
let ret = jsonpath::select(&json, r#"$.[?(@.a != "c")]"#).unwrap();
assert_eq!(json!({ "a": "b" }), ret);
let ret = jsonpath::select(&json, r#"$.[?(@.a < "b")]"#).unwrap();
assert_eq!(Value::Null, ret);
let ret = jsonpath::select(&json, r#"$.[?(@.a <= "b")]"#).unwrap();
assert_eq!(json!({ "a": "b" }), ret);
let ret = jsonpath::select(&json, r#"$.[?(@.a > "b")]"#).unwrap();
assert_eq!(Value::Null, ret);
let ret = jsonpath::select(&json, r#"$.[?(@.a >= "b")]"#).unwrap();
assert_eq!(json!({ "a": "b" }), ret);
select_and_then_compare(
r#"$.[?(@.a == "b")]"#,
json!({ "a": "b" }),
json!([{ "a": "b" }]),
);
select_and_then_compare(
r#"$.[?(@.a != "c")]"#,
json!({ "a": "b" }),
json!([{ "a": "b" }]),
);
select_and_then_compare(
r#"$.[?(@.a < "b")]"#,
json!({ "a": "b" }),
json!([Value::Null]),
);
select_and_then_compare(
r#"$.[?(@.a <= "b")]"#,
json!({ "a": "b" }),
json!([{ "a": "b" }]),
);
select_and_then_compare(
r#"$.[?(@.a > "b")]"#,
json!({ "a": "b" }),
json!([Value::Null]),
);
select_and_then_compare(
r#"$.[?(@.a >= "b")]"#,
json!({ "a": "b" }),
json!([{ "a": "b" }]),
);
}
#[test]
fn op_object() {
setup();
let json = json!({
"a": { "1": 1 },
"b": { "2": 2 },
"c": { "1": 1 },
});
let ret = jsonpath::select(&json, r#"$.[?(@.a == @.c)]"#).unwrap();
assert_eq!(json, ret);
let ret = jsonpath::select(&json, r#"$.[?(@.a != @.c)]"#).unwrap();
assert_eq!(Value::Null, ret);
let ret = jsonpath::select(&json, r#"$.[?(@.a < @.c)]"#).unwrap();
assert_eq!(Value::Null, ret);
let ret = jsonpath::select(&json, r#"$.[?(@.a <= @.c)]"#).unwrap();
assert_eq!(Value::Null, ret);
let ret = jsonpath::select(&json, r#"$.[?(@.a > @.c)]"#).unwrap();
assert_eq!(Value::Null, ret);
let ret = jsonpath::select(&json, r#"$.[?(@.a >= @.c)]"#).unwrap();
assert_eq!(Value::Null, ret);
select_and_then_compare(
r#"$.[?(@.a == @.c)]"#,
json!({"a": { "1": 1 }, "b": { "2": 2 }, "c": { "1": 1 }}),
json!([{"a": { "1": 1 }, "b": { "2": 2 }, "c": { "1": 1 }}]),
);
select_and_then_compare(
r#"$.[?(@.a != @.c)]"#,
json!({"a": { "1": 1 }, "b": { "2": 2 }, "c": { "1": 1 }}),
json!([Value::Null]),
);
select_and_then_compare(
r#"$.[?(@.a < @.c)]"#,
json!({"a": { "1": 1 }, "b": { "2": 2 }, "c": { "1": 1 }}),
json!([Value::Null]),
);
select_and_then_compare(
r#"$.[?(@.a <= @.c)]"#,
json!({"a": { "1": 1 }, "b": { "2": 2 }, "c": { "1": 1 }}),
json!([Value::Null]),
);
select_and_then_compare(
r#"$.[?(@.a > @.c)]"#,
json!({"a": { "1": 1 }, "b": { "2": 2 }, "c": { "1": 1 }}),
json!([Value::Null]),
);
select_and_then_compare(
r#"$.[?(@.a >= @.c)]"#,
json!({"a": { "1": 1 }, "b": { "2": 2 }, "c": { "1": 1 }}),
json!([Value::Null]),
);
}
#[test]
fn op_complex() {
setup();
let json = json!({ "a": { "b": 1 } });
let ret = jsonpath::select(&json, r#"$.[?(1 == @.a)]"#).unwrap();
assert_eq!(Value::Null, ret);
let ret = jsonpath::select(&json, r#"$.[?("1" != @.a)]"#).unwrap();
assert_eq!(Value::Null, ret);
let ret = jsonpath::select(&json, r#"$.[?(@.a <= 1)]"#).unwrap();
assert_eq!(Value::Null, ret);
let ret = jsonpath::select(&json, r#"$.[?(@.a > "1")]"#).unwrap();
assert_eq!(Value::Null, ret);
select_and_then_compare(
r#"$.[?(1 == @.a)]"#,
json!({ "a": { "b": 1 } }),
json!([Value::Null]),
);
select_and_then_compare(
r#"$.[?("1" != @.a)]"#,
json!({ "a": { "b": 1 } }),
json!([Value::Null]),
);
select_and_then_compare(
r#"$.[?(@.a <= 1)]"#,
json!({ "a": { "b": 1 } }),
json!([Value::Null]),
);
select_and_then_compare(
r#"$.[?(@.a > "1")]"#,
json!({ "a": { "b": 1 } }),
json!([Value::Null]),
);
}
#[test]
fn example() {
setup();
let jf = do_filter("$.store.book[*].author", "./benches/example.json");
let ret = json!(["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]);
assert_eq!(ret, jf.into_value());
select_and_then_compare(
r#"$.store.book[*].author"#,
read_json("./benches/example.json"),
json!([
"Nigel Rees",
"Evelyn Waugh",
"Herman Melville",
"J. R. R. Tolkien"
]),
);
let jf = do_filter("$..author", "./benches/example.json");
assert_eq!(ret, jf.into_value());
select_and_then_compare(
r#"$..author"#,
read_json("./benches/example.json"),
json!([
"Nigel Rees",
"Evelyn Waugh",
"Herman Melville",
"J. R. R. Tolkien"
]),
);
let jf = do_filter("$.store.*", "./benches/example.json");
let ret = json!([
[
{"category" : "reference", "author" : "Nigel Rees","title" : "Sayings of the Century", "price" : 8.95},
{"category" : "fiction", "author" : "Evelyn Waugh","title" : "Sword of Honour","price" : 12.99},
{"category" : "fiction", "author" : "Herman Melville","title" : "Moby Dick","isbn" : "0-553-21311-3","price" : 8.99},
{"category" : "fiction", "author" : "J. R. R. Tolkien","title" : "The Lord of the Rings","isbn" : "0-395-19395-8","price" : 22.99}
],
{"color" : "red","price" : 19.95},
]);
assert_eq!(ret, jf.into_value());
select_and_then_compare(
r#"$.store.*"#,
read_json("./benches/example.json"),
json!([
[
{"category" : "reference", "author" : "Nigel Rees","title" : "Sayings of the Century", "price" : 8.95},
{"category" : "fiction", "author" : "Evelyn Waugh","title" : "Sword of Honour","price" : 12.99},
{"category" : "fiction", "author" : "Herman Melville","title" : "Moby Dick","isbn" : "0-553-21311-3","price" : 8.99},
{"category" : "fiction", "author" : "J. R. R. Tolkien","title" : "The Lord of the Rings","isbn" : "0-395-19395-8","price" : 22.99}
],
{"color" : "red","price" : 19.95},
]),
);
let jf = do_filter("$.store..price", "./benches/example.json");
let ret = json!([8.95, 12.99, 8.99, 22.99, 19.95]);
assert_eq!(ret, jf.into_value());
select_and_then_compare(
r#"$.store..price"#,
read_json("./benches/example.json"),
json!([8.95, 12.99, 8.99, 22.99, 19.95]),
);
let jf = do_filter("$..book[2]", "./benches/example.json");
let ret = json!([{
select_and_then_compare(
r#"$..book[2]"#,
read_json("./benches/example.json"),
json!([
{
"category" : "fiction",
"author" : "Herman Melville",
"title" : "Moby Dick",
"isbn" : "0-553-21311-3",
"price" : 8.99
}]);
assert_eq!(ret, jf.into_value());
}
]),
);
let jf = do_filter("$..book[-2]", "./benches/example.json");
let ret = json!([{
"category" : "fiction",
"author" : "Herman Melville",
"title" : "Moby Dick",
"isbn" : "0-553-21311-3",
"price" : 8.99
}]);
assert_eq!(ret, jf.into_value());
select_and_then_compare(
r#"$..book[-2]"#,
read_json("./benches/example.json"),
json!([
{
"category" : "fiction",
"author" : "Herman Melville",
"title" : "Moby Dick",
"isbn" : "0-553-21311-3",
"price" : 8.99
}
]),
);
let jf = do_filter("$..book[0,1]", "./benches/example.json");
let ret = json!([
select_and_then_compare(
r#"$..book[0, 1]"#,
read_json("./benches/example.json"),
json!([
{
"category" : "reference",
"author" : "Nigel Rees",
@ -369,83 +433,173 @@ fn example() {
"title" : "Sword of Honour",
"price" : 12.99
}
]);
assert_eq!(ret, jf.into_value());
]),
);
let jf = do_filter("$..book[:2]", "./benches/example.json");
let ret = json!([
{
"category" : "reference",
"author" : "Nigel Rees",
"title" : "Sayings of the Century",
"price" : 8.95
select_and_then_compare(
r#"$..book[:2]"#,
read_json("./benches/example.json"),
json!([
{
"category" : "reference",
"author" : "Nigel Rees",
"title" : "Sayings of the Century",
"price" : 8.95
},
{
"category" : "fiction",
"author" : "Evelyn Waugh",
"title" : "Sword of Honour",
"price" : 12.99
}
]),
);
select_and_then_compare(
r#"$..book[2:]"#,
read_json("./benches/example.json"),
json!([
{
"category" : "fiction",
"author" : "Herman Melville",
"title" : "Moby Dick",
"isbn" : "0-553-21311-3",
"price" : 8.99
},
{
"category" : "fiction",
"author" : "Evelyn Waugh",
"title" : "Sword of Honour",
"price" : 12.99
"category" : "fiction",
"author" : "J. R. R. Tolkien",
"title" : "The Lord of the Rings",
"isbn" : "0-395-19395-8",
"price" : 22.99
}
]);
assert_eq!(ret, jf.into_value());
]),
);
let jf = do_filter("$..book[2:]", "./benches/example.json");
let ret = json!([
{
"category" : "fiction",
"author" : "Herman Melville",
"title" : "Moby Dick",
"isbn" : "0-553-21311-3",
"price" : 8.99
select_and_then_compare(
r#"$..book[?(@.isbn)]"#,
read_json("./benches/example.json"),
json!([
{
"category" : "fiction",
"author" : "Herman Melville",
"title" : "Moby Dick",
"isbn" : "0-553-21311-3",
"price" : 8.99
},
{
"category" : "fiction",
"author" : "J. R. R. Tolkien",
"title" : "The Lord of the Rings",
"isbn" : "0-395-19395-8",
"price" : 22.99
"category" : "fiction",
"author" : "J. R. R. Tolkien",
"title" : "The Lord of the Rings",
"isbn" : "0-395-19395-8",
"price" : 22.99
}
]);
assert_eq!(ret, jf.into_value());
]),
);
let jf = do_filter("$..book[?(@.isbn)]", "./benches/example.json");
let ret = json!([
{
"category" : "fiction",
"author" : "Herman Melville",
"title" : "Moby Dick",
"isbn" : "0-553-21311-3",
"price" : 8.99
select_and_then_compare(
r#"$.store.book[?(@.price < 10)]"#,
read_json("./benches/example.json"),
json!([
{
"category" : "reference",
"author" : "Nigel Rees",
"title" : "Sayings of the Century",
"price" : 8.95
},
{
"category" : "fiction",
"author" : "J. R. R. Tolkien",
"title" : "The Lord of the Rings",
"isbn" : "0-395-19395-8",
"price" : 22.99
"category" : "fiction",
"author" : "Herman Melville",
"title" : "Moby Dick",
"isbn" : "0-553-21311-3",
"price" : 8.99
}
]);
assert_eq!(ret, jf.into_value());
]),
);
let jf = do_filter("$.store.book[?(@.price < 10)]", "./benches/example.json");
let ret = json!([
{
"category" : "reference",
"author" : "Nigel Rees",
"title" : "Sayings of the Century",
"price" : 8.95
},
{
"category" : "fiction",
"author" : "Herman Melville",
"title" : "Moby Dick",
"isbn" : "0-553-21311-3",
"price" : 8.99
}
]);
assert_eq!(ret, jf.into_value());
select_and_then_compare(
r#"$..*"#,
read_json("./benches/example.json"),
read_json("./benches/giveme_every_thing_result.json"),
);
}
let jf = do_filter("$..*", "./benches/example.json");
let json: Value = serde_json::from_str(read_json("./benches/giveme_every_thing_result.json").as_str()).unwrap();
assert_eq!(json, jf.into_value());
}
#[test]
fn filer_same_obj() {
setup();
select_and_then_compare(
r#"$..[?(@.a == 1)]"#,
json!({
"a": 1,
"b" : {"a": 1},
"c" : {"a": 1}
}),
json!([
{"a": 1},
{"a": 1}
]),
);
}
#[test]
fn range() {
setup();
select_and_then_compare(
"$[ : ]",
json!(["first", "second"]),
json!(["first", "second"]),
);
select_and_then_compare(
"$[::]",
json!(["first", "second", "third", "forth", "fifth"]),
json!(["first", "second", "third", "forth", "fifth"]),
);
select_and_then_compare(
"$[::2]",
json!(["first", "second", "third", "forth", "fifth"]),
json!(["first", "third", "fifth"]),
);
select_and_then_compare(
"$[1::]",
json!(["first", "second", "third", "forth", "fifth"]),
json!(["second", "third", "forth", "fifth"]),
);
select_and_then_compare(
"$[1:2:]",
json!(["first", "second", "third", "forth", "fifth"]),
json!(["second"]),
);
select_and_then_compare(
"$[1::2]",
json!(["first", "second", "third", "forth", "fifth"]),
json!(["second", "forth"]),
);
select_and_then_compare(
"$[0:3:1]",
json!(["first", "second", "third", "forth", "fifth"]),
json!(["first", "second", "third"]),
);
select_and_then_compare(
"$[0:3:2]",
json!(["first", "second", "third", "forth", "fifth"]),
json!(["first", "third"]),
);
}
#[test]
fn quote() {
setup();
select_and_then_compare(
r#"$['single\'quote']"#,
json!({"single'quote":"value"}),
json!(["value"]),
);
select_and_then_compare(
r#"$["double\"quote"]"#,
json!({"double\"quote":"value"}),
json!(["value"]),
);
}

View File

@ -1,92 +1,90 @@
extern crate env_logger;
extern crate jsonpath_lib as jsonpath;
extern crate log;
extern crate serde;
#[macro_use]
extern crate serde_json;
use std::io::Read;
use serde::{Deserialize, Serialize};
use serde::Deserialize;
use serde_json::Value;
fn read_json(path: &str) -> Value {
let mut f = std::fs::File::open(path).unwrap();
let mut contents = String::new();
f.read_to_string(&mut contents).unwrap();
serde_json::from_str(contents.as_str()).unwrap()
}
use common::{compare_result, read_contents, read_json, setup};
fn read_contents(path: &str) -> String {
let mut f = std::fs::File::open(path).unwrap();
let mut contents = String::new();
f.read_to_string(&mut contents).unwrap();
contents
}
mod common;
#[test]
fn compile() {
setup();
let mut template = jsonpath::compile("$..friends[2]");
let json_obj = read_json("./benches/data_obj.json");
let json = template(&json_obj).unwrap();
let ret = json!([
{"id": 2,"name": "Gray Berry"},
{"id": 2,"name": "Gray Berry"}
]);
assert_eq!(json, ret);
{"id": 2,"name": "Gray Berry"},
{"id": 2,"name": "Gray Berry"}
]);
compare_result(json, ret);
let json_obj = read_json("./benches/data_array.json");
let json = template(&json_obj).unwrap();
let ret = json!([
{"id": 2,"name": "Gray Berry"},
{"id": 2,"name": "Rosetta Erickson"}
]);
assert_eq!(json, ret);
{"id": 2,"name": "Gray Berry"},
{"id": 2,"name": "Rosetta Erickson"}
]);
compare_result(json, ret);
}
#[test]
fn selector() {
setup();
let json_obj = read_json("./benches/data_obj.json");
let mut reader = jsonpath::selector(&json_obj);
let json = reader("$..friends[2]").unwrap();
let ret = json!([
{"id": 2,"name": "Gray Berry"},
{"id": 2,"name": "Gray Berry"}
]);
assert_eq!(json, ret);
{"id": 2,"name": "Gray Berry"},
{"id": 2,"name": "Gray Berry"}
]);
compare_result(json, ret);
let json = reader("$..friends[0]").unwrap();
let ret = json!([
{"id": 0},
{"id": 0,"name": "Millicent Norman"}
]);
assert_eq!(json, ret);
{"id": 0},
{"id": 0,"name": "Millicent Norman"}
]);
compare_result(json, ret);
}
#[test]
fn selector_as() {
let json_obj = read_json("./benches/data_obj.json");
let mut selector = jsonpath::selector_as::<Vec<Friend>>(&json_obj);
#[derive(Serialize, Deserialize, PartialEq, Debug)]
#[derive(Deserialize, PartialEq, Debug)]
struct Friend {
id: u8,
name: Option<String>,
}
let json_obj = read_json("./benches/data_obj.json");
let mut selector = jsonpath::selector_as::<Friend>(&json_obj);
let json = selector("$..friends[2]").unwrap();
let ret = vec!(
Friend { id: 2, name: Some("Gray Berry".to_string()) },
Friend { id: 2, name: Some("Gray Berry".to_string()) },
);
let ret = vec![
Friend {
id: 2,
name: Some("Gray Berry".to_string()),
},
Friend {
id: 2,
name: Some("Gray Berry".to_string()),
},
];
assert_eq!(json, ret);
let json = selector("$..friends[0]").unwrap();
let ret = vec!(
let ret = vec![
Friend { id: 0, name: None },
Friend { id: 0, name: Some("Millicent Norman".to_string()) },
);
Friend {
id: 0,
name: Some("Millicent Norman".to_string()),
},
];
assert_eq!(json, ret);
}
@ -95,13 +93,13 @@ fn select() {
let json_obj = read_json("./benches/example.json");
let json = jsonpath::select(&json_obj, "$..book[2]").unwrap();
let ret = json!([{
"category" : "fiction",
"author" : "Herman Melville",
"title" : "Moby Dick",
"isbn" : "0-553-21311-3",
"price" : 8.99
}]);
assert_eq!(json, ret);
"category" : "fiction",
"author" : "Herman Melville",
"title" : "Moby Dick",
"isbn" : "0-553-21311-3",
"price" : 8.99
}]);
compare_result(json, ret);
}
#[test]
@ -109,26 +107,27 @@ fn select_str() {
let json_str = read_contents("./benches/example.json");
let result_str = jsonpath::select_as_str(&json_str, "$..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" : "Herman Melville",
"title" : "Moby Dick",
"isbn" : "0-553-21311-3",
"price" : 8.99
}]);
let json: Value = serde_json::from_str(&result_str).unwrap();
assert_eq!(json, ret);
}
#[test]
fn test_to_struct() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
#[derive(Deserialize, PartialEq, Debug)]
struct Person {
name: String,
age: u8,
phones: Vec<String>,
}
let ret: Person = jsonpath::select_as(r#"
let ret: Vec<Person> = jsonpath::select_as(
r#"
{
"person":
{
@ -140,7 +139,10 @@ fn test_to_struct() {
]
}
}
"#, "$.person").unwrap();
"#,
"$.person",
)
.unwrap();
let person = Person {
name: "Doe John".to_string(),
@ -148,5 +150,5 @@ fn test_to_struct() {
phones: vec!["+44 1234567".to_string(), "+44 2345678".to_string()],
};
assert_eq!(person, ret);
}
assert_eq!(vec![person], ret);
}

58
tests/mutable.rs Normal file
View File

@ -0,0 +1,58 @@
extern crate jsonpath_lib as jsonpath;
#[macro_use]
extern crate serde_json;
use common::{read_json, setup};
use jsonpath::{Selector, SelectorMut};
use serde_json::Value;
mod common;
#[test]
fn selector_mut() {
setup();
let mut selector_mut = SelectorMut::new();
let mut nums = Vec::new();
let result = selector_mut
.str_path(r#"$.store..price"#)
.unwrap()
.value(read_json("./benches/example.json"))
.replace_with(&mut |v| {
match v {
Value::Number(n) => {
nums.push(n.as_f64().unwrap());
}
_ => {}
}
Value::String("a".to_string())
})
.unwrap()
.take()
.unwrap();
assert_eq!(
nums,
vec![8.95_f64, 12.99_f64, 8.99_f64, 22.99_f64, 19.95_f64]
);
let mut selector = Selector::new();
let result = selector
.str_path(r#"$.store..price"#)
.unwrap()
.value(&result)
.select()
.unwrap();
assert_eq!(
vec![
&json!("a"),
&json!("a"),
&json!("a"),
&json!("a"),
&json!("a")
],
result
);
}

View File

@ -1,312 +0,0 @@
extern crate env_logger;
extern crate jsonpath_lib as jsonpath;
use std::result;
use jsonpath::parser::parser::{Parser, ParseToken, NodeVisitor, FilterToken};
struct NodeVisitorTestImpl<'a> {
input: &'a str,
stack: Vec<ParseToken>,
}
impl<'a> NodeVisitorTestImpl<'a> {
fn new(input: &'a str) -> Self {
NodeVisitorTestImpl { input, stack: Vec::new() }
}
fn visit(&mut self) -> result::Result<Vec<ParseToken>, String> {
let mut parser = Parser::new(self.input);
parser.parse(self)?;
Ok(self.stack.split_off(0))
}
}
impl<'a> NodeVisitor for NodeVisitorTestImpl<'a> {
fn visit_token(&mut self, token: ParseToken) {
self.stack.push(token);
}
}
fn setup() {
let _ = env_logger::try_init();
}
fn run(input: &str) -> result::Result<Vec<ParseToken>, String> {
let mut interpreter = NodeVisitorTestImpl::new(input);
interpreter.visit()
}
#[test]
fn parse_path() {
setup();
assert_eq!(run("$.aa"), Ok(vec![
ParseToken::Absolute,
ParseToken::In,
ParseToken::Key("aa".to_owned())
]));
assert_eq!(run("$.00.a"), Ok(vec![
ParseToken::Absolute,
ParseToken::In,
ParseToken::Key("00".to_owned()),
ParseToken::In,
ParseToken::Key("a".to_owned())
]));
assert_eq!(run("$.00.韓창.seok"), Ok(vec![
ParseToken::Absolute,
ParseToken::In,
ParseToken::Key("00".to_owned()),
ParseToken::In,
ParseToken::Key("韓창".to_owned()),
ParseToken::In,
ParseToken::Key("seok".to_owned())
]));
assert_eq!(run("$.*"), Ok(vec![
ParseToken::Absolute,
ParseToken::In,
ParseToken::All
]));
assert_eq!(run("$..*"), Ok(vec![
ParseToken::Absolute,
ParseToken::Leaves,
ParseToken::All
]));
assert_eq!(run("$..[0]"), Ok(vec![
ParseToken::Absolute,
ParseToken::Leaves,
ParseToken::Array,
ParseToken::Number(0.0),
ParseToken::ArrayEof
]));
match run("$.") {
Ok(_) => panic!(),
_ => {}
}
match run("$..") {
Ok(_) => panic!(),
_ => {}
}
match run("$. a") {
Ok(_) => panic!(),
_ => {}
}
}
#[test]
fn parse_array_sytax() {
setup();
assert_eq!(run("$.book[?(@.isbn)]"), Ok(vec![
ParseToken::Absolute,
ParseToken::In,
ParseToken::Key("book".to_string()),
ParseToken::Array,
ParseToken::Relative,
ParseToken::In,
ParseToken::Key("isbn".to_string()),
ParseToken::ArrayEof
]));
//
// Array도 컨텍스트 In으로 간주 할거라서 중첩되면 하나만
//
assert_eq!(run("$.[*]"), Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::All,
ParseToken::ArrayEof
]));
assert_eq!(run("$.a[*]"), Ok(vec![
ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()),
ParseToken::Array,
ParseToken::All,
ParseToken::ArrayEof
]));
assert_eq!(run("$.a[*].가"), Ok(vec![
ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()),
ParseToken::Array,
ParseToken::All,
ParseToken::ArrayEof,
ParseToken::In, ParseToken::Key("".to_owned())
]));
assert_eq!(run("$.a[0][1]"), Ok(vec![
ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()),
ParseToken::Array,
ParseToken::Number(0_f64),
ParseToken::ArrayEof,
ParseToken::Array,
ParseToken::Number(1_f64),
ParseToken::ArrayEof
]));
assert_eq!(run("$.a[1,2]"), Ok(vec![
ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()),
ParseToken::Array,
ParseToken::Union(vec![1, 2]),
ParseToken::ArrayEof
]));
assert_eq!(run("$.a[10:]"), Ok(vec![
ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()),
ParseToken::Array,
ParseToken::Range(Some(10), None),
ParseToken::ArrayEof
]));
assert_eq!(run("$.a[:11]"), Ok(vec![
ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()),
ParseToken::Array,
ParseToken::Range(None, Some(11)),
ParseToken::ArrayEof
]));
assert_eq!(run("$.a[-12:13]"), Ok(vec![
ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()),
ParseToken::Array,
ParseToken::Range(Some(-12), Some(13)),
ParseToken::ArrayEof
]));
assert_eq!(run("$.a[?(1>2)]"), Ok(vec![
ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()),
ParseToken::Array,
ParseToken::Number(1_f64), ParseToken::Number(2_f64), ParseToken::Filter(FilterToken::Greater),
ParseToken::ArrayEof
]));
assert_eq!(run("$.a[?($.b>3)]"), Ok(vec![
ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()),
ParseToken::Array,
ParseToken::Absolute, ParseToken::In, ParseToken::Key("b".to_owned()), ParseToken::Number(3_f64), ParseToken::Filter(FilterToken::Greater),
ParseToken::ArrayEof
]));
assert_eq!(run("$[?($.c>@.d && 1==2)]"), Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Absolute, ParseToken::In, ParseToken::Key("c".to_owned()),
ParseToken::Relative, ParseToken::In, ParseToken::Key("d".to_owned()),
ParseToken::Filter(FilterToken::Greater),
ParseToken::Number(1_f64), ParseToken::Number(2_f64), ParseToken::Filter(FilterToken::Equal),
ParseToken::Filter(FilterToken::And),
ParseToken::ArrayEof
]));
assert_eq!(run("$[?($.c>@.d&&(1==2||3>=4))]"), Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Absolute, ParseToken::In, ParseToken::Key("c".to_owned()),
ParseToken::Relative, ParseToken::In, ParseToken::Key("d".to_owned()),
ParseToken::Filter(FilterToken::Greater),
ParseToken::Number(1_f64), ParseToken::Number(2_f64), ParseToken::Filter(FilterToken::Equal),
ParseToken::Number(3_f64), ParseToken::Number(4_f64), ParseToken::Filter(FilterToken::GreaterOrEqual),
ParseToken::Filter(FilterToken::Or),
ParseToken::Filter(FilterToken::And),
ParseToken::ArrayEof
]));
assert_eq!(run("$[?(@.a<@.b)]"), Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Relative, ParseToken::In, ParseToken::Key("a".to_owned()),
ParseToken::Relative, ParseToken::In, ParseToken::Key("b".to_owned()),
ParseToken::Filter(FilterToken::Little),
ParseToken::ArrayEof
]));
assert_eq!(run("$[*][*][*]"), Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::All,
ParseToken::ArrayEof,
ParseToken::Array,
ParseToken::All,
ParseToken::ArrayEof,
ParseToken::Array,
ParseToken::All,
ParseToken::ArrayEof
]));
assert_eq!(run("$['a']['bb']"), Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Key("a".to_string()),
ParseToken::ArrayEof,
ParseToken::Array,
ParseToken::Key("bb".to_string()),
ParseToken::ArrayEof
]));
match run("$[") {
Ok(_) => panic!(),
_ => {}
}
match run("$[]") {
Ok(_) => panic!(),
_ => {}
}
match run("$[a]") {
Ok(_) => panic!(),
_ => {}
}
match run("$[?($.a)]") {
Ok(_) => panic!(),
_ => {}
}
match run("$[?(@.a > @.b]") {
Ok(_) => panic!(),
_ => {}
}
match run("$[?(@.a < @.b&&(@.c < @.d)]") {
Ok(_) => panic!(),
_ => {}
}
}
#[test]
fn parse_array_float() {
setup();
assert_eq!(run("$[?(1.1<2.1)]"), Ok(vec![
ParseToken::Absolute,
ParseToken::Array,
ParseToken::Number(1.1), ParseToken::Number(2.1), ParseToken::Filter(FilterToken::Little),
ParseToken::ArrayEof
]));
match run("$[1.1]") {
Ok(_) => panic!(),
_ => {}
}
match run("$[?(1.1<.2)]") {
Ok(_) => panic!(),
_ => {}
}
match run("$[?(1.1<2.)]") {
Ok(_) => panic!(),
_ => {}
}
match run("$[?(1.1<2.a)]") {
Ok(_) => panic!(),
_ => {}
}
}

545
tests/readme.rs Normal file
View File

@ -0,0 +1,545 @@
extern crate jsonpath_lib as jsonpath;
extern crate serde;
#[macro_use]
extern crate serde_json;
use serde::Deserialize;
use serde_json::Value;
use jsonpath::{Selector, SelectorMut};
mod common;
#[test]
fn readme() {
let json_obj = json!({
"store": {
"book": [
{
"category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{
"category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{
"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
},
"expensive": 10
});
let mut selector = jsonpath::selector(&json_obj);
assert_eq!(
selector("$.store.book[*].author").unwrap(),
vec![
"Nigel Rees",
"Evelyn Waugh",
"Herman Melville",
"J. R. R. Tolkien"
]
);
assert_eq!(
selector("$..author").unwrap(),
vec![
"Nigel Rees",
"Evelyn Waugh",
"Herman Melville",
"J. R. R. Tolkien"
]
);
assert_eq!(
selector("$.store.*").unwrap(),
vec![
&json!([
{ "category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95 },
{ "category": "fiction", "author": "Evelyn Waugh", "title": "Sword of Honour", "price": 12.99 },
{ "category": "fiction", "author": "Herman Melville", "title": "Moby Dick", "isbn": "0-553-21311-3", "price": 8.99 },
{ "category": "fiction", "author": "J. R. R. Tolkien", "title": "The Lord of the Rings", "isbn": "0-395-19395-8", "price": 22.99 }
]),
&json!({ "color": "red", "price": 19.95 })
]
);
assert_eq!(
selector("$.store..price").unwrap(),
vec![8.95, 12.99, 8.99, 22.99, 19.95]
);
assert_eq!(
selector("$..book[2]").unwrap(),
vec![&json!({
"category" : "fiction",
"author" : "Herman Melville",
"title" : "Moby Dick",
"isbn" : "0-553-21311-3",
"price" : 8.99
})]
);
assert_eq!(
selector("$..book[-2]").unwrap(),
vec![&json!({
"category" : "fiction",
"author" : "Herman Melville",
"title" : "Moby Dick",
"isbn" : "0-553-21311-3",
"price" : 8.99
})]
);
assert_eq!(
selector("$..book[0,1]").unwrap(),
vec![
&json!({"category" : "reference","author" : "Nigel Rees","title" : "Sayings of the Century","price" : 8.95}),
&json!({"category" : "fiction","author" : "Evelyn Waugh","title" : "Sword of Honour","price" : 12.99})
]
);
assert_eq!(
selector("$..book[:2]").unwrap(),
vec![
&json!({"category" : "reference","author" : "Nigel Rees","title" : "Sayings of the Century","price" : 8.95}),
&json!({"category" : "fiction","author" : "Evelyn Waugh","title" : "Sword of Honour","price" : 12.99})
]
);
assert_eq!(
selector("$..book[:2]").unwrap(),
vec![
&json!({"category" : "reference","author" : "Nigel Rees","title" : "Sayings of the Century","price" : 8.95}),
&json!({"category" : "fiction","author" : "Evelyn Waugh","title" : "Sword of Honour","price" : 12.99})
]
);
assert_eq!(
selector("$..book[?(@.isbn)]").unwrap(),
vec![
&json!({"category" : "fiction","author" : "Herman Melville","title" : "Moby Dick","isbn" : "0-553-21311-3","price" : 8.99}),
&json!({"category" : "fiction","author" : "J. R. R. Tolkien","title" : "The Lord of the Rings","isbn" : "0-395-19395-8","price" : 22.99})
]
);
assert_eq!(
selector("$.store.book[?(@.price < 10)]").unwrap(),
vec![
&json!({"category" : "reference","author" : "Nigel Rees","title" : "Sayings of the Century","price" : 8.95}),
&json!({"category" : "fiction","author" : "Herman Melville","title" : "Moby Dick","isbn" : "0-553-21311-3","price" : 8.99})
]
);
}
#[test]
fn readme_selector() {
#[derive(Deserialize, PartialEq, Debug)]
struct Friend {
name: String,
age: Option<u8>,
}
let json_obj = json!({
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]});
let mut selector = Selector::new();
let result = selector
.str_path("$..[?(@.age >= 30)]")
.unwrap()
.value(&json_obj)
.select()
.unwrap();
assert_eq!(vec![&json!({"name": "친구3", "age": 30})], result);
let result = selector.select_as_str().unwrap();
assert_eq!(r#"[{"name":"친구3","age":30}]"#, result);
let result = selector.select_as::<Friend>().unwrap();
assert_eq!(
vec![Friend {
name: "친구3".to_string(),
age: Some(30)
}],
result
);
}
#[test]
fn readme_selector_mut() {
let json_obj = json!({
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]});
let mut selector_mut = SelectorMut::new();
let result = selector_mut
.str_path("$..[?(@.age == 20)].age")
.unwrap()
.value(json_obj)
.replace_with(&mut |v| {
let age = if let Value::Number(n) = v {
n.as_u64().unwrap() * 2
} else {
0
};
json!(age)
})
.unwrap()
.take()
.unwrap();
assert_eq!(
result,
json!({
"school": {
"friends": [
{"name": "친구1", "age": 40},
{"name": "친구2", "age": 40}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]})
);
}
#[test]
fn readme_select() {
let json_obj = json!({
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]});
let json = jsonpath::select(&json_obj, "$..friends[0]").unwrap();
assert_eq!(
json,
vec![
&json!({"name": "친구3", "age": 30}),
&json!({"name": "친구1", "age": 20})
]
);
}
#[test]
fn readme_select_as_str() {
let ret = jsonpath::select_as_str(
r#"
{
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]
}
"#,
"$..friends[0]",
)
.unwrap();
assert_eq!(
ret,
r#"[{"name":"친구3","age":30},{"name":"친구1","age":20}]"#
);
}
#[test]
fn readme_select_as() {
#[derive(Deserialize, PartialEq, Debug)]
struct Person {
name: String,
age: u8,
phones: Vec<String>,
}
let ret: Vec<Person> = jsonpath::select_as(
r#"{
"person":
{
"name": "Doe John",
"age": 44,
"phones": [
"+44 1234567",
"+44 2345678"
]
}
}"#,
"$.person",
)
.unwrap();
let person = Person {
name: "Doe John".to_string(),
age: 44,
phones: vec!["+44 1234567".to_string(), "+44 2345678".to_string()],
};
assert_eq!(ret[0], person);
}
#[test]
fn readme_compile() {
let mut first_firend = jsonpath::compile("$..friends[0]");
let json_obj = json!({
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]});
let json = first_firend(&json_obj).unwrap();
assert_eq!(
json,
vec![
&json!({"name": "친구3", "age": 30}),
&json!({"name": "친구1", "age": 20})
]
);
}
#[test]
fn readme_selector_fn() {
let json_obj = json!({
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]});
let mut selector = jsonpath::selector(&json_obj);
let json = selector("$..friends[0]").unwrap();
assert_eq!(
json,
vec![
&json!({"name": "친구3", "age": 30}),
&json!({"name": "친구1", "age": 20})
]
);
let json = selector("$..friends[1]").unwrap();
assert_eq!(
json,
vec![
&json!({"name": "친구4"}),
&json!({"name": "친구2", "age": 20})
]
);
}
#[test]
fn readme_selector_as() {
let json_obj = json!({
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]});
#[derive(Deserialize, PartialEq, Debug)]
struct Friend {
name: String,
age: Option<u8>,
}
let mut selector = jsonpath::selector_as::<Friend>(&json_obj);
let json = selector("$..friends[0]").unwrap();
let ret = vec![
Friend {
name: "친구3".to_string(),
age: Some(30),
},
Friend {
name: "친구1".to_string(),
age: Some(20),
},
];
assert_eq!(json, ret);
let json = selector("$..friends[1]").unwrap();
let ret = vec![
Friend {
name: "친구4".to_string(),
age: None,
},
Friend {
name: "친구2".to_string(),
age: Some(20),
},
];
assert_eq!(json, ret);
}
#[test]
fn readme_delete() {
let json_obj = json!({
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]});
let ret = jsonpath::delete(json_obj, "$..[?(20 == @.age)]").unwrap();
assert_eq!(
ret,
json!({
"school": {
"friends": [
null,
null
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]})
);
}
#[test]
fn readme_delete2() {
let json_obj = common::read_json("./benches/example.json");
let ret = jsonpath::delete(json_obj, "$.store.book").unwrap();
println!("{:?}", ret);
assert_eq!(
ret,
json!({
"store": {
"book": null,
"bicycle": {
"color": "red",
"price": 19.95
}
},
"expensive": 10
})
);
}
#[test]
fn readme_replace_with() {
let json_obj = json!({
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]});
let result = jsonpath::replace_with(json_obj, "$..[?(@.age == 20)].age", &mut |v| {
let age = if let Value::Number(n) = v {
n.as_u64().unwrap() * 2
} else {
0
};
json!(age)
})
.unwrap();
assert_eq!(
result,
json!({
"school": {
"friends": [
{"name": "친구1", "age": 40},
{"name": "친구2", "age": 40}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]})
);
}

View File

@ -1,92 +0,0 @@
extern crate jsonpath_lib as jsonpath;
extern crate serde;
extern crate serde_json;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use jsonpath::Selector;
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Person {
name: String,
age: u8,
phone: String,
}
fn input_str() -> &'static str {
r#"[
{
"name": "이름1",
"age": 40,
"phone": "+33 12341234"
},
{
"name": "이름2",
"age": 42,
"phone": "++44 12341234"
}
]"#
}
fn input_json() -> Value {
serde_json::from_str(input_str()).unwrap()
}
fn input_person() -> Vec<Person> {
serde_json::from_str(input_str()).unwrap()
}
#[test]
fn selector_value_from() {
let result = Selector::new()
.path("$..[?(@.age > 40)]").unwrap()
.value_from(&input_person()).unwrap()
.select_to::<Vec<Person>>().unwrap();
assert_eq!(input_person()[1], result[0]);
}
#[test]
fn selector_value() {
let result = Selector::new()
.path("$..[?(@.age > 40)]").unwrap()
.value((&input_json()).into()).unwrap()
.select_to_value().unwrap();
assert_eq!(input_json()[1], result[0]);
}
#[test]
fn selector_value_from_str() {
let result = Selector::new()
.path("$..[?(@.age > 40)]").unwrap()
.value_from_str(input_str()).unwrap()
.select_to_value().unwrap();
assert_eq!(input_json()[1], result[0]);
}
#[test]
fn selector_select_to() {
let mut selector = Selector::new();
let result = selector
.path("$..[?(@.age > 40)]").unwrap()
.value_from_str(input_str()).unwrap()
.select_to_value().unwrap();
assert_eq!(input_json()[1], result[0]);
let result = selector.select_to_str().unwrap();
assert_eq!(serde_json::to_string(&vec![&input_json()[1].clone()]).unwrap(), result);
let result = selector.select_to::<Vec<Person>>().unwrap();
assert_eq!(input_person()[1], result[0]);
let _ = selector.path("$..[?(@.age == 40)]");
let result = selector.select_to_value().unwrap();
assert_eq!(input_json()[0], result[0]);
let result = selector.select_to_str().unwrap();
assert_eq!(serde_json::to_string(&vec![&input_json()[0].clone()]).unwrap(), result);
let result = selector.select_to::<Vec<Person>>().unwrap();
assert_eq!(input_person()[0], result[0]);
}

View File

@ -1,39 +0,0 @@
extern crate jsonpath_lib as jsonpath;
extern crate serde;
extern crate serde_json;
use std::io::Read;
use serde_json::Value;
use jsonpath::ref_value::model::{RefValue, RefValueWrapper};
fn read_json(path: &str) -> String {
let mut f = std::fs::File::open(path).unwrap();
let mut contents = String::new();
f.read_to_string(&mut contents).unwrap();
contents
}
#[test]
fn de() {
let json_str = read_json("./benches/example.json");
// RefValue -> Value
let ref_value: RefValue = serde_json::from_str(json_str.as_str()).unwrap();
let ref value_wrapper: RefValueWrapper = ref_value.into();
let value: Value = value_wrapper.into();
// Value
let json: Value = serde_json::from_str(json_str.as_str()).unwrap();
assert_eq!(value, json);
}
#[test]
fn ser() {
let json_str = read_json("./benches/example.json");
let ref_value: RefValue = serde_json::from_str(json_str.as_str()).unwrap();
let ref_value_str = serde_json::to_string(&ref_value).unwrap();
let json: Value = serde_json::from_str(json_str.as_str()).unwrap();
let json_str = serde_json::to_string(&json).unwrap();
assert_eq!(ref_value_str, json_str);
}

View File

@ -1,194 +0,0 @@
extern crate jsonpath_lib as jsonpath;
use jsonpath::parser::tokenizer::{Tokenizer, Token, TokenError, PreloadedTokenizer};
fn collect_token(input: &str) -> (Vec<Token>, Option<TokenError>) {
let mut tokenizer = Tokenizer::new(input);
let mut vec = vec![];
loop {
match tokenizer.next_token() {
Ok(t) => vec.push(t),
Err(e) => return (vec, Some(e)),
}
}
}
fn run(input: &str, expected: (Vec<Token>, Option<TokenError>)) {
let (vec, err) = collect_token(input.clone());
assert_eq!((vec, err), expected, "\"{}\"", input);
}
#[test]
fn peek() {
let mut tokenizer = PreloadedTokenizer::new("$.a");
match tokenizer.next_token() {
Ok(t) => assert_eq!(Token::Absolute(0), t),
_ => panic!()
}
match tokenizer.peek_token() {
Ok(t) => assert_eq!(&Token::Dot(1), t),
_ => panic!()
}
match tokenizer.peek_token() {
Ok(t) => assert_eq!(&Token::Dot(1), t),
_ => panic!()
}
match tokenizer.next_token() {
Ok(t) => assert_eq!(Token::Dot(1), t),
_ => panic!()
}
}
#[test]
fn token() {
run("$.01.a",
(
vec![
Token::Absolute(0),
Token::Dot(1),
Token::Key(2, "01".to_string()),
Token::Dot(4),
Token::Key(5, "a".to_string())
]
, Some(TokenError::Eof)
));
run("$. []",
(
vec![
Token::Absolute(0),
Token::Dot(1),
Token::Whitespace(2, 2),
Token::OpenArray(5),
Token::CloseArray(6)
]
, Some(TokenError::Eof)
));
run("$..",
(
vec![
Token::Absolute(0),
Token::Dot(1),
Token::Dot(2),
]
, Some(TokenError::Eof)
));
run("$..ab",
(
vec![
Token::Absolute(0),
Token::Dot(1),
Token::Dot(2),
Token::Key(3, "ab".to_string())
]
, Some(TokenError::Eof)
));
run("$..가 [",
(
vec![
Token::Absolute(0),
Token::Dot(1),
Token::Dot(2),
Token::Key(3, "".to_string()),
Token::Whitespace(6, 0),
Token::OpenArray(7),
]
, Some(TokenError::Eof)
));
run("[-1, 2 ]",
(
vec![
Token::OpenArray(0),
Token::Key(1, "-1".to_string()),
Token::Comma(3),
Token::Whitespace(4, 0),
Token::Key(5, "2".to_string()),
Token::Whitespace(6, 0),
Token::CloseArray(7),
]
, Some(TokenError::Eof)
));
run("[ 1 2 , 3 \"abc\" : -10 ]",
(
vec![
Token::OpenArray(0),
Token::Whitespace(1, 0),
Token::Key(2, "1".to_string()),
Token::Whitespace(3, 0),
Token::Key(4, "2".to_string()),
Token::Whitespace(5, 0),
Token::Comma(6),
Token::Whitespace(7, 0),
Token::Key(8, "3".to_string()),
Token::Whitespace(9, 0),
Token::DoubleQuoted(10, "abc".to_string()),
Token::Whitespace(15, 0),
Token::Split(16),
Token::Whitespace(17, 0),
Token::Key(18, "-10".to_string()),
Token::Whitespace(21, 0),
Token::CloseArray(22),
]
, Some(TokenError::Eof)
));
run("?(@.a가 <41.01)",
(
vec![
Token::Question(0),
Token::OpenParenthesis(1),
Token::At(2),
Token::Dot(3),
Token::Key(4, "a가".to_string()),
Token::Whitespace(8, 0),
Token::Little(9),
Token::Key(10, "41".to_string()),
Token::Dot(12),
Token::Key(13, "01".to_string()),
Token::CloseParenthesis(15),
]
, Some(TokenError::Eof)
));
run("?(@.a <4a.01)",
(
vec![
Token::Question(0),
Token::OpenParenthesis(1),
Token::At(2),
Token::Dot(3),
Token::Key(4, "a".to_string()),
Token::Whitespace(5, 0),
Token::Little(6),
Token::Key(7, "4a".to_string()),
Token::Dot(9),
Token::Key(10, "01".to_string()),
Token::CloseParenthesis(12),
]
, Some(TokenError::Eof)
));
run("?($.c>@.d)", (
vec![
Token::Question(0),
Token::OpenParenthesis(1),
Token::Absolute(2),
Token::Dot(3),
Token::Key(4, "c".to_string()),
Token::Greater(5),
Token::At(6),
Token::Dot(7),
Token::Key(8, "d".to_string()),
Token::CloseParenthesis(9)
]
, Some(TokenError::Eof)
));
}

7
wasm/.gitignore vendored
View File

@ -2,7 +2,10 @@ target
**/*.rs.bk
Cargo.lock
bin/
pkg/
browser_pkg
nodejs_pkg
all_pkg
wasm-pack.log
.idea/*
.vscode
.vscode
tests/node_modules

View File

@ -1,9 +1,9 @@
[package]
name = "jsonpath_wasm"
version = "0.1.7"
name = "jsonpath-wasm"
version = "0.2.2"
authors = ["Changseok Han <freestrings@gmail.com>"]
description = "JsonPath Webassembly version compiled by Rust - Demo: https://freestrings.github.io/jsonpath"
keywords = ["library", "jsonpath", "json", "webassembly"]
description = "It is Webassembly version of jsonpath_lib that is JsonPath engine written in Rust - Demo: https://freestrings.github.io/jsonpath"
keywords = ["jsonpath", "json", "webassembly", "parsing", "rust"]
repository = "https://github.com/freestrings/jsonpath"
license = "MIT"
@ -20,15 +20,16 @@ console_error_panic_hook = { version = "0.1.1", optional = true }
wee_alloc = { version = "0.4.2", optional = true }
jsonpath_lib = { path = "../" }
serde = "1.0"
serde_json = { version = "1.0", features = ["preserve_order"] }
lazy_static = "1.3.0"
web-sys = { version = "0.3", features = ['console'] }
js-sys = "0.3"
[dev-dependencies]
wasm-bindgen-test = "0.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
js-sys = "0.3"
[profile.release]
opt-level = "s"

331
wasm/README.md Normal file
View File

@ -0,0 +1,331 @@
# jsonpath-wasm
[![Build Status](https://travis-ci.org/freestrings/jsonpath.svg?branch=master)](https://travis-ci.org/freestrings/jsonpath)
It is Webassembly version of [jsonpath_lib](https://github.com/freestrings/jsonpath) that is [JsonPath](https://goessner.net/articles/JsonPath/) engine written in Rust.
## APIs
<details><summary><b>npm package</b></summary>
```javascript
// browser
import * as jsonpath from "jsonpath-wasm";
// NodeJs
const jsonpath = require('jsonpath-wasm');
```
</details>
<details><summary><b>Javascript - jsonpath.Selector class</b></summary>
`wasm-bindgen` 리턴 타입 제약 때문에 빌더 패턴은 지원하지 않는다.
It does not support `builder-pattern` due to the `return type` restriction of `wasm-bindgen`.
```javascript
let jsonObj = {
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]
};
let ret = [
{"name": "친구3", "age": 30},
{"name": "친구1", "age": 20}
];
let selector = new jsonpath.Selector();
selector.path('$..friends[0]');
selector.value(jsonObj);
let retObj = selector.select();
console.log(JSON.stringify(ret) == JSON.stringify(retObj));
// => true
```
</details>
<details><summary><b>Javascript - jsonpath.SelectorMut class</b></summary>
빌더 패턴 제약은 `Selector class`와 동일하다.
```javascript
let jsonObj = {
'school': {
'friends': [
{'name': '친구1', 'age': 20},
{'name': '친구2', 'age': 20},
],
},
'friends': [
{'name': '친구3', 'age': 30},
{'name': '친구4'},
],
};
let selector = new jsonpath.SelectorMut();
selector.path('$..[?(@.age == 20)]');
{
selector.value(jsonObj);
selector.deleteValue();
let resultObj = {
'school': {'friends': [null, null]},
'friends': [
{'name': '친구3', 'age': 30},
{'name': '친구4'},
],
};
console.log(JSON.stringify(selector.take()) !== JSON.stringify(resultObj));
// => true
}
{
selector.value(jsonObj);
selector.replaceWith((v) => {
v.age = v.age * 2;
return v;
});
let resultObj = {
'school': {
'friends': [
{'name': '친구1', 'age': 40},
{'name': '친구2', 'age': 40},
],
},
'friends': [
{'name': '친구3', 'age': 30},
{'name': '친구4'},
],
};
console.log(JSON.stringify(selector.take()) !== JSON.stringify(resultObj));
// => true
}
```
</details>
<details><summary><b>Javascript - jsonpath.select(json: string|object, jsonpath: string)</b></summary>
```javascript
let jsonObj = {
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]
};
let ret = [
{"name": "친구3", "age": 30},
{"name": "친구1", "age": 20}
];
let selectAsString = jsonpath.select(JSON.stringify(jsonObj), '$..friends[0]');
let selectAsObj = jsonpath.select(jsonObj, '$..friends[0]');
console.log(
JSON.stringify(ret) == JSON.stringify(selectAsString),
JSON.stringify(ret) == JSON.stringify(selectAsObj)
);
// => true, true
```
</details>
<details><summary><b>Javascript - jsonpath.compile(jsonpath: string)</b></summary>
```javascript
let template = jsonpath.compile('$..friends[0]');
let jsonObj = {
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]
};
let ret = [
{"name": "친구3", "age": 30},
{"name": "친구1", "age": 20}
];
let selectAsString = template(JSON.stringify(jsonObj));
let selectAsObj = template(jsonObj);
console.log(
JSON.stringify(ret) == JSON.stringify(selectAsString),
JSON.stringify(ret) == JSON.stringify(selectAsObj)
);
// => true, true
let jsonObj2 = {
"school": {
"friends": [
{"name": "Millicent Norman"},
{"name": "Vincent Cannon"}
]
},
"friends": [ {"age": 30}, {"age": 40} ]
};
let ret2 = [
{"age": 30},
{"name": "Millicent Norman"}
];
let selectAsString2 = template(JSON.stringify(jsonObj2));
let selectAsObj2 = template(jsonObj2);
console.log(
JSON.stringify(ret2) == JSON.stringify(selectAsString2),
JSON.stringify(ret2) == JSON.stringify(selectAsObj2)
);
// => true, true
```
</details>
<details><summary><b>Javascript - jsonpath.selector(json: string|object)</b></summary>
```javascript
let jsonObj = {
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]
};
let ret1 = [
{"name": "친구3", "age": 30},
{"name": "친구1", "age": 20}
];
let ret2 = [
{"name": "친구4"},
{"name": "친구2", "age": 20}
];
let selector = jsonpath.selector(jsonObj);
// or as json string
// let selector = jsonpath.selector(JSON.stringify(jsonObj));
let select1 = selector('$..friends[0]');
let select2 = selector('$..friends[1]');
console.log(
JSON.stringify(ret1) == JSON.stringify(select1),
JSON.stringify(ret2) == JSON.stringify(select2)
);
// => true, true
```
</details>
<details><summary><b>Javascript - jsonpath.deleteValue(json: string|object, path: string)</b></summary>
```javascript
let jsonObj = {
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]
};
let _1 = jsonpath.deleteValue(jsonObj, '$..friends[0]');
let result = jsonpath.deleteValue(_1, '$..friends[1]');
console.log(JSON.stringify(result) !== JSON.stringify({
"school": { "friends": [null, null]},
"friends": [null, null]
}));
// => true
```
</details>
<details><summary><b>Javascript - jsonpath.replaceWith(json: string|object, path: string, fun: function(json: object) => json: object</b></summary>
```javascript
let jsonObj = {
"school": {
"friends": [
{"name": "친구1", "age": 20},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 30},
{"name": "친구4"}
]
};
let result = jsonpath.replaceWith(jsonObj, '$..friends[0]', (v) => {
v.age = v.age * 2;
return v;
});
console.log(JSON.stringify(result) === JSON.stringify({
"school": {
"friends": [
{"name": "친구1", "age": 40},
{"name": "친구2", "age": 20}
]
},
"friends": [
{"name": "친구3", "age": 60},
{"name": "친구4"}
]
}));
// => true
```
</details>
[Javascript - Other Examples](https://github.com/freestrings/jsonpath/wiki/Javascript-examples)

View File

@ -1,25 +1,15 @@
extern crate cfg_if;
extern crate js_sys;
extern crate jsonpath_lib as jsonpath;
#[macro_use]
extern crate lazy_static;
extern crate serde_json;
extern crate wasm_bindgen;
extern crate web_sys;
use std::collections::HashMap;
use std::ops::Deref;
use std::result::Result;
use std::sync::Mutex;
use cfg_if::cfg_if;
use jsonpath::filter::value_filter::JsonValueFilter;
use jsonpath::parser::parser::{Node, NodeVisitor, Parser};
use jsonpath::ref_value::model::{RefValue, RefValueWrapper};
use jsonpath::Selector as _Selector;
use jsonpath::SelectorMut as _SelectorMut;
use jsonpath::{JsonPathError, Parser};
use serde_json::Value;
use wasm_bindgen::prelude::*;
use web_sys::console;
use std::result;
cfg_if! {
if #[cfg(feature = "wee_alloc")] {
@ -39,89 +29,78 @@ cfg_if! {
}
}
fn filter_ref_value(json: RefValueWrapper, node: Node) -> JsValue {
let mut jf = JsonValueFilter::new_from_value(json);
jf.visit(node);
let taken = &jf.take_value();
match JsValue::from_serde(taken.deref()) {
Ok(js_value) => js_value,
Err(e) => JsValue::from_str(&format!("Json deserialize error: {:?}", e))
}
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn error(s: &str);
}
fn into_serde_json(js_value: &JsValue) -> Result<RefValue, String> {
macro_rules! console_error {
($($t:tt)*) => (error(&format_args!($($t)*).to_string()))
}
fn into_serde_json<D>(js_value: &JsValue) -> Result<D, String>
where
D: for<'a> serde::de::Deserialize<'a>,
{
if js_value.is_string() {
match serde_json::from_str(js_value.as_string().unwrap().as_str()) {
Ok(json) => Ok(json),
Err(e) => Err(format!("{:?}", e))
Err(e) => Err(e.to_string()),
}
} else {
match js_value.into_serde() {
Ok(json) => Ok(json),
Err(e) => Err(format!("{:?}", e))
Err(e) => Err(e.to_string()),
}
}
}
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")
fn replace_fun(v: &Value, fun: &js_sys::Function) -> Value {
match JsValue::from_serde(v) {
Ok(js_v) => match fun.call1(&JsValue::NULL, &js_v) {
Ok(result) => match into_serde_json(&result) {
Ok(json) => json,
Err(e) => {
console_error!("replace_with - closure returned a invalid JSON: {:?}", e);
Value::Null
}
},
Err(e) => {
console_error!("replace_with - fail to call closure: {:?}", e);
Value::Null
}
}
_ => into_ref_value(&js_value, node)
}
}
lazy_static! {
static ref CACHE_JSON: Mutex<HashMap<usize, RefValueWrapper>> = Mutex::new(HashMap::new());
static ref CACHE_JSON_IDX: Mutex<usize> = Mutex::new(0);
}
#[wasm_bindgen(js_name = allocJson)]
pub extern 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
console_error!("replace_with - invalid JSON object: {:?}", e);
Value::Null
}
}
}
#[wasm_bindgen(js_name = deallocJson)]
pub extern fn dealloc_json(ptr: usize) -> bool {
let mut map = CACHE_JSON.lock().unwrap();
map.remove(&ptr).is_some()
}
#[wasm_bindgen]
pub fn compile(path: &str) -> JsValue {
let mut parser = Parser::new(path);
let node = parser.compile();
let node = Parser::compile(path);
let cb = Closure::wrap(Box::new(move |js_value: JsValue| {
let json = match into_serde_json(&js_value) {
Ok(json) => json,
Err(e) => return JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e))),
};
let mut selector = _Selector::new();
match &node {
Ok(node) => get_ref_value(js_value, node.clone()),
Err(e) => JsValue::from_str(&format!("Json path error: {:?}", e))
Ok(node) => selector.compiled_path(node),
Err(e) => return JsValue::from_str(&format!("{:?}", JsonPathError::Path(e.clone()))),
};
match selector.value(&json).select() {
Ok(ret) => match JsValue::from_serde(&ret) {
Ok(ret) => ret,
Err(e) => JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e.to_string()))),
},
Err(e) => JsValue::from_str(&format!("{:?}", e)),
}
}) as Box<Fn(JsValue) -> JsValue>);
@ -132,28 +111,29 @@ pub fn compile(path: &str) -> JsValue {
#[wasm_bindgen]
pub fn selector(js_value: JsValue) -> JsValue {
let json = match js_value.as_f64() {
Some(val) => {
match CACHE_JSON.lock().unwrap().get(&(val as usize)) {
Some(json) => json.clone(),
_ => return JsValue::from_str("Invalid pointer")
}
}
_ => {
match into_serde_json(&js_value) {
Ok(json) => json.into(),
Err(e) => return JsValue::from_str(e.as_str())
}
}
let json: Value = match JsValue::into_serde(&js_value) {
Ok(json) => json,
Err(e) => return JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e.to_string()))),
};
let cb = Closure::wrap(Box::new(move |path: String| {
let mut parser = Parser::new(path.as_str());
match parser.compile() {
Ok(node) => filter_ref_value(json.clone(), node),
Err(e) => return JsValue::from_str(e.as_str())
}
}) as Box<Fn(String) -> JsValue>);
let cb = Closure::wrap(
Box::new(move |path: String| match Parser::compile(path.as_str()) {
Ok(node) => {
let mut selector = _Selector::new();
let _ = selector.compiled_path(&node);
match selector.value(&json).select() {
Ok(ret) => match JsValue::from_serde(&ret) {
Ok(ret) => ret,
Err(e) => {
JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e.to_string())))
}
},
Err(e) => JsValue::from_str(&format!("{:?}", e)),
}
}
Err(e) => return JsValue::from_str(&format!("{:?}", JsonPathError::Path(e))),
}) as Box<Fn(String) -> JsValue>,
);
let ret = cb.as_ref().clone();
cb.forget();
@ -162,10 +142,120 @@ pub fn selector(js_value: JsValue) -> JsValue {
#[wasm_bindgen]
pub fn select(js_value: JsValue, path: &str) -> JsValue {
let mut parser = Parser::new(path);
match parser.compile() {
Ok(node) => get_ref_value(js_value, node),
Err(e) => return JsValue::from_str(e.as_str())
let json = match into_serde_json(&js_value) {
Ok(json) => json,
Err(e) => return JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e))),
};
match jsonpath::select(&json, path) {
Ok(ret) => match JsValue::from_serde(&ret) {
Ok(ret) => ret,
Err(e) => JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e.to_string()))),
},
Err(e) => JsValue::from_str(&format!("{:?}", e)),
}
}
#[wasm_bindgen(catch, js_name = "deleteValue")]
pub fn delete(js_value: JsValue, path: &str) -> JsValue {
let json = match into_serde_json(&js_value) {
Ok(json) => json,
Err(e) => return JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e))),
};
match jsonpath::delete(json, path) {
Ok(ret) => match JsValue::from_serde(&ret) {
Ok(ret) => ret,
Err(e) => JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e.to_string()))),
},
Err(e) => JsValue::from_str(&format!("{:?}", e)),
}
}
#[wasm_bindgen(catch, js_name = "replaceWith")]
pub fn replace_with(js_value: JsValue, path: &str, fun: js_sys::Function) -> JsValue {
let json = match into_serde_json(&js_value) {
Ok(json) => json,
Err(e) => return JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e))),
};
match jsonpath::replace_with(json, path, &mut |v| replace_fun(v, &fun)) {
Ok(ret) => match JsValue::from_serde(&ret) {
Ok(ret) => ret,
Err(e) => JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e.to_string()))),
},
Err(e) => JsValue::from_str(&format!("{:?}", e)),
}
}
///
/// `wasm_bindgen` 제약으로 builder-pattern을 구사 할 수 없다.
/// lifetime 제약으로 Selector를 사용 할 수 없다.
///
#[wasm_bindgen]
pub struct Selector {
path: Option<String>,
value: Option<Value>,
}
#[wasm_bindgen]
impl Selector {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Selector {
path: None,
value: None,
}
}
#[wasm_bindgen(catch)]
pub fn path(&mut self, path: &str) -> Result<(), JsValue> {
self.path = Some(path.to_string());
Ok(())
}
#[wasm_bindgen(catch)]
pub fn value(&mut self, value: JsValue) -> Result<(), JsValue> {
let json = into_serde_json(&value)
.map_err(|e| JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e))))?;
self.value = Some(json);
Ok(())
}
#[wasm_bindgen(catch, js_name = select)]
pub fn select(&mut self) -> Result<JsValue, JsValue> {
let mut selector = _Selector::new();
if let Some(path) = &self.path {
let _ = selector
.str_path(&path)
.map_err(|e| JsValue::from_str(&format!("{:?}", e)))?;
} else {
return Err(JsValue::from_str(&format!(
"{:?}",
JsonPathError::EmptyPath
)));
}
if let Some(value) = &self.value {
let _ = selector.value(value);
} else {
return Err(JsValue::from_str(&format!(
"{:?}",
JsonPathError::EmptyValue
)));
}
match selector.select() {
Ok(ret) => match JsValue::from_serde(&ret) {
Ok(ret) => Ok(ret),
Err(e) => Err(JsValue::from_str(&format!(
"{:?}",
JsonPathError::Serde(e.to_string())
))),
},
Err(e) => Err(JsValue::from_str(&format!("{:?}", e))),
}
}
}
@ -173,45 +263,108 @@ pub fn select(js_value: JsValue, path: &str) -> JsValue {
/// `wasm_bindgen` 제약으로 builder-pattern을 구사 할 수 없다.
///
#[wasm_bindgen]
pub struct Selector {
selector: _Selector
pub struct SelectorMut {
path: Option<String>,
value: Option<Value>,
}
#[wasm_bindgen]
impl Selector {
impl SelectorMut {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Selector { selector: _Selector::new() }
SelectorMut {
path: None,
value: None,
}
}
#[wasm_bindgen(catch)]
pub fn path(&mut self, path: &str) -> result::Result<(), JsValue> {
let _ = self.selector.path(path)?;
pub fn path(&mut self, path: &str) -> Result<(), JsValue> {
self.path = Some(path.to_string());
Ok(())
}
#[wasm_bindgen(catch)]
pub fn value(&mut self, value: JsValue) -> result::Result<(), JsValue> {
let ref_value = into_serde_json(&value)?;
let _ = self.selector.value(ref_value)?;
pub fn value(&mut self, value: JsValue) -> Result<(), JsValue> {
let json = into_serde_json(&value)
.map_err(|e| JsValue::from_str(&format!("{:?}", JsonPathError::Serde(e))))?;
self.value = Some(json);
Ok(())
}
#[wasm_bindgen(catch, js_name = selectToStr)]
pub fn select_to_str(&mut self) -> result::Result<JsValue, JsValue> {
let json_str = self.selector.select_to_str()?;
Ok(JsValue::from_str(&json_str))
#[wasm_bindgen(catch, js_name = "deleteValue")]
pub fn delete(&mut self) -> Result<(), JsValue> {
let mut selector = _SelectorMut::new();
if let Some(path) = &self.path {
let _ = selector.str_path(path);
} else {
return Err(JsValue::from_str(&format!(
"{:?}",
JsonPathError::EmptyPath
)));
};
if let Some(value) = self.value.take() {
selector.value(value);
} else {
return Err(JsValue::from_str(&format!(
"{:?}",
JsonPathError::EmptyValue
)));
};
match selector.delete() {
Err(e) => Err(JsValue::from_str(&format!("{:?}", e))),
_ => {
self.value = selector.take();
Ok(())
}
}
}
#[wasm_bindgen(catch, js_name = selectTo)]
pub fn select_to(&mut self) -> result::Result<JsValue, JsValue> {
let ref_value = self.selector.select_to::<RefValue>()
.map_err(|e| JsValue::from_str(&e))?;
Ok(JsValue::from_serde(&ref_value)
.map_err(|e| JsValue::from_str(&format!("{:?}", e)))?)
#[wasm_bindgen(catch, js_name = replaceWith)]
pub fn replace_with(&mut self, fun: js_sys::Function) -> Result<(), JsValue> {
let mut selector = _SelectorMut::new();
if let Some(path) = &self.path {
let _ = selector.str_path(path);
} else {
return Err(JsValue::from_str(&format!(
"{:?}",
JsonPathError::EmptyPath
)));
};
if let Some(value) = self.value.take() {
selector.value(value);
} else {
return Err(JsValue::from_str(&format!(
"{:?}",
JsonPathError::EmptyValue
)));
};
match selector.replace_with(&mut |v| replace_fun(v, &fun)) {
Err(e) => Err(JsValue::from_str(&format!("{:?}", e))),
_ => {
self.value = selector.take();
Ok(())
}
}
}
#[wasm_bindgen(catch)]
pub fn take(&mut self) -> Result<JsValue, JsValue> {
match self.value.take() {
Some(ret) => match JsValue::from_serde(&ret) {
Ok(ret) => Ok(ret),
Err(e) => Err(JsValue::from_str(&format!("{:?}", e))),
},
None => Err(JsValue::from_str(&format!(
"{:?}",
JsonPathError::EmptyValue
))),
}
}
}
#[wasm_bindgen]
pub fn testa() {}

976
wasm/tests/package-lock.json generated Normal file
View File

@ -0,0 +1,976 @@
{
"name": "jsonpath-wasm-test",
"version": "0.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"ansi-colors": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
"integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==",
"dev": true
},
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"dev": true
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
}
},
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"dev": true,
"requires": {
"sprintf-js": "~1.0.2"
}
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"browser-stdout": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
"integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
"dev": true
},
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"dev": true
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
},
"dependencies": {
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
}
}
},
"cliui": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
"integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
"dev": true,
"requires": {
"string-width": "^2.1.1",
"strip-ansi": "^4.0.0",
"wrap-ansi": "^2.0.0"
}
},
"code-point-at": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"dev": true
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
"cross-spawn": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
"integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
"dev": true,
"requires": {
"nice-try": "^1.0.4",
"path-key": "^2.0.1",
"semver": "^5.5.0",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
}
},
"debug": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
},
"decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
"dev": true
},
"define-properties": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
"integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
"dev": true,
"requires": {
"object-keys": "^1.0.12"
}
},
"diff": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
"dev": true
},
"emoji-regex": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
"dev": true
},
"end-of-stream": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
"integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
"dev": true,
"requires": {
"once": "^1.4.0"
}
},
"es-abstract": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz",
"integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==",
"dev": true,
"requires": {
"es-to-primitive": "^1.2.0",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"is-callable": "^1.1.4",
"is-regex": "^1.0.4",
"object-keys": "^1.0.12"
}
},
"es-to-primitive": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
"integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
"dev": true,
"requires": {
"is-callable": "^1.1.4",
"is-date-object": "^1.0.1",
"is-symbol": "^1.0.2"
}
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true
},
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"dev": true
},
"execa": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
"integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
"dev": true,
"requires": {
"cross-spawn": "^6.0.0",
"get-stream": "^4.0.0",
"is-stream": "^1.1.0",
"npm-run-path": "^2.0.0",
"p-finally": "^1.0.0",
"signal-exit": "^3.0.0",
"strip-eof": "^1.0.0"
}
},
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"dev": true,
"requires": {
"locate-path": "^3.0.0"
}
},
"flat": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz",
"integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==",
"dev": true,
"requires": {
"is-buffer": "~2.0.3"
}
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true
},
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true
},
"get-stream": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
"integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
"dev": true,
"requires": {
"pump": "^3.0.0"
}
},
"glob": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"growl": {
"version": "1.10.5",
"resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
"integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
"dev": true
},
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"requires": {
"function-bind": "^1.1.1"
}
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"has-symbols": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
"integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
"dev": true
},
"he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"dev": true
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dev": true,
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true
},
"invert-kv": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
"integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
"dev": true
},
"is-buffer": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz",
"integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==",
"dev": true
},
"is-callable": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
"integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==",
"dev": true
},
"is-date-object": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
"integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
"dev": true
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
},
"is-regex": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
"integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
"dev": true,
"requires": {
"has": "^1.0.1"
}
},
"is-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
"dev": true
},
"is-symbol": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
"integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
"dev": true,
"requires": {
"has-symbols": "^1.0.0"
}
},
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
"dev": true
},
"js-yaml": {
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
"dev": true,
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
},
"jsonpath-wasm": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/jsonpath-wasm/-/jsonpath-wasm-0.1.3.tgz",
"integrity": "sha512-c9A8bxQSzHWu52KPDSWjW9MxdhVOZ8J8Gpi3TjD5H4XMcpiGtDNi6Pm9gYb+TgcRynA6hHD5YTmRfaN6vTiSLg==",
"dev": true
},
"lcid": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
"integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
"dev": true,
"requires": {
"invert-kv": "^2.0.0"
}
},
"locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"dev": true,
"requires": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
}
},
"lodash": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
"dev": true
},
"log-symbols": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
"integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==",
"dev": true,
"requires": {
"chalk": "^2.0.1"
}
},
"map-age-cleaner": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
"integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
"dev": true,
"requires": {
"p-defer": "^1.0.0"
}
},
"mem": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz",
"integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==",
"dev": true,
"requires": {
"map-age-cleaner": "^0.1.1",
"mimic-fn": "^2.0.0",
"p-is-promise": "^2.0.0"
}
},
"mimic-fn": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
"dev": true
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true,
"requires": {
"minimist": "0.0.8"
}
},
"mocha": {
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-6.1.4.tgz",
"integrity": "sha512-PN8CIy4RXsIoxoFJzS4QNnCH4psUCPWc4/rPrst/ecSJJbLBkubMiyGCP2Kj/9YnWbotFqAoeXyXMucj7gwCFg==",
"dev": true,
"requires": {
"ansi-colors": "3.2.3",
"browser-stdout": "1.3.1",
"debug": "3.2.6",
"diff": "3.5.0",
"escape-string-regexp": "1.0.5",
"find-up": "3.0.0",
"glob": "7.1.3",
"growl": "1.10.5",
"he": "1.2.0",
"js-yaml": "3.13.1",
"log-symbols": "2.2.0",
"minimatch": "3.0.4",
"mkdirp": "0.5.1",
"ms": "2.1.1",
"node-environment-flags": "1.0.5",
"object.assign": "4.1.0",
"strip-json-comments": "2.0.1",
"supports-color": "6.0.0",
"which": "1.3.1",
"wide-align": "1.1.3",
"yargs": "13.2.2",
"yargs-parser": "13.0.0",
"yargs-unparser": "1.5.0"
}
},
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"dev": true
},
"nice-try": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
"dev": true
},
"node-environment-flags": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz",
"integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==",
"dev": true,
"requires": {
"object.getownpropertydescriptors": "^2.0.3",
"semver": "^5.7.0"
}
},
"npm-run-path": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
"integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
"dev": true,
"requires": {
"path-key": "^2.0.0"
}
},
"number-is-nan": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
"dev": true
},
"object-keys": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
"dev": true
},
"object.assign": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
"integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
"dev": true,
"requires": {
"define-properties": "^1.1.2",
"function-bind": "^1.1.1",
"has-symbols": "^1.0.0",
"object-keys": "^1.0.11"
}
},
"object.getownpropertydescriptors": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz",
"integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=",
"dev": true,
"requires": {
"define-properties": "^1.1.2",
"es-abstract": "^1.5.1"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"requires": {
"wrappy": "1"
}
},
"os-locale": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
"integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
"dev": true,
"requires": {
"execa": "^1.0.0",
"lcid": "^2.0.0",
"mem": "^4.0.0"
}
},
"p-defer": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
"integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=",
"dev": true
},
"p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
"dev": true
},
"p-is-promise": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz",
"integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==",
"dev": true
},
"p-limit": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
"integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
}
},
"p-locate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"dev": true,
"requires": {
"p-limit": "^2.0.0"
}
},
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true
},
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
"dev": true
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},
"path-key": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
"integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
"dev": true
},
"pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"dev": true,
"requires": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
"dev": true
},
"require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
"dev": true
},
"semver": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
"dev": true
},
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
"dev": true
},
"shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
"integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
"dev": true,
"requires": {
"shebang-regex": "^1.0.0"
}
},
"shebang-regex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
"dev": true
},
"signal-exit": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
"dev": true
},
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
"dev": true
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"dev": true,
"requires": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
}
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"dev": true,
"requires": {
"ansi-regex": "^3.0.0"
}
},
"strip-eof": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
"integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
"dev": true
},
"strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"dev": true
},
"supports-color": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz",
"integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
},
"which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
"dev": true,
"requires": {
"isexe": "^2.0.0"
}
},
"which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
"dev": true
},
"wide-align": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
"dev": true,
"requires": {
"string-width": "^1.0.2 || 2"
}
},
"wrap-ansi": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
"dev": true,
"requires": {
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1"
},
"dependencies": {
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true
},
"is-fullwidth-code-point": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"dev": true,
"requires": {
"number-is-nan": "^1.0.0"
}
},
"string-width": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"dev": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
"strip-ansi": "^3.0.0"
}
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
"ansi-regex": "^2.0.0"
}
}
}
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
},
"y18n": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"dev": true
},
"yargs": {
"version": "13.2.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz",
"integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==",
"dev": true,
"requires": {
"cliui": "^4.0.0",
"find-up": "^3.0.0",
"get-caller-file": "^2.0.1",
"os-locale": "^3.1.0",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^3.0.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^13.0.0"
},
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
},
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"dev": true,
"requires": {
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^5.1.0"
}
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
"ansi-regex": "^4.1.0"
}
}
}
},
"yargs-parser": {
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz",
"integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
},
"yargs-unparser": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.5.0.tgz",
"integrity": "sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==",
"dev": true,
"requires": {
"flat": "^4.1.0",
"lodash": "^4.17.11",
"yargs": "^12.0.5"
},
"dependencies": {
"get-caller-file": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
"integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
"dev": true
},
"require-main-filename": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
"integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
"dev": true
},
"yargs": {
"version": "12.0.5",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
"integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
"dev": true,
"requires": {
"cliui": "^4.0.0",
"decamelize": "^1.2.0",
"find-up": "^3.0.0",
"get-caller-file": "^1.0.1",
"os-locale": "^3.0.0",
"require-directory": "^2.1.1",
"require-main-filename": "^1.0.1",
"set-blocking": "^2.0.0",
"string-width": "^2.0.0",
"which-module": "^2.0.0",
"y18n": "^3.2.1 || ^4.0.0",
"yargs-parser": "^11.1.1"
}
},
"yargs-parser": {
"version": "11.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz",
"integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
}
}
}
}
}

14
wasm/tests/package.json Normal file
View File

@ -0,0 +1,14 @@
{
"name": "jsonpath-wasm-test",
"version": "0.1.0",
"description": "jsonpath-wasm internal tests",
"author": "Changseok Han <freestrings@gmail.com>",
"license": "MIT",
"scripts": {
"test": "mocha"
},
"devDependencies": {
"jsonpath-wasm": "*",
"mocha": "^6.1.4"
}
}

View File

@ -0,0 +1,906 @@
const jsonpath = require('jsonpath-wasm');
let jsonObj = {
'store': {
'book': [
{
'category': 'reference',
'author': 'Nigel Rees',
'title': 'Sayings of the Century',
'price': 8.95,
},
{
'category': 'fiction',
'author': 'Evelyn Waugh',
'title': 'Sword of Honour',
'price': 12.99,
},
{
'category': 'fiction',
'author': 'Herman Melville',
'title': 'Moby Dick',
'isbn': '0-553-21311-3',
'price': 8.99,
},
{
'category': 'fiction',
'author': 'J. R. R. Tolkien',
'title': 'The Lord of the Rings',
'isbn': '0-395-19395-8',
'price': 22.99,
},
],
'bicycle': {
'color': 'red',
'price': 19.95,
},
},
'expensive': 10,
};
let list = {
'$.store.book[*].author': [
'Nigel Rees',
'Evelyn Waugh',
'Herman Melville',
'J. R. R. Tolkien',
],
'$..author': [
'Nigel Rees',
'Evelyn Waugh',
'Herman Melville',
'J. R. R. Tolkien',
],
'$.store.*': [
[
{
'category': 'reference',
'author': 'Nigel Rees',
'title': 'Sayings of the Century',
'price': 8.95,
},
{
'category': 'fiction',
'author': 'Evelyn Waugh',
'title': 'Sword of Honour',
'price': 12.99,
},
{
'category': 'fiction',
'author': 'Herman Melville',
'title': 'Moby Dick',
'isbn': '0-553-21311-3',
'price': 8.99,
},
{
'category': 'fiction',
'author': 'J. R. R. Tolkien',
'title': 'The Lord of the Rings',
'isbn': '0-395-19395-8',
'price': 22.99,
},
],
{
'color': 'red',
'price': 19.95,
},
],
'$.store..price': [
8.95,
12.99,
8.99,
22.99,
19.95,
],
'$..book[2]': [
{
'category': 'fiction',
'author': 'Herman Melville',
'title': 'Moby Dick',
'isbn': '0-553-21311-3',
'price': 8.99,
},
],
'$..book[-2]': [
{
'category': 'fiction',
'author': 'Herman Melville',
'title': 'Moby Dick',
'isbn': '0-553-21311-3',
'price': 8.99,
},
],
'$..book[0,1]': [
{
'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,
},
],
'$..book[:2]': [
{
'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,
},
],
'$..book[1:2]': [
{
'category': 'fiction',
'author': 'Evelyn Waugh',
'title': 'Sword of Honour',
'price': 12.99,
},
],
'$..book[-2:]': [
{
'category': 'fiction',
'author': 'Herman Melville',
'title': 'Moby Dick',
'isbn': '0-553-21311-3',
'price': 8.99,
},
{
'category': 'fiction',
'author': 'J. R. R. Tolkien',
'title': 'The Lord of the Rings',
'isbn': '0-395-19395-8',
'price': 22.99,
},
],
'$..book[2:]': [
{
'category': 'fiction',
'author': 'Herman Melville',
'title': 'Moby Dick',
'isbn': '0-553-21311-3',
'price': 8.99,
},
{
'category': 'fiction',
'author': 'J. R. R. Tolkien',
'title': 'The Lord of the Rings',
'isbn': '0-395-19395-8',
'price': 22.99,
},
],
'$..book[?(@.isbn)]': [
{
'category': 'fiction',
'author': 'Herman Melville',
'title': 'Moby Dick',
'isbn': '0-553-21311-3',
'price': 8.99,
},
{
'category': 'fiction',
'author': 'J. R. R. Tolkien',
'title': 'The Lord of the Rings',
'isbn': '0-395-19395-8',
'price': 22.99,
},
],
'$.store.book[?(@.price < 10)]': [
{
'category': 'reference',
'author': 'Nigel Rees',
'title': 'Sayings of the Century',
'price': 8.95,
},
{
'category': 'fiction',
'author': 'Herman Melville',
'title': 'Moby Dick',
'isbn': '0-553-21311-3',
'price': 8.99,
},
],
'$..*': [
{
'book': [
{
'category': 'reference',
'author': 'Nigel Rees',
'title': 'Sayings of the Century',
'price': 8.95,
},
{
'category': 'fiction',
'author': 'Evelyn Waugh',
'title': 'Sword of Honour',
'price': 12.99,
},
{
'category': 'fiction',
'author': 'Herman Melville',
'title': 'Moby Dick',
'isbn': '0-553-21311-3',
'price': 8.99,
},
{
'category': 'fiction',
'author': 'J. R. R. Tolkien',
'title': 'The Lord of the Rings',
'isbn': '0-395-19395-8',
'price': 22.99,
},
],
'bicycle': {
'color': 'red',
'price': 19.95,
},
},
10,
[
{
'category': 'reference',
'author': 'Nigel Rees',
'title': 'Sayings of the Century',
'price': 8.95,
},
{
'category': 'fiction',
'author': 'Evelyn Waugh',
'title': 'Sword of Honour',
'price': 12.99,
},
{
'category': 'fiction',
'author': 'Herman Melville',
'title': 'Moby Dick',
'isbn': '0-553-21311-3',
'price': 8.99,
},
{
'category': 'fiction',
'author': 'J. R. R. Tolkien',
'title': 'The Lord of the Rings',
'isbn': '0-395-19395-8',
'price': 22.99,
},
],
{
'color': 'red',
'price': 19.95,
},
{
'category': 'reference',
'author': 'Nigel Rees',
'title': 'Sayings of the Century',
'price': 8.95,
},
{
'category': 'fiction',
'author': 'Evelyn Waugh',
'title': 'Sword of Honour',
'price': 12.99,
},
{
'category': 'fiction',
'author': 'Herman Melville',
'title': 'Moby Dick',
'isbn': '0-553-21311-3',
'price': 8.99,
},
{
'category': 'fiction',
'author': 'J. R. R. Tolkien',
'title': 'The Lord of the Rings',
'isbn': '0-395-19395-8',
'price': 22.99,
},
'reference',
'Nigel Rees',
'Sayings of the Century',
8.95,
'fiction',
'Evelyn Waugh',
'Sword of Honour',
12.99,
'fiction',
'Herman Melville',
'Moby Dick',
'0-553-21311-3',
8.99,
'fiction',
'J. R. R. Tolkien',
'The Lord of the Rings',
'0-395-19395-8',
22.99,
'red',
19.95,
],
'$..book[ ?( (@.price < 13 || $.store.bicycle.price < @.price) && @.price <=10 ) ]': [
{
'category': 'reference',
'author': 'Nigel Rees',
'title': 'Sayings of the Century',
'price': 8.95,
},
{
'category': 'fiction',
'author': 'Herman Melville',
'title': 'Moby Dick',
'isbn': '0-553-21311-3',
'price': 8.99,
},
],
};
describe('compile test', () => {
it('basic', (done) => {
let template = jsonpath.compile('$.a');
let result = template({'a': 1});
if (result[0] === 1) {
done();
}
});
});
describe('selector test', () => {
it('basic', (done) => {
let selector = jsonpath.selector({'a': 1});
let result = selector('$.a');
if (result[0] === 1) {
done();
}
});
});
describe('select test', () => {
it('basic', (done) => {
let result = jsonpath.select({'a': 1}, '$.a');
if (result[0] === 1) {
done();
}
});
});
describe('filter test', () => {
function run(done, path, expected) {
let result = jsonpath.select(jsonObj, path);
if (JSON.stringify(result) === JSON.stringify(expected)) {
done();
}
}
for (var i in list) {
it(i, (done) => {
run(done, i, list[i]);
});
}
it('object equal', (done) => {
let selector = new jsonpath.Selector();
selector.path('$..[?(@.a == 1)]');
selector.value({
'a': 1,
'b': {'a': 1},
'c': {'a': 1},
});
let result = selector.select();
if (JSON.stringify(result) === JSON.stringify([{'a': 1}, {'a': 1}])) {
done();
}
});
it('escaped single quote notation', (done) => {
let result = jsonpath.select({"single'quote":"value"}, "$['single\\'quote']");
if (JSON.stringify(result) === JSON.stringify(["value"])) {
done();
}
});
it('escaped double quote notation', (done) => {
let result = jsonpath.select({"single\"quote":"value"}, "$['single\"quote']");
if (JSON.stringify(result) === JSON.stringify(["value"])) {
done();
}
});
it('array range with step - $[::]', (done) => {
let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[::]");
if (JSON.stringify(result) === JSON.stringify(["first", "second", "third", "forth", "fifth"])) {
done();
}
});
it('array range with step - $[::2]', (done) => {
let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[::2]");
if (JSON.stringify(result) === JSON.stringify(["first", "third", "fifth"])) {
done();
}
});
it('array range with step - $[1: :]', (done) => {
let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[1: :]");
if (JSON.stringify(result) === JSON.stringify(["second", "third", "forth", "fifth"])) {
done();
}
});
it('array range with step - $[1:2:]', (done) => {
let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[1:2:]");
if (JSON.stringify(result) === JSON.stringify(["second"])) {
done();
}
});
it('array range with step - $[1::2]', (done) => {
let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[1::2]");
if (JSON.stringify(result) === JSON.stringify(["second", "forth"])) {
done();
}
});
it('array range with step - $[0:3:1]', (done) => {
let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[0:3:1]");
if (JSON.stringify(result) === JSON.stringify(["first", "second", "third"])) {
done();
}
});
it('array range with step - $[0:3:2]', (done) => {
let result = jsonpath.select(["first", "second", "third", "forth", "fifth"], "$[0:3:2]");
if (JSON.stringify(result) === JSON.stringify(["first", "third"])) {
done();
}
});
it('array keys', (done) => {
let result = jsonpath.select({
"key1": "value1",
"key2": 2
}, "$['key1', 'key2']");
if (JSON.stringify(result) === JSON.stringify(["value1", 2])) {
done();
}
});
});
describe('SelectorMut test', () => {
it('delete', (done) => {
let jsonObjNew = JSON.parse(JSON.stringify(jsonObj));
let result = jsonpath.deleteValue(jsonObjNew, '$.store.book');
if (JSON.stringify(result) === JSON.stringify({
'store': {
'book': null,
'bicycle': {
'color': 'red',
'price': 19.95,
},
},
'expensive': 10,
})) {
done();
}
});
it('replaceWith', (done) => {
let jsonObjNew = JSON.parse(JSON.stringify(jsonObj));
let result = jsonpath.replaceWith(jsonObjNew, '$.store.book', (v) => {
let ret = v[0];
ret.price = 9;
return ret;
});
if (JSON.stringify(result) === JSON.stringify({
'store': {
'book': {
'category': 'reference',
'author': 'Nigel Rees',
'title': 'Sayings of the Century',
'price': 9,
},
'bicycle': {
'color': 'red',
'price': 19.95,
},
},
'expensive': 10,
})) {
done();
}
});
it('SeletorMut delete', (done) => {
let jsonObjNew = JSON.parse(JSON.stringify(jsonObj));
let selector = new jsonpath.SelectorMut();
selector.path('$.store.book');
selector.value(jsonObjNew);
selector.deleteValue();
let result = selector.take();
if (JSON.stringify(result) === JSON.stringify({
'store': {
'book': null,
'bicycle': {
'color': 'red',
'price': 19.95,
},
},
'expensive': 10,
})) {
done();
}
});
it('SeletorMut replaceWith', (done) => {
let jsonObjNew = JSON.parse(JSON.stringify(jsonObj));
let selector = new jsonpath.SelectorMut();
selector.path('$.store.book');
selector.value(jsonObjNew);
selector.replaceWith((v) => {
let ret = v[0];
ret.price = 9;
return ret;
});
let result = selector.take();
if (JSON.stringify(result) === JSON.stringify({
'store': {
'book': {
'category': 'reference',
'author': 'Nigel Rees',
'title': 'Sayings of the Century',
'price': 9,
},
'bicycle': {
'color': 'red',
'price': 19.95,
},
},
'expensive': 10,
})) {
done();
}
});
});
describe('Selector test', () => {
it('select', (done) => {
let selector = new jsonpath.Selector();
selector.value(jsonObj);
for (var i in list) {
selector.path(i);
if (JSON.stringify(list[i]) !== JSON.stringify(selector.select())) {
throw `fail: ${i}`;
}
}
done();
});
});
describe('README test', () => {
it('jsonpath.Selector', (done) => {
let jsonObj = {
'school': {
'friends': [
{'name': '친구1', 'age': 20},
{'name': '친구2', 'age': 20},
],
},
'friends': [
{'name': '친구3', 'age': 30},
{'name': '친구4'},
],
};
let selector = new jsonpath.Selector();
selector.value(jsonObj);
{
selector.path('$..[?(@.age >= 30)]');
let jsonObj = selector.select();
let resultObj = [{'name': '친구3', 'age': 30}];
if (JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) {
throw 'jsonpath.Selector: $..[?(@.age >= 30)]';
}
}
{
selector.path('$..[?(@.age == 20)]');
let jsonObj = selector.select();
let resultObj = [{'name': '친구1', 'age': 20}, {'name': '친구2', 'age': 20}];
if (JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) {
throw 'jsonpath.Selector: $..[?(@.age >= 20)]';
}
}
{
selector.value({'friends': [{'name': '친구5', 'age': 20}]});
let jsonObj = selector.select();
let resultObj = [{'name': '친구5', 'age': 20}];
if (JSON.stringify(jsonObj) !== JSON.stringify(resultObj)) {
throw 'jsonpath.Selector: change value';
}
}
done();
});
it('jsonpath.SelectorMut', (done) => {
let jsonObj = {
'school': {
'friends': [
{'name': '친구1', 'age': 20},
{'name': '친구2', 'age': 20},
],
},
'friends': [
{'name': '친구3', 'age': 30},
{'name': '친구4'},
],
};
let selector = new jsonpath.SelectorMut();
selector.path('$..[?(@.age == 20)]');
{
selector.value(jsonObj);
selector.deleteValue();
let resultObj = {
'school': {'friends': [null, null]},
'friends': [
{'name': '친구3', 'age': 30},
{'name': '친구4'},
],
};
if (JSON.stringify(selector.take()) !== JSON.stringify(resultObj)) {
throw 'jsonpath.SelectorMut.deleteValue';
}
}
{
selector.value(jsonObj);
selector.replaceWith((v) => {
v.age = v.age * 2;
return v;
});
let resultObj = {
'school': {
'friends': [
{'name': '친구1', 'age': 40},
{'name': '친구2', 'age': 40},
],
},
'friends': [
{'name': '친구3', 'age': 30},
{'name': '친구4'},
],
};
if (JSON.stringify(selector.take()) !== JSON.stringify(resultObj)) {
throw 'jsonpath.SelectorMut.replaceWith';
}
}
done();
});
it('jsonpath.select(json: string|object, jsonpath: string)', (done) => {
let jsonObj = {
'school': {
'friends': [
{'name': '친구1', 'age': 20},
{'name': '친구2', 'age': 20},
],
},
'friends': [
{'name': '친구3', 'age': 30},
{'name': '친구4'},
],
};
let ret = [
{'name': '친구3', 'age': 30},
{'name': '친구1', 'age': 20},
];
let selectAsString = jsonpath.select(JSON.stringify(jsonObj), '$..friends[0]');
let selectAsObj = jsonpath.select(jsonObj, '$..friends[0]');
if (
JSON.stringify(ret) !== JSON.stringify(selectAsString) ||
JSON.stringify(ret) !== JSON.stringify(selectAsObj)
) {
throw 'jsonpath.select(json: string|object, jsonpath: string)';
}
done();
});
it('jsonpath.compile(jsonpath: string)', (done) => {
let template = jsonpath.compile('$..friends[0]');
let jsonObj = {
'school': {
'friends': [
{'name': '친구1', 'age': 20},
{'name': '친구2', 'age': 20},
],
},
'friends': [
{'name': '친구3', 'age': 30},
{'name': '친구4'},
],
};
let ret = [
{'name': '친구3', 'age': 30},
{'name': '친구1', 'age': 20},
];
let selectAsString = template(JSON.stringify(jsonObj));
let selectAsObj = template(jsonObj);
if (
JSON.stringify(ret) !== JSON.stringify(selectAsString) ||
JSON.stringify(ret) !== JSON.stringify(selectAsObj)
) {
throw 'jsonpath.compile(jsonpath: string) 1';
}
let jsonObj2 = {
'school': {
'friends': [
{'name': 'Millicent Norman'},
{'name': 'Vincent Cannon'},
],
},
'friends': [{'age': 30}, {'age': 40}],
};
let ret2 = [
{'age': 30},
{'name': 'Millicent Norman'},
];
let selectAsString2 = template(JSON.stringify(jsonObj2));
let selectAsObj2 = template(jsonObj2);
if (
JSON.stringify(ret2) !== JSON.stringify(selectAsString2) ||
JSON.stringify(ret2) !== JSON.stringify(selectAsObj2)
) {
throw 'jsonpath.compile(jsonpath: string) 2';
}
done();
});
it('jsonpath.selector(json: string|object)', (done) => {
let jsonObj = {
'school': {
'friends': [
{'name': '친구1', 'age': 20},
{'name': '친구2', 'age': 20},
],
},
'friends': [
{'name': '친구3', 'age': 30},
{'name': '친구4'},
],
};
let ret1 = [
{'name': '친구3', 'age': 30},
{'name': '친구1', 'age': 20},
];
let ret2 = [
{'name': '친구4'},
{'name': '친구2', 'age': 20},
];
let selector = jsonpath.selector(jsonObj);
let select1 = selector('$..friends[0]');
let select2 = selector('$..friends[1]');
if (
JSON.stringify(ret1) !== JSON.stringify(select1) ||
JSON.stringify(ret2) !== JSON.stringify(select2)
) {
throw 'jsonpath.selector(json: string|object)';
}
done();
});
it('jsonpath.deleteValue(json: string|object, path: string)', (done) => {
let jsonObj = {
'school': {
'friends': [
{'name': '친구1', 'age': 20},
{'name': '친구2', 'age': 20},
],
},
'friends': [
{'name': '친구3', 'age': 30},
{'name': '친구4'},
],
};
let _1 = jsonpath.deleteValue(jsonObj, '$..friends[0]');
let result = jsonpath.deleteValue(_1, '$..friends[1]');
if (JSON.stringify(result) === JSON.stringify({
'school': {'friends': [null, null]},
'friends': [null, null],
})) {
done();
}
});
it('jsonpath.replaceWith(json: string|object, path: string, fun: function(json: object) => json: object', (done) => {
let jsonObj = {
'school': {
'friends': [
{'name': '친구1', 'age': 20},
{'name': '친구2', 'age': 20},
],
},
'friends': [
{'name': '친구3', 'age': 30},
{'name': '친구4'},
],
};
let result = jsonpath.replaceWith(jsonObj, '$..friends[0]', (v) => {
v.age = v.age * 2;
return v;
});
if (JSON.stringify(result) === JSON.stringify({
'school': {
'friends': [
{'name': '친구1', 'age': 40},
{'name': '친구2', 'age': 20},
],
},
'friends': [
{'name': '친구3', 'age': 60},
{'name': '친구4'},
],
})) {
done();
}
});
});
describe('ISSUE test', () => {
it('Results do not match other implementations #6', (done) => {
let result = jsonpath.select(["first", "second"], "$[:]");
if (JSON.stringify(result) === JSON.stringify(["first", "second"])) {
done();
}
});
});

View File

@ -10,6 +10,7 @@ extern crate wasm_bindgen_test;
use serde_json::Value;
use wasm_bindgen::*;
use wasm_bindgen::prelude::*;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
@ -94,25 +95,32 @@ fn selector() {
assert_eq!(json, target_json());
}
#[wasm_bindgen_test]
fn alloc_dealloc_json() {
let ptr = jsonpath::alloc_json(JsValue::from_str(json_str()));
assert_eq!(ptr > 0, true);
let json: Value = jsonpath::select(JsValue::from_f64(ptr as f64), "$..book[2]").into_serde().unwrap();
assert_eq!(json, target_json());
assert_eq!(jsonpath::dealloc_json(ptr), true);
let err = jsonpath::select(JsValue::from_f64(ptr as f64), "$..book[2]").as_string().unwrap();
assert_eq!(err, "Invalid pointer".to_string());
}
#[wasm_bindgen_test]
fn selector_struct() {
let mut selector = jsonpath::Selector::new();
selector.path("$..book[2]").unwrap();
selector.value(JsValue::from_str(json_str())).unwrap();
let json: Value = selector.select_to().unwrap().into_serde().unwrap();
let json: Value = selector.select_as().unwrap().into_serde().unwrap();
assert_eq!(json, target_json());
let cb = Closure::wrap(Box::new(|js_value: JsValue| {
match js_value.into_serde().unwrap() {
Value::Array(mut vec) => {
match vec.pop().unwrap() {
Value::Object(mut map) => {
map.clear();
map.insert("key".to_string(), Value::String("value".to_string()));
JsValue::from_serde(&Value::Object(map)).unwrap()
}
_ => return JsValue::NULL
}
}
_ => return JsValue::NULL
}
}) as Box<Fn(JsValue) -> JsValue>);
selector.map(cb.as_ref().clone()).unwrap();
let js_value = selector.get().unwrap();
assert_eq!(js_value.into_serde::<Value>().unwrap(), json!({ "key": "value" }));
}

View File

@ -1,170 +0,0 @@
/**
* GitHub Corners, page css
* Author: Tim Holman
*/
* {
box-sizing: border-box;
}
html, body {
font-family: Helvetica, Arial, sans-serif;
background: #fff;
margin: 0px;
}
h1 {
font-size: 30px;
}
p {
font-size: 16px;
font-weight: 100;
line-height: 24px;
}
.wrapper {
padding-right: 20px;
padding-left: 20px;
padding-top: 50px;
max-width: 580px;
margin: auto;
}
/**
* Demo Code
*/
.version {
border: 2px solid #eee;
margin-top: 25px;
height: 200px;
display: -webkit-flex;
display: flex;
}
.version-section {
padding: 10px;
height: 100%;
}
.version-section.dark {
background: #151513;
}
.demo {
border-right: 2px solid #eee;
position: relative;
width: 200px;
}
.code {
vertical-align: top;
width: 336px;
}
.code textarea {
border: 2px solid #eee;
outline: 0px;
height: 100%;
width: 100%;
font-family: monospace;
font-size: 10px;
}
.github-corner:hover .octo-arm {
animation: octocat-wave 560ms ease-in-out;
}
@keyframes octocat-wave {
0% {
transform: rotate(0deg);
}
20% {
transform: rotate(-25deg);
}
40% {
transform: rotate(10deg);
}
60% {
transform: rotate(-25deg);
}
80% {
transform: rotate(10deg);
}
100% {
transform: rotate(0deg);
}
}
@media (max-width: 500px) {
.github-corner:hover .octo-arm {
animation: none;
}
.github-corner .octo-arm {
animation: octocat-wave 560ms ease-in-out;
}
}
/**
* Footer
*/
footer {
width: 100%;
margin-top: 25px;
margin-bottom: 100px;
background: #F3F3F3;
height: 50px;
padding: 15px;
padding-left: 25px;
padding-right: 25px;
color: #2D2D2D;
font-size: 13px;
letter-spacing: 1px;
font-family: monospace;
}
footer a {
color: #2d2d2d;
}
footer span {
margin-left: 10px;
margin-right: 10px;
}
.twitter-share-button {
margin-bottom: -4px;
}
/**
* Media
*/
@media (max-width: 500px) {
footer {
text-align: center;
height: auto;
line-height: 30px;
}
footer span {
display: none;
}
footer a {
display: block;
}
.twitter-share-button {
margin-bottom: -8px;
}
}

View File

@ -1,10 +1,82 @@
<!DOCTYPE html>
<html>
<head>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-3020058-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-3020058-1');
</script>
<meta charset="utf-8">
<meta property="og:image" content="https://avatars0.githubusercontent.com/u/1104423?s=400&amp;v=4"/>
<meta property="og:site_name" content="GitHub"/>
<meta property="og:type" content="object"/>
<meta property="og:title" content="freestrings/jsonpath"/>
<meta property="og:url" content="https://github.com/freestrings/jsonpath"/>
<meta property="og:description" content="JsonPath evaluator with Webassembly via Rust - freestrings/jsonpath"/>
<title>JsonPath evaluator - Webassembly via Rust</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<link href="./css/github-corner.css" rel="stylesheet" type="text/css">
<style>
/**
* GitHub Corners, page css
* Author: Tim Holman
*/
.code textarea {
border: 2px solid #eee;
outline: 0px;
height: 100%;
width: 100%;
font-family: monospace;
font-size: 10px;
}
.github-corner:hover .octo-arm {
animation: octocat-wave 560ms ease-in-out;
}
@keyframes octocat-wave {
0% {
transform: rotate(0deg);
}
20% {
transform: rotate(-25deg);
}
40% {
transform: rotate(10deg);
}
60% {
transform: rotate(-25deg);
}
80% {
transform: rotate(10deg);
}
100% {
transform: rotate(0deg);
}
}
@media (max-width: 500px) {
.github-corner:hover .octo-arm {
animation: none;
}
.github-corner .octo-arm {
animation: octocat-wave 560ms ease-in-out;
}
}
</style>
</head>
<body role="document">
<div class="container">
@ -92,7 +164,9 @@
<svg width="80" height="80" viewBox="0 0 250 250" style="position: absolute; top: 0px; right: 0px; border: 0px;" aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" fill="#151513"></path>
<path class="octo-arm" d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="#ffffff" style="transform-origin: 130px 106px;"></path>
<path class="octo-body" d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="#ffffff"></path>
<path class="octo-body"
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
fill="#ffffff"></path>
</svg>
</a>
<script src="./bootstrap.js"></script>

View File

@ -1,4 +1,4 @@
import * as jsonpath from "@browser/jsonpath-wasm";
import * as jsonpath from "jsonpath-wasm";
function getTextarea() {
return document.querySelector('#json-example');

View File

@ -1,6 +1,6 @@
{
"name": "jsonpath-wasm",
"version": "0.1.0",
"name": "jsonpath-wasm-evaluator",
"version": "0.2.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -1010,13 +1010,14 @@
"dev": true
},
"copy-webpack-plugin": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.0.0.tgz",
"integrity": "sha512-iiDj+8nnZeW/i8vYJ3+ABSZkOefJnDYIGLojiZKKFDvf1wcEInABXH1+hN7axQMn04qvJxKjgVOee0e14XPtCg==",
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.0.2.tgz",
"integrity": "sha512-7nC7EynPrnBTtBwwbG1aTqrfNS1aTb9eEjSmQDqFtKAsJrR3uDb+pCDIFT2LzhW+SgGJxQcYzThrmXzzZ720uw==",
"dev": true,
"requires": {
"cacache": "^11.3.1",
"find-cache-dir": "^2.0.0",
"glob-parent": "^3.1.0",
"globby": "^7.1.1",
"is-glob": "^4.0.0",
"loader-utils": "^1.1.0",
@ -1928,9 +1929,9 @@
"dev": true
},
"fsevents": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz",
"integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==",
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz",
"integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==",
"dev": true,
"optional": true,
"requires": {
@ -1957,7 +1958,7 @@
"optional": true
},
"are-we-there-yet": {
"version": "1.1.4",
"version": "1.1.5",
"bundled": true,
"dev": true,
"optional": true,
@ -1983,7 +1984,7 @@
}
},
"chownr": {
"version": "1.0.1",
"version": "1.1.1",
"bundled": true,
"dev": true,
"optional": true
@ -2022,7 +2023,7 @@
}
},
"deep-extend": {
"version": "0.5.1",
"version": "0.6.0",
"bundled": true,
"dev": true,
"optional": true
@ -2071,7 +2072,7 @@
}
},
"glob": {
"version": "7.1.2",
"version": "7.1.3",
"bundled": true,
"dev": true,
"optional": true,
@ -2091,12 +2092,12 @@
"optional": true
},
"iconv-lite": {
"version": "0.4.21",
"version": "0.4.24",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safer-buffer": "^2.1.0"
"safer-buffer": ">= 2.1.2 < 3"
}
},
"ignore-walk": {
@ -2161,17 +2162,17 @@
"optional": true
},
"minipass": {
"version": "2.2.4",
"version": "2.3.5",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.1",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
}
},
"minizlib": {
"version": "1.1.0",
"version": "1.2.1",
"bundled": true,
"dev": true,
"optional": true,
@ -2195,7 +2196,7 @@
"optional": true
},
"needle": {
"version": "2.2.0",
"version": "2.2.4",
"bundled": true,
"dev": true,
"optional": true,
@ -2206,18 +2207,18 @@
}
},
"node-pre-gyp": {
"version": "0.10.0",
"version": "0.10.3",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"detect-libc": "^1.0.2",
"mkdirp": "^0.5.1",
"needle": "^2.2.0",
"needle": "^2.2.1",
"nopt": "^4.0.1",
"npm-packlist": "^1.1.6",
"npmlog": "^4.0.2",
"rc": "^1.1.7",
"rc": "^1.2.7",
"rimraf": "^2.6.1",
"semver": "^5.3.0",
"tar": "^4"
@ -2234,13 +2235,13 @@
}
},
"npm-bundled": {
"version": "1.0.3",
"version": "1.0.5",
"bundled": true,
"dev": true,
"optional": true
},
"npm-packlist": {
"version": "1.1.10",
"version": "1.2.0",
"bundled": true,
"dev": true,
"optional": true,
@ -2317,12 +2318,12 @@
"optional": true
},
"rc": {
"version": "1.2.7",
"version": "1.2.8",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"deep-extend": "^0.5.1",
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
"minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
@ -2352,16 +2353,16 @@
}
},
"rimraf": {
"version": "2.6.2",
"version": "2.6.3",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"glob": "^7.0.5"
"glob": "^7.1.3"
}
},
"safe-buffer": {
"version": "5.1.1",
"version": "5.1.2",
"bundled": true,
"dev": true,
"optional": true
@ -2379,7 +2380,7 @@
"optional": true
},
"semver": {
"version": "5.5.0",
"version": "5.6.0",
"bundled": true,
"dev": true,
"optional": true
@ -2432,17 +2433,17 @@
"optional": true
},
"tar": {
"version": "4.4.1",
"version": "4.4.8",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"chownr": "^1.0.1",
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
"minipass": "^2.2.4",
"minizlib": "^1.1.0",
"minipass": "^2.3.4",
"minizlib": "^1.1.1",
"mkdirp": "^0.5.0",
"safe-buffer": "^5.1.1",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.2"
}
},
@ -2453,12 +2454,12 @@
"optional": true
},
"wide-align": {
"version": "1.1.2",
"version": "1.1.3",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"string-width": "^1.0.2"
"string-width": "^1.0.2 || 2"
}
},
"wrappy": {
@ -2468,7 +2469,7 @@
"optional": true
},
"yallist": {
"version": "3.0.2",
"version": "3.0.3",
"bundled": true,
"dev": true,
"optional": true
@ -2655,9 +2656,9 @@
}
},
"homedir-polyfill": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz",
"integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=",
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
"integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
"dev": true,
"requires": {
"parse-passwd": "^1.0.0"
@ -3467,9 +3468,9 @@
"dev": true
},
"nan": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
"version": "2.13.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz",
"integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==",
"dev": true,
"optional": true
},
@ -4779,9 +4780,9 @@
"dev": true
},
"supports-color": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
"integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
@ -5200,9 +5201,9 @@
}
},
"webpack-cli": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.2.3.tgz",
"integrity": "sha512-Ik3SjV6uJtWIAN5jp5ZuBMWEAaP5E4V78XJ2nI+paFPh8v4HPSwo/myN0r29Xc/6ZKnd2IdrAlpSgNOu2CDQ6Q==",
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.0.tgz",
"integrity": "sha512-t1M7G4z5FhHKJ92WRKwZ1rtvi7rHc0NZoZRbSkol0YKl4HvcC8+DsmGDmK7MmZxHSAetHagiOsjOB6MmzC2TUw==",
"dev": true,
"requires": {
"chalk": "^2.4.1",
@ -5215,13 +5216,13 @@
"loader-utils": "^1.1.0",
"supports-color": "^5.5.0",
"v8-compile-cache": "^2.0.2",
"yargs": "^12.0.4"
"yargs": "^12.0.5"
},
"dependencies": {
"camelcase": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
"integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==",
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"dev": true
},
"decamelize": {
@ -5230,58 +5231,6 @@
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
"dev": true
},
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"dev": true,
"requires": {
"locate-path": "^3.0.0"
}
},
"locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"dev": true,
"requires": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
}
},
"p-limit": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz",
"integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
}
},
"p-locate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"dev": true,
"requires": {
"p-limit": "^2.0.0"
}
},
"p-try": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz",
"integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==",
"dev": true
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
},
"yargs": {
"version": "12.0.5",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",

View File

@ -1,32 +1,15 @@
{
"name": "jsonpath-wasm",
"version": "0.1.0",
"name": "jsonpath-wasm-evaluator",
"version": "0.2.0",
"main": "index.js",
"scripts": {
"build": "webpack --config webpack.config.js",
"start": "webpack-dev-server"
},
"repository": {
"type": "git",
"url": "git+https://github.com/freestrings/jsonpath.git"
},
"keywords": [
"webassembly",
"wasm",
"rust",
"webpack",
"jsonpath"
],
"author": "Changseok Han <freestrings@gmail.com>",
"license": "(MIT OR Apache-2.0)",
"bugs": {
"url": "https://github.com/freestrings/jsonpath/issues"
},
"homepage": "https://github.com/freestrings/jsonpath#readme",
"devDependencies": {
"copy-webpack-plugin": "^5.0.0",
"copy-webpack-plugin": "^5.0.1",
"webpack": "^4.29.6",
"webpack-cli": "^3.1.0",
"webpack-dev-server": "^3.1.5"
"webpack-cli": "^3.3.0",
"webpack-dev-server": "^3.2.1"
}
}

View File

@ -1,5 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80" viewBox="0 0 250 250" fill="#151513" style="position: absolute; top: 0; left: 0">
<path fill="#fff" d="M250 0L135 115h-15l-12 27L0 250V0z"/>
<path class="octo-arm" d="M122 109c15-9 9-19 9-19-3-7-2-11-2-11 1-7-3-2-3-2-4 5-2 11-2 11 3 10-5 15-9 16" style="-webkit-transform-origin: 120px 144px; transform-origin: 120px 144px"/>
<path class="octo-body" d="M135 115s-4 2-5 0l-14-14c-3-2-6-3-8-3 8-11 15-24-2-41-5-5-10-7-16-7-1-2-3-7-12-11 0 0-5 3-7 16-4 2-8 5-12 9s-7 8-9 12c-14 4-17 9-17 9 4 8 9 11 11 11 0 6 2 11 7 16 16 16 30 10 41 2 0 3 1 7 5 11l12 11c1 2-1 6-1 6z"/>
</svg>

Before

Width:  |  Height:  |  Size: 644 B

View File

@ -1,5 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80" viewBox="0 0 250 250" fill="#151513" style="position: absolute; top: 0; right: 0">
<path d="M0 0l115 115h15l12 27 108 108V0z" fill="#fff"/>
<path class="octo-arm" d="M128 109c-15-9-9-19-9-19 3-7 2-11 2-11-1-7 3-2 3-2 4 5 2 11 2 11-3 10 5 15 9 16" style="-webkit-transform-origin: 130px 106px; transform-origin: 130px 106px"/>
<path class="octo-body" d="M115 115s4 2 5 0l14-14c3-2 6-3 8-3-8-11-15-24 2-41 5-5 10-7 16-7 1-2 3-7 12-11 0 0 5 3 7 16 4 2 8 5 12 9s7 8 9 12c14 3 17 7 17 7-4 8-9 11-11 11 0 6-2 11-7 16-16 16-30 10-41 2 0 3-1 7-5 11l-12 11c-1 1 1 5 1 5z"/>
</svg>

Before

Width:  |  Height:  |  Size: 641 B

View File

@ -1,4 +1,4 @@
import * as jpw from "@browser/jsonpath-wasm";
import * as jpw from "jsonpath-wasm";
import * as jp from "jsonpath/jsonpath.js";
function run(message, iter, cb) {
@ -61,9 +61,6 @@ let path = '$..book[?(@.price<30 && @.category=="fiction")]';
let template = jpw.compile(path);
let selector = jpw.selector(json);
let ptr = jpw.allocJson(json);
if(ptr == 0) console.error('invalid ptr');
let iterCount = 2000;
run('jsonpath', iterCount, function() { jp.query(json, path) })
@ -73,17 +70,7 @@ run('jsonpath', iterCount, function() { jp.query(json, path) })
.then(function() {
return run('jsonpath-wasm- compile', iterCount, function() { template(json) });
})
.then(function() {
return run('jsonpath-wasm- compile-alloc', iterCount, function() { template(ptr) });
})
.then(function() {
return run('jsonpath-wasm- select', iterCount, function() { jpw.select(json, path) });
})
.then(function() {
return run('jsonpath-wasm- select-alloc', iterCount, function() { jpw.select(ptr, path) });
})
.finally(function() {
if(!jpw.deallocJson(ptr)) {
console.error('fail to dealloc');
}
});
.finally(function() {});

View File

@ -1,6 +1,6 @@
{
"name": "jsonpath-wasm",
"version": "0.1.0",
"name": "jsonpath-wasm-bench",
"version": "0.2.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,28 +1,11 @@
{
"name": "jsonpath-wasm",
"version": "0.1.0",
"name": "jsonpath-wasm-bench",
"version": "0.2.0",
"main": "index.js",
"scripts": {
"build": "webpack --config webpack.config.js",
"start": "webpack-dev-server"
},
"repository": {
"type": "git",
"url": "git+https://github.com/freestrings/jsonpath.git"
},
"keywords": [
"webassembly",
"wasm",
"rust",
"webpack",
"jsonpath"
],
"author": "Changseok Han <freestrings@gmail.com>",
"license": "(MIT OR Apache-2.0)",
"bugs": {
"url": "https://github.com/freestrings/jsonpath/issues"
},
"homepage": "https://github.com/freestrings/jsonpath#readme",
"devDependencies": {
"copy-webpack-plugin": "^5.0.0",
"webpack": "^4.29.6",