mirror of
https://github.com/fluencelabs/jsonpath
synced 2025-05-07 06:52:13 +00:00
json_filter 기본
This commit is contained in:
parent
8d16051b80
commit
6674cb3763
@ -8,3 +8,6 @@ log = "0.4"
|
||||
env_logger = "0.6.0"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
bencher = "0.1.5"
|
38
benches/bench.rs
Normal file
38
benches/bench.rs
Normal file
@ -0,0 +1,38 @@
|
||||
#![feature(test)]
|
||||
extern crate rs_jsonpath;
|
||||
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
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_a(b: &mut Bencher) {
|
||||
let string = read_json("./benches/data_array.json");
|
||||
let v: Value = serde_json::from_str(string.as_str()).unwrap();
|
||||
b.iter(move || {
|
||||
for _ in 1..1000 {
|
||||
let _ = v.clone();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_b(b: &mut Bencher) {
|
||||
let string = read_json("./benches/data_array.json");
|
||||
b.iter(move || {
|
||||
for _ in 1..1000 {
|
||||
let _: Value = serde_json::from_str(string.as_str()).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
92
benches/data_array.json
Normal file
92
benches/data_array.json
Normal file
@ -0,0 +1,92 @@
|
||||
[
|
||||
{
|
||||
"_id": "5741cfe6bf9f447a509a269e",
|
||||
"index": 0,
|
||||
"guid": "642f0c2a-3d87-43ac-8f82-25f004e0c96a",
|
||||
"isActive": false,
|
||||
"balance": "$3,666.68",
|
||||
"picture": "http://placehold.it/32x32",
|
||||
"age": 39,
|
||||
"eyeColor": "blue",
|
||||
"name": "Leonor Herman",
|
||||
"gender": "female",
|
||||
"company": "RODEOMAD",
|
||||
"email": "leonorherman@rodeomad.com",
|
||||
"phone": "+1 (848) 456-2962",
|
||||
"address": "450 Seeley Street, Iberia, North Dakota, 7859",
|
||||
"about": "Reprehenderit in anim laboris labore sint occaecat labore proident ipsum exercitation. Ut ea aliqua duis occaecat consectetur aliqua anim id. Dolor ea fugiat excepteur reprehenderit eiusmod enim non sit nisi. Mollit consequat anim mollit et excepteur qui laborum qui eiusmod. Qui ea amet incididunt cillum quis occaecat excepteur qui duis nisi. Dolore labore eu sunt consequat magna.\r\n",
|
||||
"registered": "2015-03-06T02:49:06 -02:00",
|
||||
"latitude": -29.402032,
|
||||
"longitude": 151.088135,
|
||||
"tags": [
|
||||
"Lorem",
|
||||
"voluptate",
|
||||
"aute",
|
||||
"ullamco",
|
||||
"elit",
|
||||
"esse",
|
||||
"culpa"
|
||||
],
|
||||
"friends": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "Millicent Norman"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Vincent Cannon"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Gray Berry"
|
||||
}
|
||||
],
|
||||
"greeting": "Hello, Leonor Herman! You have 4 unread messages.",
|
||||
"favoriteFruit": "apple"
|
||||
},
|
||||
{
|
||||
"_id": "5741cfe69424f42d4493caa2",
|
||||
"index": 1,
|
||||
"guid": "40ec6b43-e6e6-44e1-92a8-dc80cd5d7179",
|
||||
"isActive": true,
|
||||
"balance": "$2,923.78",
|
||||
"picture": "http://placehold.it/32x32",
|
||||
"age": 36,
|
||||
"eyeColor": "blue",
|
||||
"name": "Barton Barnes",
|
||||
"gender": "male",
|
||||
"company": "BRAINQUIL",
|
||||
"email": "bartonbarnes@brainquil.com",
|
||||
"phone": "+1 (907) 553-3739",
|
||||
"address": "644 Falmouth Street, Sedley, Michigan, 5602",
|
||||
"about": "Et nulla laboris consectetur laborum labore. Officia dolor sint do amet excepteur dolore eiusmod. Occaecat pariatur sunt velit sunt ullamco labore commodo mollit sint dolore occaecat.\r\n",
|
||||
"registered": "2014-08-28T01:07:22 -03:00",
|
||||
"latitude": 14.056553,
|
||||
"longitude": -61.911624,
|
||||
"tags": [
|
||||
"laboris",
|
||||
"sunt",
|
||||
"esse",
|
||||
"tempor",
|
||||
"pariatur",
|
||||
"occaecat",
|
||||
"et"
|
||||
],
|
||||
"friends": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "Tillman Mckay"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Rivera Berg"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Rosetta Erickson"
|
||||
}
|
||||
],
|
||||
"greeting": "Hello, Barton Barnes! You have 2 unread messages.",
|
||||
"favoriteFruit": "banana"
|
||||
}
|
||||
]
|
61
benches/data_obj.json
Normal file
61
benches/data_obj.json
Normal file
@ -0,0 +1,61 @@
|
||||
{
|
||||
"_id": "5741cfe6bf9f447a509a269e",
|
||||
"index": 0,
|
||||
"guid": "642f0c2a-3d87-43ac-8f82-25f004e0c96a",
|
||||
"isActive": false,
|
||||
"balance": "$3,666.68",
|
||||
"picture": "http://placehold.it/32x32",
|
||||
"age": 39,
|
||||
"eyeColor": "blue",
|
||||
"name": "Leonor Herman",
|
||||
"gender": "female",
|
||||
"company": "RODEOMAD",
|
||||
"email": "leonorherman@rodeomad.com",
|
||||
"phone": "+1 (848) 456-2962",
|
||||
"address": "450 Seeley Street, Iberia, North Dakota, 7859",
|
||||
"about": "Reprehenderit in anim laboris labore sint occaecat labore proident ipsum exercitation. Ut ea aliqua duis occaecat consectetur aliqua anim id. Dolor ea fugiat excepteur reprehenderit eiusmod enim non sit nisi. Mollit consequat anim mollit et excepteur qui laborum qui eiusmod. Qui ea amet incididunt cillum quis occaecat excepteur qui duis nisi. Dolore labore eu sunt consequat magna.\r\n",
|
||||
"registered": "2015-03-06T02:49:06 -02:00",
|
||||
"latitude": -29.402032,
|
||||
"longitude": 151.088135,
|
||||
"tags": [
|
||||
"Lorem",
|
||||
"voluptate",
|
||||
"aute",
|
||||
"ullamco",
|
||||
"elit",
|
||||
"esse",
|
||||
"culpa"
|
||||
],
|
||||
"school": {
|
||||
"friends": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "Millicent Norman"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Vincent Cannon"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Gray Berry"
|
||||
}
|
||||
]
|
||||
},
|
||||
"friends": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "Millicent Norman"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Vincent Cannon"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Gray Berry"
|
||||
}
|
||||
],
|
||||
"greeting": "Hello, Leonor Herman! You have 4 unread messages.",
|
||||
"favoriteFruit": "apple"
|
||||
}
|
772
src/jsonpath/json_filter.rs
Normal file
772
src/jsonpath/json_filter.rs
Normal file
@ -0,0 +1,772 @@
|
||||
use core::borrow::Borrow;
|
||||
use std::cmp::Ordering;
|
||||
use std::error::Error;
|
||||
use std::io::Read;
|
||||
use std::rc::Rc;
|
||||
use std::result;
|
||||
|
||||
use serde_json::Value;
|
||||
use serde_json::value::Index;
|
||||
|
||||
use jsonpath::parser::{
|
||||
FilterToken,
|
||||
NodeVisitor,
|
||||
Parser,
|
||||
ParseToken,
|
||||
};
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
enum CmpType {
|
||||
Eq,
|
||||
Ne,
|
||||
Gt,
|
||||
Ge,
|
||||
Lt,
|
||||
Le,
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
trait IntoType {
|
||||
fn into_type(&self) -> CmpType;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ExprTerm {
|
||||
String(String),
|
||||
Number(f64),
|
||||
Bool(bool),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum TermContext {
|
||||
Constants(ExprTerm),
|
||||
Json(Vec<Value>),
|
||||
}
|
||||
|
||||
impl TermContext {
|
||||
fn cmp_value_term<'a, F: PrivCmp>(et: &'a ExprTerm, cmp_fn: F, default: bool)
|
||||
-> impl FnMut(&&'a mut Value) -> bool {
|
||||
move |v| match v {
|
||||
Value::Bool(ref v1) => {
|
||||
match et {
|
||||
ExprTerm::Bool(v2) => cmp_fn.cmp_bool(v1, v2),
|
||||
_ => default
|
||||
}
|
||||
}
|
||||
Value::Number(ref v1) => match v1.as_f64() {
|
||||
Some(ref v1) => {
|
||||
match et {
|
||||
ExprTerm::Number(v2) => cmp_fn.cmp_f64(v1, v2),
|
||||
_ => default
|
||||
}
|
||||
}
|
||||
_ => default
|
||||
},
|
||||
Value::String(ref v1) => {
|
||||
match et {
|
||||
ExprTerm::String(v2) => cmp_fn.cmp_string(v1, v2),
|
||||
_ => default
|
||||
}
|
||||
}
|
||||
_ => default
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp_values_term<F: PrivCmp>(v: &mut Vec<Value>, et: &ExprTerm, cmp: F) -> TermContext {
|
||||
let ret = v.iter_mut()
|
||||
.filter(Self::cmp_value_term(et, cmp, false))
|
||||
.map(|v| v.take())
|
||||
.collect();
|
||||
TermContext::Json(ret)
|
||||
}
|
||||
|
||||
fn cmp_term_term<F: PrivCmp>(v1: &ExprTerm, v2: &ExprTerm, cmp_fn: F, default: bool) -> bool {
|
||||
match v1 {
|
||||
ExprTerm::Bool(vv1) => match v2 {
|
||||
ExprTerm::Bool(vv2) => cmp_fn.cmp_bool(vv1, vv2),
|
||||
_ => default
|
||||
}
|
||||
ExprTerm::Number(vv1) => match v2 {
|
||||
ExprTerm::Number(vv2) => cmp_fn.cmp_f64(vv1, vv2),
|
||||
_ => default
|
||||
}
|
||||
ExprTerm::String(vv1) => match v2 {
|
||||
ExprTerm::String(vv2) => cmp_fn.cmp_string(vv1, vv2),
|
||||
_ => default
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp_value_value(v1: &mut Vec<Value>, v2: &mut Vec<Value>, cmp_type: CmpType) -> TermContext {
|
||||
match cmp_type {
|
||||
CmpType::Eq => {
|
||||
let mut map: HashMap<String, Value> = HashMap::new();
|
||||
for v in v1 {
|
||||
map.insert(format!("{:?}", v), v.take());
|
||||
}
|
||||
|
||||
let mut ret: HashMap<String, Value> = HashMap::new();
|
||||
for v in v2 {
|
||||
let key = format!("{:?}", v);
|
||||
if map.contains_key(&key) {
|
||||
ret.insert(key, v.take());
|
||||
}
|
||||
}
|
||||
|
||||
let v = ret.values_mut().into_iter().map(|v| v.take()).collect();
|
||||
TermContext::Json(v)
|
||||
}
|
||||
CmpType::Ne => {
|
||||
let mut map: HashMap<String, Value> = HashMap::new();
|
||||
for v in v1 {
|
||||
map.insert(format!("{:?}", v), v.take());
|
||||
}
|
||||
|
||||
let mut ret: HashMap<String, Value> = HashMap::new();
|
||||
for v in v2 {
|
||||
let key = format!("{:?}", v);
|
||||
if !map.contains_key(&key) {
|
||||
ret.insert(key, v.take());
|
||||
}
|
||||
}
|
||||
|
||||
let v = ret.values_mut().into_iter().map(|v| v.take()).collect();
|
||||
TermContext::Json(v)
|
||||
}
|
||||
CmpType::Gt | CmpType::Ge | CmpType::Lt | CmpType::Le => {
|
||||
TermContext::Constants(ExprTerm::Bool(false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp<F: PrivCmp + IntoType>(&mut self, other: &mut TermContext, cmp_fn: F, default: bool) -> TermContext {
|
||||
match self {
|
||||
TermContext::Constants(et) => {
|
||||
match other {
|
||||
TermContext::Constants(oet) => {
|
||||
let b = Self::cmp_term_term(et, oet, cmp_fn, default);
|
||||
TermContext::Constants(ExprTerm::Bool(b))
|
||||
}
|
||||
TermContext::Json(v) => {
|
||||
Self::cmp_values_term(v, et, cmp_fn)
|
||||
}
|
||||
}
|
||||
}
|
||||
TermContext::Json(v) => {
|
||||
match other {
|
||||
TermContext::Json(ov) => {
|
||||
Self::cmp_value_value(v, ov, cmp_fn.into_type())
|
||||
}
|
||||
TermContext::Constants(et) => {
|
||||
Self::cmp_values_term(v, et, cmp_fn)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn eq(&mut self, other: &mut TermContext) -> TermContext {
|
||||
self.cmp(other, CmpEq, false)
|
||||
}
|
||||
|
||||
fn ne(&mut self, other: &mut TermContext) -> TermContext {
|
||||
self.cmp(other, CmpNe, true)
|
||||
}
|
||||
|
||||
fn gt(&mut self, other: &mut TermContext) -> TermContext {
|
||||
self.cmp(other, CmpGt, false)
|
||||
}
|
||||
|
||||
fn ge(&mut self, other: &mut TermContext) -> TermContext {
|
||||
self.cmp(other, CmpGe, false)
|
||||
}
|
||||
|
||||
fn lt(&mut self, other: &mut TermContext) -> TermContext {
|
||||
self.cmp(other, CmpLt, false)
|
||||
}
|
||||
|
||||
fn le(&mut self, other: &mut TermContext) -> TermContext {
|
||||
self.cmp(other, CmpLe, false)
|
||||
}
|
||||
|
||||
fn cmp_cond<F: PrivCmp>(&mut self, other: &mut TermContext, cmp_fn: F) -> TermContext {
|
||||
match self {
|
||||
TermContext::Constants(et) => {
|
||||
match other {
|
||||
TermContext::Constants(oet) => {
|
||||
let b = Self::cmp_term_term(et, oet, cmp_fn, false);
|
||||
TermContext::Constants(ExprTerm::Bool(b))
|
||||
}
|
||||
TermContext::Json(v) => {
|
||||
let list = v.iter_mut().map(|v| v.take()).collect();
|
||||
TermContext::Json(list)
|
||||
}
|
||||
}
|
||||
}
|
||||
TermContext::Json(v) => {
|
||||
match other {
|
||||
TermContext::Json(ov) => {
|
||||
let mut map: HashMap<String, Value> = HashMap::new();
|
||||
for val in v {
|
||||
map.insert(format!("{:?}", val), val.take());
|
||||
}
|
||||
for val in ov {
|
||||
map.insert(format!("{:?}", val), val.take());
|
||||
}
|
||||
let list: Vec<Value> = map.values_mut().into_iter().map(|val| val.take()).collect();
|
||||
TermContext::Json(list)
|
||||
}
|
||||
TermContext::Constants(et) => {
|
||||
let list = v.iter_mut().map(|v| v.take()).collect();
|
||||
TermContext::Json(list)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn and(&mut self, other: &mut TermContext) -> TermContext {
|
||||
self.cmp_cond(other, CmpAnd)
|
||||
}
|
||||
|
||||
fn or(&mut self, other: &mut TermContext) -> TermContext {
|
||||
self.cmp_cond(other, CmpOr)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JsonValueFilter {
|
||||
json: Rc<Box<Value>>,
|
||||
current: Vec<Value>,
|
||||
stack: Vec<ParseToken>,
|
||||
filter_stack: Vec<TermContext>,
|
||||
in_array: bool,
|
||||
}
|
||||
|
||||
impl NodeVisitor for JsonValueFilter {
|
||||
fn visit_token(&mut self, token: ParseToken) {
|
||||
debug!("visit_token: {:?}", token);
|
||||
match token {
|
||||
ParseToken::Absolute
|
||||
| ParseToken::Relative
|
||||
| ParseToken::In
|
||||
| ParseToken::Leaves => {
|
||||
self.stack.push(token);
|
||||
}
|
||||
ParseToken::Array => {
|
||||
self.in_array = true;
|
||||
}
|
||||
ParseToken::ArrayEof => {
|
||||
self.in_array = false;
|
||||
match self.filter_stack.pop() {
|
||||
Some(TermContext::Constants(_)) => unreachable!(),
|
||||
Some(TermContext::Json(v)) => self.current = v,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if !self.filter_stack.is_empty() {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
ParseToken::All => {
|
||||
if self.in_array {
|
||||
self.stack.push(token);
|
||||
} else {
|
||||
match self.stack.pop() {
|
||||
Some(ParseToken::In) => {
|
||||
self.step_in_all();
|
||||
}
|
||||
Some(ParseToken::Leaves) => {
|
||||
self.step_leaves_all();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
ParseToken::Key(key) => {
|
||||
if self.in_array {
|
||||
self.stack.push(ParseToken::Key(key));
|
||||
} else {
|
||||
match self.stack.pop() {
|
||||
Some(ParseToken::In) => {
|
||||
self.step_in(key);
|
||||
}
|
||||
Some(ParseToken::Leaves) => {
|
||||
self.step_leaves(key);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if match self.stack.last() {
|
||||
Some(ParseToken::Absolute) | Some(ParseToken::Relative) => true,
|
||||
_ => false
|
||||
} {
|
||||
self.stack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
ParseToken::Number(_) => {
|
||||
self.stack.push(token);
|
||||
}
|
||||
ParseToken::Filter(ref ft) => {
|
||||
let left = self.filter_stack.pop();
|
||||
let right = self.filter_stack.pop();
|
||||
|
||||
trace!("left {:?}", left);
|
||||
trace!("right {:?}", right);
|
||||
|
||||
if let Some(mut left) = left {
|
||||
if let Some(mut right) = right {
|
||||
let tc = match ft {
|
||||
FilterToken::Equal => left.eq(&mut right),
|
||||
FilterToken::NotEqual => left.ne(&mut right),
|
||||
FilterToken::Greater => left.gt(&mut right),
|
||||
FilterToken::GreaterOrEqual => left.ge(&mut right),
|
||||
FilterToken::Little => left.lt(&mut right),
|
||||
FilterToken::LittleOrEqual => left.le(&mut right),
|
||||
FilterToken::And => left.and(&mut right),
|
||||
FilterToken::Or => left.or(&mut right),
|
||||
};
|
||||
self.filter_stack.push(tc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
other => {
|
||||
debug!("visit_token other: {:?}", other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clean_filter_context(&mut self) {
|
||||
debug!("clean_filter_context");
|
||||
self.clean_filter_path();
|
||||
self.clean_filter_constants();
|
||||
}
|
||||
}
|
||||
|
||||
impl JsonValueFilter {
|
||||
pub fn new(json: &str) -> result::Result<Self, String> {
|
||||
let json: Value = serde_json::from_str(json)
|
||||
.map_err(|e| e.description().to_string())?;
|
||||
let root = json.clone();
|
||||
Ok(JsonValueFilter {
|
||||
json: Rc::new(Box::new(json)),
|
||||
current: vec![root],
|
||||
stack: Vec::new(),
|
||||
filter_stack: Vec::new(),
|
||||
in_array: false,
|
||||
})
|
||||
}
|
||||
|
||||
fn fork(&self, from_current: bool) -> Self {
|
||||
JsonValueFilter {
|
||||
json: self.json.clone(),
|
||||
current: if from_current {
|
||||
self.current.clone()
|
||||
} else {
|
||||
let v: &Value = self.json.as_ref().borrow();
|
||||
vec![v.clone()]
|
||||
},
|
||||
stack: Vec::new(),
|
||||
filter_stack: Vec::new(),
|
||||
in_array: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn clean_filter_path(&mut self) {
|
||||
let mut paths = Vec::new();
|
||||
|
||||
loop {
|
||||
trace!("clean_filter_path - loop: {:?}", self.stack.last());
|
||||
|
||||
if match self.stack.last() {
|
||||
Some(ParseToken::Absolute)
|
||||
| Some(ParseToken::Relative)
|
||||
| Some(ParseToken::In)
|
||||
| Some(ParseToken::Leaves)
|
||||
| Some(ParseToken::All)
|
||||
| Some(ParseToken::Key(_)) => true,
|
||||
_ => false
|
||||
} {
|
||||
self.stack.pop().map(|t| paths.push(t));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
trace!("clean_filter_path: {:?}", paths);
|
||||
|
||||
if let Some(forked) = match paths.pop() {
|
||||
Some(ParseToken::Absolute) => {
|
||||
Some(self.fork(false))
|
||||
}
|
||||
Some(ParseToken::Relative) => {
|
||||
Some(self.fork(true))
|
||||
}
|
||||
_ => None
|
||||
}.and_then(|mut forked| {
|
||||
while let Some(t) = paths.pop() {
|
||||
forked.visit_token(t);
|
||||
}
|
||||
Some(forked)
|
||||
}) {
|
||||
trace!("clean_filter_path -> {:?}", forked.current);
|
||||
self.filter_stack.push(TermContext::Json(forked.current));
|
||||
}
|
||||
}
|
||||
|
||||
fn clean_filter_constants(&mut self) {
|
||||
trace!("clean_filter_constants: {:?}", self.stack.last());
|
||||
|
||||
if match self.stack.last() {
|
||||
Some(ParseToken::Key(_))
|
||||
| Some(ParseToken::Number(_)) => true,
|
||||
_ => false
|
||||
} {
|
||||
match self.stack.pop() {
|
||||
Some(ParseToken::Key(ref v)) if v.eq_ignore_ascii_case("true") => {
|
||||
self.filter_stack.push(TermContext::Constants(ExprTerm::Bool(true)))
|
||||
}
|
||||
Some(ParseToken::Key(ref v)) if v.eq_ignore_ascii_case("false") => {
|
||||
self.filter_stack.push(TermContext::Constants(ExprTerm::Bool(false)))
|
||||
}
|
||||
Some(ParseToken::Key(v)) => {
|
||||
self.filter_stack.push(TermContext::Constants(ExprTerm::String(v)))
|
||||
}
|
||||
Some(ParseToken::Number(v)) => {
|
||||
self.filter_stack.push(TermContext::Constants(ExprTerm::Number(v)))
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn step_leaves_all(&mut self) -> &Vec<Value> {
|
||||
debug!("step_leaves_all");
|
||||
|
||||
let mut buf = Vec::new();
|
||||
loop {
|
||||
self.step_in_all().iter().map(|v| buf.push(v.clone()));
|
||||
if self.current.len() == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.current = buf;
|
||||
&self.current
|
||||
}
|
||||
|
||||
fn step_leaves(&mut self, key: String) -> &Vec<Value> {
|
||||
debug!("step_leaves");
|
||||
|
||||
let mut buf = Vec::new();
|
||||
loop {
|
||||
self.step_in(key.clone()).iter().map(|v| buf.push(v.clone()));
|
||||
if self.current.len() == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.current = buf;
|
||||
&self.current
|
||||
}
|
||||
|
||||
fn step_in_all(&mut self) -> &Vec<Value> {
|
||||
debug!("step_in_all");
|
||||
|
||||
fn to_vec<'a, I: Iterator<Item=&'a mut Value>>(iter: I) -> Vec<Value> {
|
||||
iter.map(|v| v.take())
|
||||
.filter(|v| !v.is_null())
|
||||
.collect()
|
||||
}
|
||||
|
||||
self.current = self.current.iter_mut()
|
||||
.flat_map(|v| {
|
||||
match v {
|
||||
Value::Object(map) => to_vec(map.values_mut()),
|
||||
Value::Array(list) => to_vec(list.iter_mut()),
|
||||
Value::Null => Vec::new(),
|
||||
_ => vec![v.take()]
|
||||
}
|
||||
}).collect();
|
||||
|
||||
&self.current
|
||||
}
|
||||
|
||||
fn step_in<I: Index>(&mut self, key: I) -> &Vec<Value> {
|
||||
debug!("step_in");
|
||||
self.current = self.current.iter_mut()
|
||||
.map(|v| {
|
||||
trace!("step_in - map: {:?}", v);
|
||||
match v.get_mut(&key) {
|
||||
Some(value) => value.take(),
|
||||
_ => Value::Null
|
||||
}
|
||||
})
|
||||
.filter(|v| !v.is_null())
|
||||
.collect();
|
||||
|
||||
&self.current
|
||||
}
|
||||
|
||||
fn current(&self) -> &Vec<Value> {
|
||||
&self.current
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
extern crate env_logger;
|
||||
|
||||
use std::sync::{Once, ONCE_INIT};
|
||||
|
||||
use jsonpath::tokenizer::PreloadedTokenizer;
|
||||
|
||||
use super::*;
|
||||
|
||||
static INIT: Once = ONCE_INIT;
|
||||
|
||||
fn setup() {
|
||||
INIT.call_once(|| {
|
||||
env_logger::init();
|
||||
});
|
||||
}
|
||||
|
||||
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 string = read_json("./benches/data_obj.json");
|
||||
let mut jf = JsonValueFilter::new(string.as_str()).unwrap();
|
||||
{
|
||||
let current = jf.step_in("friends");
|
||||
assert_eq!(current[0].is_array(), true);
|
||||
}
|
||||
|
||||
let string = read_json("./benches/data_array.json");
|
||||
let mut jf = JsonValueFilter::new(string.as_str()).unwrap();
|
||||
{
|
||||
let current = jf.step_in(1);
|
||||
assert_eq!(current[0].is_object(), true);
|
||||
}
|
||||
{
|
||||
let current = jf.step_in("friends");
|
||||
assert_eq!(current[0].is_array(), true);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fork() {
|
||||
setup();
|
||||
|
||||
let string = read_json("./benches/data_obj.json");
|
||||
let mut jf = JsonValueFilter::new(string.as_str()).unwrap();
|
||||
{
|
||||
let current = jf.step_in("friends");
|
||||
assert_eq!(current[0].is_array(), true);
|
||||
}
|
||||
|
||||
let jf_from_current = jf.fork(true);
|
||||
{
|
||||
let current = jf_from_current.current();
|
||||
assert_eq!(current[0].is_array(), true);
|
||||
}
|
||||
|
||||
let mut jf_from_root = jf_from_current.fork(false);
|
||||
{
|
||||
let current = jf_from_root.step_in("age");
|
||||
assert_eq!(current[0].is_number(), true);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter() {
|
||||
setup();
|
||||
|
||||
let string = read_json("./benches/data_obj.json");
|
||||
let mut jf = JsonValueFilter::new(string.as_str()).unwrap();
|
||||
let mut parser = Parser::new("$.school[?(@.friends==@.friends)]");
|
||||
parser.parse(&mut jf).unwrap();
|
||||
let v = json!([
|
||||
{"id": 0,"name": "Millicent Norman"},
|
||||
{"id": 1,"name": "Vincent Cannon" },
|
||||
{"id": 2,"name": "Gray Berry"}
|
||||
]);
|
||||
assert_eq!(v, jf.current());
|
||||
}
|
||||
}
|
@ -1,27 +1,12 @@
|
||||
pub mod path_reader;
|
||||
pub mod tokenizer;
|
||||
pub mod parser;
|
||||
mod path_reader;
|
||||
mod tokenizer;
|
||||
mod parser;
|
||||
mod json_filter;
|
||||
mod utils;
|
||||
|
||||
mod utils {
|
||||
use std::result;
|
||||
use std::result;
|
||||
|
||||
pub fn vec_to_int<F>(vec: &Vec<char>, msg_handler: F) -> result::Result<isize, String>
|
||||
where F: Fn() -> String {
|
||||
match vec.iter().map(|c| *c).collect::<String>().as_str().parse::<isize>() {
|
||||
Ok(n) => Ok(n),
|
||||
_ => Err(msg_handler())
|
||||
}
|
||||
}
|
||||
fn read(json: &str, path: &str) -> result::Result<(), String> {
|
||||
|
||||
pub fn vec_to_float<F>(vec: &Vec<char>, msg_handler: F) -> result::Result<f64, String>
|
||||
where F: Fn() -> String {
|
||||
match vec.iter().map(|c| *c).collect::<String>().as_str().parse::<f64>() {
|
||||
Ok(n) => Ok(n),
|
||||
_ => Err(msg_handler())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vec_to_string(vec: &Vec<char>) -> String {
|
||||
vec.iter().map(|c| *c).collect::<String>()
|
||||
}
|
||||
Ok(())
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
use std::result;
|
||||
|
||||
use super::tokenizer::{
|
||||
self,
|
||||
Token,
|
||||
PreloadedTokenizer,
|
||||
TokenError,
|
||||
@ -13,7 +12,7 @@ const DUMMY: usize = 0;
|
||||
type Result<T> = result::Result<T, String>;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum ParseToken {
|
||||
pub enum ParseToken {
|
||||
// '$'
|
||||
Absolute,
|
||||
// '@'
|
||||
@ -43,33 +42,37 @@ enum ParseToken {
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum FilterToken {
|
||||
pub enum FilterToken {
|
||||
Equal,
|
||||
NotEqual,
|
||||
Little,
|
||||
LittleOrEqual,
|
||||
Greater,
|
||||
GreaterOrEqual,
|
||||
Literal(String),
|
||||
Number(f64),
|
||||
And,
|
||||
Or,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Node {
|
||||
pub struct Node {
|
||||
left: Option<Box<Node>>,
|
||||
right: Option<Box<Node>>,
|
||||
token: ParseToken,
|
||||
}
|
||||
|
||||
struct Parser<'a> {
|
||||
pub struct Parser<'a> {
|
||||
tokenizer: PreloadedTokenizer<'a>
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
fn parse(&mut self) -> Result<Node> {
|
||||
self.json_path()
|
||||
pub fn new(input: &'a str) -> Self {
|
||||
Parser { tokenizer: PreloadedTokenizer::new(input) }
|
||||
}
|
||||
|
||||
pub fn parse<V: NodeVisitor>(&mut self, visitor: &mut V) -> Result<()> {
|
||||
let node = self.json_path()?;
|
||||
visitor.visit(node);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn json_path(&mut self) -> Result<Node> {
|
||||
@ -85,7 +88,7 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn paths(&mut self, mut prev: Node) -> Result<Node> {
|
||||
fn paths(&mut self, prev: Node) -> Result<Node> {
|
||||
debug!("#paths");
|
||||
match self.tokenizer.peek_token() {
|
||||
Ok(Token::Dot(_)) => {
|
||||
@ -441,9 +444,30 @@ impl<'a> Parser<'a> {
|
||||
|
||||
fn expr(&mut self) -> Result<Node> {
|
||||
debug!("#expr");
|
||||
|
||||
let has_prop_candidate = match self.tokenizer.peek_token() {
|
||||
Ok(Token::At(_)) => true,
|
||||
_ => false
|
||||
};
|
||||
|
||||
let node = self.term()?;
|
||||
self.eat_whitespace();
|
||||
self.op(node)
|
||||
|
||||
if match self.tokenizer.peek_token() {
|
||||
Ok(Token::Equal(_))
|
||||
| Ok(Token::NotEqual(_))
|
||||
| Ok(Token::Little(_))
|
||||
| Ok(Token::LittleOrEqual(_))
|
||||
| Ok(Token::Greater(_))
|
||||
| Ok(Token::GreaterOrEqual(_)) => true,
|
||||
_ => false
|
||||
} {
|
||||
self.op(node)
|
||||
} else if has_prop_candidate {
|
||||
Ok(node)
|
||||
} else {
|
||||
return Err(self.tokenizer.err_msg());
|
||||
}
|
||||
}
|
||||
|
||||
fn term_num(&mut self) -> Result<Node> {
|
||||
@ -574,7 +598,7 @@ impl<'a> Parser<'a> {
|
||||
Node { left: None, right: None, token: token }
|
||||
}
|
||||
|
||||
fn close_token(&mut self, mut ret: Node, token: Token) -> Result<Node> {
|
||||
fn close_token(&mut self, ret: Node, token: Token) -> Result<Node> {
|
||||
debug!("#close_token");
|
||||
match self.tokenizer.next_token() {
|
||||
Ok(ref t) if t.partial_eq(token) => {
|
||||
@ -583,158 +607,59 @@ impl<'a> Parser<'a> {
|
||||
Err(TokenError::Eof) => {
|
||||
Ok(ret)
|
||||
}
|
||||
other => {
|
||||
_ => {
|
||||
Err(self.tokenizer.err_msg())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait NodeVisitor {
|
||||
fn visit(&mut self, node: Node);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum InterpreterToken {
|
||||
Path(Vec<ParseToken>, ParseToken),
|
||||
Token(ParseToken),
|
||||
}
|
||||
|
||||
struct Interpreter<'a> {
|
||||
input: &'a str,
|
||||
stack: Vec<Vec<ParseToken>>,
|
||||
}
|
||||
|
||||
impl<'a> Interpreter<'a> {
|
||||
fn new(input: &'a str) -> Self {
|
||||
Interpreter { input, stack: vec![] }
|
||||
}
|
||||
|
||||
fn interpret(&mut self) -> result::Result<Vec<Vec<ParseToken>>, String> {
|
||||
let tokenizer = PreloadedTokenizer::new(self.input);
|
||||
let mut parser = Parser { tokenizer };
|
||||
let node = parser.parse()?;
|
||||
self.visit(node);
|
||||
self.flush_path();
|
||||
self.flush_meta();
|
||||
Ok(self.stack.split_off(0))
|
||||
}
|
||||
|
||||
fn flush_path(&mut self) {
|
||||
let mut buf = vec![];
|
||||
|
||||
loop {
|
||||
if !match self.stack.last() {
|
||||
Some(n) if n.len() == 1 => {
|
||||
match n[0] {
|
||||
ParseToken::Absolute
|
||||
| ParseToken::Relative
|
||||
| ParseToken::In
|
||||
| ParseToken::Leaves
|
||||
| ParseToken::All
|
||||
| ParseToken::Key(_) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
_ => false
|
||||
} {
|
||||
break;
|
||||
}
|
||||
|
||||
self.stack.pop().map(|mut e| buf.insert(0, e.pop().unwrap()));
|
||||
}
|
||||
|
||||
if buf.len() > 0 {
|
||||
self.flush_meta();
|
||||
self.stack.push(buf);
|
||||
}
|
||||
}
|
||||
|
||||
fn flush_expr(&mut self) {
|
||||
if match self.stack.last() {
|
||||
Some(n) if n.len() == 1 => {
|
||||
match n[0] {
|
||||
ParseToken::Filter(FilterToken::Equal)
|
||||
| ParseToken::Filter(FilterToken::NotEqual)
|
||||
| ParseToken::Filter(FilterToken::Little)
|
||||
| ParseToken::Filter(FilterToken::LittleOrEqual)
|
||||
| ParseToken::Filter(FilterToken::Greater)
|
||||
| ParseToken::Filter(FilterToken::GreaterOrEqual) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
_ => false
|
||||
} {
|
||||
let mut op = self.stack.pop().unwrap();
|
||||
let mut term = self.stack.pop().unwrap();
|
||||
self.stack.last_mut().map(|v| {
|
||||
v.append(&mut op);
|
||||
v.append(&mut term);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn flush_meta(&mut self) {
|
||||
if match self.stack.last() {
|
||||
Some(t) if t.len() == 1 => {
|
||||
match t.last() {
|
||||
Some(ParseToken::ArrayEof) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
_ => false
|
||||
} {
|
||||
self.stack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> NodeVisitor for Interpreter<'a> {
|
||||
pub trait NodeVisitor {
|
||||
fn visit(&mut self, node: Node) {
|
||||
match node.token {
|
||||
ParseToken::Absolute
|
||||
| ParseToken::Relative
|
||||
| ParseToken::All
|
||||
| ParseToken::Key(_) => {
|
||||
self.stack.push(vec![node.token]);
|
||||
self.visit_token(node.token);
|
||||
}
|
||||
ParseToken::In
|
||||
| ParseToken::Leaves => {
|
||||
node.left.map(|n| self.visit(*n));
|
||||
self.stack.push(vec![node.token]);
|
||||
self.visit_token(node.token);
|
||||
node.right.map(|n| self.visit(*n));
|
||||
}
|
||||
| ParseToken::Range(_, _)
|
||||
| ParseToken::Union(_)
|
||||
| ParseToken::Number(_) => {
|
||||
self.stack.push(vec![node.token]);
|
||||
self.visit_token(node.token);
|
||||
}
|
||||
|
||||
| ParseToken::Array => {
|
||||
node.left.map(|n| self.visit(*n));
|
||||
self.flush_path();
|
||||
self.flush_meta();
|
||||
self.stack.push(vec![node.token]);
|
||||
self.visit_token(node.token);
|
||||
node.right.map(|n| self.visit(*n));
|
||||
self.stack.push(vec![ParseToken::ArrayEof]);
|
||||
self.visit_token(ParseToken::ArrayEof);
|
||||
}
|
||||
ParseToken::Filter(FilterToken::And)
|
||||
| ParseToken::Filter(FilterToken::Or) => {
|
||||
node.left.map(|n| self.visit(*n));
|
||||
node.right.map(|n| self.visit(*n));
|
||||
self.stack.push(vec![node.token]);
|
||||
self.visit_token(node.token);
|
||||
}
|
||||
ParseToken::Filter(_) => {
|
||||
node.left.map(|n| self.visit(*n));
|
||||
self.flush_path();
|
||||
self.clean_filter_context();
|
||||
node.right.map(|n| self.visit(*n));
|
||||
self.flush_path();
|
||||
self.stack.push(vec![node.token]);
|
||||
self.flush_expr();
|
||||
self.clean_filter_context();
|
||||
self.visit_token(node.token);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_token(&mut self, token: ParseToken);
|
||||
fn clean_filter_context(&mut self) {}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -747,15 +672,39 @@ mod tests {
|
||||
|
||||
static INIT: Once = ONCE_INIT;
|
||||
|
||||
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 tokenizer = PreloadedTokenizer::new(self.input);
|
||||
let mut parser = Parser { tokenizer };
|
||||
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() {
|
||||
INIT.call_once(|| {
|
||||
env_logger::init();
|
||||
});
|
||||
}
|
||||
|
||||
fn run(input: &str) -> result::Result<Vec<Vec<ParseToken>>, String> {
|
||||
let mut interpreter = Interpreter::new(input);
|
||||
interpreter.interpret()
|
||||
fn run(input: &str) -> result::Result<Vec<ParseToken>, String> {
|
||||
let mut interpreter = NodeVisitorTestImpl::new(input);
|
||||
interpreter.visit()
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -763,44 +712,39 @@ mod tests {
|
||||
setup();
|
||||
|
||||
assert_eq!(run("$.aa"), Ok(vec![
|
||||
vec![ParseToken::Absolute,
|
||||
ParseToken::In,
|
||||
ParseToken::Key("aa".to_owned())
|
||||
]
|
||||
ParseToken::Absolute,
|
||||
ParseToken::In,
|
||||
ParseToken::Key("aa".to_owned())
|
||||
]));
|
||||
|
||||
assert_eq!(run("$.00.a"), Ok(vec![
|
||||
vec![ParseToken::Absolute,
|
||||
ParseToken::In,
|
||||
ParseToken::Key("00".to_owned()),
|
||||
ParseToken::In,
|
||||
ParseToken::Key("a".to_owned())
|
||||
]
|
||||
ParseToken::Absolute,
|
||||
ParseToken::In,
|
||||
ParseToken::Key("00".to_owned()),
|
||||
ParseToken::In,
|
||||
ParseToken::Key("a".to_owned())
|
||||
]));
|
||||
|
||||
assert_eq!(run("$.00.韓창.seok"), Ok(vec![
|
||||
vec![ParseToken::Absolute,
|
||||
ParseToken::In,
|
||||
ParseToken::Key("00".to_owned()),
|
||||
ParseToken::In,
|
||||
ParseToken::Key("韓창".to_owned()),
|
||||
ParseToken::In,
|
||||
ParseToken::Key("seok".to_owned())
|
||||
]
|
||||
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![
|
||||
vec![ParseToken::Absolute,
|
||||
ParseToken::In,
|
||||
ParseToken::All
|
||||
]
|
||||
ParseToken::Absolute,
|
||||
ParseToken::In,
|
||||
ParseToken::All
|
||||
]));
|
||||
|
||||
assert_eq!(run("$..*"), Ok(vec![
|
||||
vec![ParseToken::Absolute,
|
||||
ParseToken::Leaves,
|
||||
ParseToken::All
|
||||
]
|
||||
ParseToken::Absolute,
|
||||
ParseToken::Leaves,
|
||||
ParseToken::All
|
||||
]));
|
||||
|
||||
match run("$.") {
|
||||
@ -823,110 +767,148 @@ mod tests {
|
||||
fn parse_array() {
|
||||
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![
|
||||
vec![ParseToken::Absolute],
|
||||
vec![ParseToken::Array],
|
||||
vec![ParseToken::All]
|
||||
ParseToken::Absolute,
|
||||
ParseToken::Array,
|
||||
ParseToken::All,
|
||||
ParseToken::ArrayEof
|
||||
]));
|
||||
|
||||
assert_eq!(run("$.a[*]"), Ok(vec![
|
||||
vec![ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned())],
|
||||
vec![ParseToken::Array],
|
||||
vec![ParseToken::All]
|
||||
ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()),
|
||||
ParseToken::Array,
|
||||
ParseToken::All,
|
||||
ParseToken::ArrayEof
|
||||
]));
|
||||
|
||||
assert_eq!(run("$.a[*].가"), Ok(vec![
|
||||
vec![ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned())],
|
||||
vec![ParseToken::Array],
|
||||
vec![ParseToken::All],
|
||||
vec![ParseToken::In, ParseToken::Key("가".to_owned())]
|
||||
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![
|
||||
vec![ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned())],
|
||||
vec![ParseToken::Array],
|
||||
vec![ParseToken::Number(0_f64)],
|
||||
vec![ParseToken::Array],
|
||||
vec![ParseToken::Number(1_f64)]
|
||||
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![
|
||||
vec![ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned())],
|
||||
vec![ParseToken::Array],
|
||||
vec![ParseToken::Union(vec![1, 2])]
|
||||
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![
|
||||
vec![ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned())],
|
||||
vec![ParseToken::Array],
|
||||
vec![ParseToken::Range(Some(10), None)]
|
||||
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![
|
||||
vec![ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned())],
|
||||
vec![ParseToken::Array],
|
||||
vec![ParseToken::Range(None, Some(11))]
|
||||
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![
|
||||
vec![ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned())],
|
||||
vec![ParseToken::Array],
|
||||
vec![ParseToken::Range(Some(-12), Some(13))]
|
||||
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![
|
||||
vec![ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned())],
|
||||
vec![ParseToken::Array],
|
||||
vec![ParseToken::Number(1_f64), ParseToken::Filter(FilterToken::Greater), ParseToken::Number(2_f64)]
|
||||
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![
|
||||
vec![ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned())],
|
||||
vec![ParseToken::Array],
|
||||
vec![ParseToken::Absolute, ParseToken::In, ParseToken::Key("b".to_owned()), ParseToken::Filter(FilterToken::Greater), ParseToken::Number(3_f64)]
|
||||
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![
|
||||
vec![ParseToken::Absolute],
|
||||
vec![ParseToken::Array],
|
||||
vec![ParseToken::Absolute, ParseToken::In, ParseToken::Key("c".to_owned()),
|
||||
ParseToken::Filter(FilterToken::Greater),
|
||||
ParseToken::Relative, ParseToken::In, ParseToken::Key("d".to_owned())],
|
||||
vec![ParseToken::Number(1_f64), ParseToken::Filter(FilterToken::Equal), ParseToken::Number(2_f64)],
|
||||
vec![ParseToken::Filter(FilterToken::And)]
|
||||
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![
|
||||
vec![ParseToken::Absolute],
|
||||
vec![ParseToken::Array],
|
||||
vec![ParseToken::Absolute, ParseToken::In, ParseToken::Key("c".to_owned()),
|
||||
ParseToken::Filter(FilterToken::Greater),
|
||||
ParseToken::Relative, ParseToken::In, ParseToken::Key("d".to_owned())],
|
||||
vec![ParseToken::Number(1_f64), ParseToken::Filter(FilterToken::Equal), ParseToken::Number(2_f64)],
|
||||
vec![ParseToken::Number(3_f64), ParseToken::Filter(FilterToken::GreaterOrEqual), ParseToken::Number(4_f64)],
|
||||
vec![ParseToken::Filter(FilterToken::Or)],
|
||||
vec![ParseToken::Filter(FilterToken::And)]
|
||||
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![
|
||||
vec![ParseToken::Absolute],
|
||||
vec![ParseToken::Array],
|
||||
vec![ParseToken::Relative, ParseToken::In, ParseToken::Key("a".to_owned()),
|
||||
ParseToken::Filter(FilterToken::Little),
|
||||
ParseToken::Relative, ParseToken::In, ParseToken::Key("b".to_owned())]
|
||||
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![
|
||||
vec![ParseToken::Absolute],
|
||||
vec![ParseToken::Array],
|
||||
vec![ParseToken::All],
|
||||
vec![ParseToken::Array],
|
||||
vec![ParseToken::All],
|
||||
vec![ParseToken::Array],
|
||||
vec![ParseToken::All]
|
||||
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("$[]") {
|
||||
@ -960,9 +942,10 @@ mod tests {
|
||||
setup();
|
||||
|
||||
assert_eq!(run("$[?(1.1<2.1)]"), Ok(vec![
|
||||
vec![ParseToken::Absolute],
|
||||
vec![ParseToken::Array],
|
||||
vec![ParseToken::Number(1.1), ParseToken::Filter(FilterToken::Little), ParseToken::Number(2.1)]
|
||||
ParseToken::Absolute,
|
||||
ParseToken::Array,
|
||||
ParseToken::Number(1.1), ParseToken::Number(2.1), ParseToken::Filter(FilterToken::Little),
|
||||
ParseToken::ArrayEof
|
||||
]));
|
||||
|
||||
match run("$[1.1]") {
|
||||
|
@ -6,8 +6,6 @@ use jsonpath::path_reader::{
|
||||
PathReader,
|
||||
};
|
||||
|
||||
use super::utils;
|
||||
|
||||
const ABSOLUTE: &'static str = "$";
|
||||
const DOT: &'static str = ".";
|
||||
const AT: &'static str = "@";
|
||||
@ -31,7 +29,6 @@ const NOT_EQUAL: &'static str = "!=";
|
||||
const AND: &'static str = "&&";
|
||||
const OR: &'static str = "||";
|
||||
const WHITESPACE: &'static str = " ";
|
||||
const EOF: &'static str = "Eof";
|
||||
|
||||
const CH_DOLLA: char = '$';
|
||||
const CH_DOT: char = '.';
|
||||
@ -146,14 +143,12 @@ fn simple_matched_token(ch: char, pos: usize) -> Option<Token> {
|
||||
|
||||
pub struct Tokenizer<'a> {
|
||||
input: PathReader<'a>,
|
||||
token_list: Vec<Token>,
|
||||
}
|
||||
|
||||
impl<'a> Tokenizer<'a> {
|
||||
pub fn new(input: &'a str) -> Self {
|
||||
Tokenizer {
|
||||
input: PathReader::new(input),
|
||||
token_list: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
21
src/jsonpath/utils.rs
Normal file
21
src/jsonpath/utils.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use std::result;
|
||||
|
||||
pub fn vec_to_int<F>(vec: &Vec<char>, msg_handler: F) -> result::Result<isize, String>
|
||||
where F: Fn() -> String {
|
||||
match vec.iter().map(|c| *c).collect::<String>().as_str().parse::<isize>() {
|
||||
Ok(n) => Ok(n),
|
||||
_ => Err(msg_handler())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vec_to_float<F>(vec: &Vec<char>, msg_handler: F) -> result::Result<f64, String>
|
||||
where F: Fn() -> String {
|
||||
match vec.iter().map(|c| *c).collect::<String>().as_str().parse::<f64>() {
|
||||
Ok(n) => Ok(n),
|
||||
_ => Err(msg_handler())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vec_to_string(vec: &Vec<char>) -> String {
|
||||
vec.iter().map(|c| *c).collect::<String>()
|
||||
}
|
@ -2,6 +2,8 @@
|
||||
extern crate log;
|
||||
extern crate env_logger;
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde_json;
|
||||
extern crate core;
|
||||
|
||||
pub mod jsonpath;
|
Loading…
x
Reference in New Issue
Block a user