mirror of
https://github.com/fluencelabs/jsonpath
synced 2025-06-12 23:51:26 +00:00
json_filter 기본
This commit is contained in:
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;
|
Reference in New Issue
Block a user