SelectorMut first commit

This commit is contained in:
freestrings 2019-06-09 22:55:35 +09:00
parent 9a08df7843
commit dd9315bc90
8 changed files with 254 additions and 46 deletions

View File

@ -10,6 +10,8 @@ use std::io::Read;
use serde::Deserialize;
use serde_json::Value;
use jsonpath::{SelectorMut, Selector};
use self::test::Bencher;
fn read_json(path: &str) -> String {
@ -102,4 +104,33 @@ fn bench_select_as(b: &mut Bencher) {
let _: Vec<Book> = jsonpath::select_as(&json, r#"$..book[?(@.price<30 && @.category=="fiction")][0]"#).unwrap();
}
});
}
#[bench]
fn bench_delete(b: &mut Bencher) {
let json = get_json();
let mut selector = SelectorMut::new();
let _ = selector.str_path(get_path());
b.iter(move || {
for _ in 1..100 {
let _ = selector.value(json.clone()).delete();
}
});
}
#[bench]
fn bench_select_to_compare_with_delete(b: &mut Bencher) {
let json = &get_json();
let mut selector = Selector::new();
let _ = selector.str_path(get_path());
let _ = selector.value(json);
b.iter(move || {
for _ in 1..100 {
let _ = json.clone();
let _ = selector.reset_value().select();
}
});
}

View File

@ -55,7 +55,7 @@ fn _selector(b: &mut Bencher, index: usize) {
b.iter(move || {
for _ in 1..100 {
let mut selector = jsonpath::Selector::new();
let _ = selector.path(get_path(index));
let _ = selector.str_path(get_path(index));
selector.value(&json);
let r = selector.select();
if r.is_err() {

View File

@ -125,23 +125,23 @@
extern crate array_tool;
extern crate core;
extern crate env_logger;
extern crate indexmap;
#[macro_use]
extern crate log;
extern crate serde;
extern crate serde_json;
extern crate indexmap;
use serde_json::Value;
pub use parser::parser::{Node, Parser};
pub use select::{Selector, SelectorMut};
pub use select::JsonPathError;
#[doc(hidden)]
mod parser;
#[doc(hidden)]
mod select;
pub use select::Selector;
pub use select::JsonPathError;
pub use parser::parser::{Node, Parser};
/// It is a high-order function. it compile a JsonPath and then returns a function. this return-function can be reused for different JsonObjects.
///
/// ```rust
@ -219,7 +219,7 @@ pub fn selector<'a>(json: &'a Value) -> impl FnMut(&'a str) -> Result<Vec<&Value
let mut selector = Selector::new();
let _ = selector.value(json);
move |path: &str| {
selector.path(path)?.reset_value().select()
selector.str_path(path)?.reset_value().select()
}
}
@ -273,7 +273,7 @@ pub fn selector_as<T: serde::de::DeserializeOwned>(json: &Value) -> impl FnMut(&
let mut selector = Selector::new();
let _ = selector.value(json);
move |path: &str| {
selector.path(path)?.reset_value().select_as()
selector.str_path(path)?.reset_value().select_as()
}
}
@ -303,7 +303,7 @@ pub fn selector_as<T: serde::de::DeserializeOwned>(json: &Value) -> impl FnMut(&
/// ]);
/// ```
pub fn select<'a>(json: &'a Value, path: &'a str) -> Result<Vec<&'a Value>, JsonPathError> {
Selector::new().path(path)?.value(json).select()
Selector::new().str_path(path)?.value(json).select()
}
/// This function compile a jsonpath everytime and it convert `&str` to `jsonpath's RefValue` everytime and then it return a json string.
@ -331,7 +331,7 @@ pub fn select<'a>(json: &'a Value, path: &'a str) -> Result<Vec<&'a Value>, Json
/// ```
pub fn select_as_str(json_str: &str, path: &str) -> Result<String, JsonPathError> {
let json = serde_json::from_str(json_str).map_err(|e| JsonPathError::Serde(e.to_string()))?;
let ret = Selector::new().path(path)?.value(&json).select()?;
let ret = Selector::new().str_path(path)?.value(&json).select()?;
serde_json::to_string(&ret).map_err(|e| JsonPathError::Serde(e.to_string()))
}
@ -375,5 +375,5 @@ pub fn select_as_str(json_str: &str, path: &str) -> Result<String, JsonPathError
/// ```
pub fn select_as<T: serde::de::DeserializeOwned>(json_str: &str, path: &str) -> Result<Vec<T>, JsonPathError> {
let json = serde_json::from_str(json_str).map_err(|e| JsonPathError::Serde(e.to_string()))?;
Selector::new().path(path)?.value(&json).select_as()
Selector::new().str_path(path)?.value(&json).select_as()
}

View File

@ -1,8 +1,10 @@
use std::collections::HashSet;
use array_tool::vec::{Intersect, Union};
use serde_json::{Number, Value};
use parser::parser::*;
use std::collections::HashSet;
use indexmap::IndexSet;
fn to_f64(n: &Number) -> f64 {
if n.is_i64() {
@ -382,6 +384,21 @@ impl<'a> ExprTerm<'a> {
}
}
impl<'a> Into<ExprTerm<'a>> for &Vec<&'a Value> {
fn into(self) -> ExprTerm<'a> {
if self.len() == 1 {
match &self[0] {
Value::Number(v) => return ExprTerm::Number(v.clone()),
Value::String(v) => return ExprTerm::String(v.clone()),
Value::Bool(v) => return ExprTerm::Bool(*v),
_ => {}
}
}
ExprTerm::Json(None, self.to_vec())
}
}
fn walk_all_with_str<'a>(vec: &Vec<&'a Value>, tmp: &mut Vec<&'a Value>, key: &str, is_filter: bool) {
if is_filter {
walk(vec, tmp, &|v| match v {
@ -491,19 +508,19 @@ impl<'a> Selector<'a> {
}
}
pub fn path(&mut self, path: &str) -> Result<&mut Self, JsonPathError> {
pub fn str_path(&mut self, path: &str) -> Result<&mut Self, JsonPathError> {
debug!("path : {}", path);
self.node = Some(Parser::compile(path).map_err(|e| JsonPathError::Path(e))?);
Ok(self)
}
pub(crate) fn reset_value(&mut self) -> &mut Self {
self.current = None;
pub fn compiled_path(&mut self, node: Node) -> &mut Self {
self.node = Some(node);
self
}
pub fn compiled_path(&mut self, node: Node) -> &mut Self {
self.node = Some(node);
pub fn reset_value(&mut self) -> &mut Self {
self.current = None;
self
}
@ -733,19 +750,10 @@ impl<'a> NodeVisitor for Selector<'a> {
if !self.selectors.is_empty() {
match token {
ParseToken::Absolute | ParseToken::Relative | ParseToken::Filter(_) => {
let s = self.selectors.pop().unwrap();
let selector = self.selectors.pop().unwrap();
if let Some(current) = &s.current {
let term = if current.len() == 1 {
match current[0] {
Value::Number(v) => ExprTerm::Number(v.clone()),
Value::String(v) => ExprTerm::String(v.clone()),
Value::Bool(v) => ExprTerm::Bool(*v),
_ => ExprTerm::Json(None, current.to_vec())
}
} else {
ExprTerm::Json(None, current.to_vec())
};
if let Some(current) = &selector.current {
let term = current.into();
if let Some(s) = self.selectors.last_mut() {
s.terms.push(Some(term));
@ -760,19 +768,20 @@ impl<'a> NodeVisitor for Selector<'a> {
}
}
if let Some(s) = self.selectors.last_mut() {
s.visit_token(token);
if let Some(selector) = self.selectors.last_mut() {
selector.visit_token(token);
return;
}
match token {
ParseToken::Absolute => {
if self.current.is_some() {
let mut s = Selector::new();
let mut selector = Selector::new();
if let Some(value) = self.value {
s.value = Some(value);
s.current = Some(vec![value]);
self.selectors.push(s);
selector.value = Some(value);
selector.current = Some(vec![value]);
self.selectors.push(selector);
}
return;
}
@ -964,15 +973,143 @@ impl<'a> NodeVisitor for Selector<'a> {
}
}
pub trait Modifiable {
fn delete_from_selected(&mut self, value: &mut Value);
pub struct SelectorMut {
path: Option<Node>,
value: Option<Value>,
}
impl<'a> Modifiable for Selector<'a> {
fn delete_from_selected(&mut self, value: &mut Value) {
match &self.current {
Some(current) => {}
_ => {}
impl SelectorMut {
pub fn new() -> Self {
SelectorMut { path: None, value: None }
}
pub fn str_path(&mut self, path: &str) -> Result<&mut Self, JsonPathError> {
self.path = Some(Parser::compile(path).map_err(|e| JsonPathError::Path(e))?);
Ok(self)
}
pub fn value(&mut self, value: Value) -> &mut Self {
self.value = Some(value);
self
}
pub fn take(&mut self) -> Option<Value> {
self.value.take()
}
fn compute_paths(&self, result: &Vec<&Value>) -> Vec<Vec<String>> {
fn _walk(origin: &Value, target: &Value, tokens: &mut Vec<String>, visited: &mut IndexSet<Vec<String>>) -> bool {
if visited.contains(tokens) {
return false;
}
if std::ptr::eq(origin, target) {
debug!("tokens: {:?}", tokens);
return true;
}
match origin {
Value::Array(vec) => for (i, v) in vec.iter().enumerate() {
tokens.push(i.to_string());
if _walk(v, target, tokens, visited) {
return true;
}
tokens.pop();
},
Value::Object(map) => for (k, v) in map {
tokens.push(k.clone());
if _walk(v, target, tokens, visited) {
return true;
}
tokens.pop();
}
_ => {}
}
return false;
}
let mut visited = IndexSet::new();
if let Some(origin) = &self.value {
for v in result {
let mut tokens = Vec::new();
if _walk(origin, v, &mut tokens, &mut visited) {
visited.insert(tokens);
}
}
}
visited.iter().map(|v| v.to_vec()).collect()
}
pub fn delete(&mut self) -> Result<&mut Self, JsonPathError> {
self.replace_with(&mut |_| Value::Null)
}
pub fn replace_with<F: FnMut(&Value) -> Value>(&mut self, fun: &mut F) -> Result<&mut Self, JsonPathError> {
let mut selector = Selector::new();
if let Some(path) = self.path.take() {
selector.compiled_path(path);
}
if let Some(value) = &self.value {
selector.value(value);
}
let result = selector.select();
self.path = Some(selector.node.unwrap());
let paths = self.compute_paths(&result?);
if let Some(mut value) = self.value.take() {
for tokens in paths {
self.replace_value(tokens, &mut value, fun);
}
self.value = Some(value);
}
Ok(self)
}
fn replace_value<F: FnMut(&Value) -> Value>(&mut self, tokens: Vec<String>, value: &mut Value, fun: &mut F) {
let mut target = value;
for (i, token) in tokens.iter().enumerate() {
let target_once = target;
let is_last = i == tokens.len() - 1;
let target_opt = match *target_once {
Value::Object(ref mut map) => {
if is_last {
if let Some(v) = map.remove(token) {
map.insert(token.clone(), fun(&v));
return;
}
}
map.get_mut(token)
}
Value::Array(ref mut vec) => {
if let Ok(x) = token.parse::<usize>() {
if is_last {
let v = &vec[x];
vec[x] = fun(v);
return;
}
vec.get_mut(x)
} else {
None
}
}
_ => None,
};
if let Some(t) = target_opt {
target = t;
} else {
break;
}
}
}
}

View File

@ -31,7 +31,7 @@ pub fn read_contents(path: &str) -> String {
#[allow(dead_code)]
pub fn select_and_then_compare<'a>(path: &str, json: Value, target: Value) {
let mut s = Selector::new();
let _ = s.path(path);
let _ = s.str_path(path);
let _ = s.value(&json);
let result = serde_json::to_value(s.select().unwrap()).unwrap();
assert_eq!(result, target, "{}", path);

40
tests/mutable.rs Normal file
View File

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

View File

@ -151,7 +151,7 @@ fn readme_selector() {
let mut selector = Selector::new();
let result = selector
.path("$..[?(@.age >= 30)]").unwrap()
.str_path("$..[?(@.age >= 30)]").unwrap()
.value(&json_obj)
.select().unwrap();

View File

@ -108,7 +108,7 @@ pub fn selector(js_value: JsValue) -> JsValue {
#[wasm_bindgen]
pub fn select(js_value: JsValue, path: &str) -> JsValue {
let mut selector = _Selector::new();
let _ = selector.path(path);
let _ = selector.str_path(path);
let json = match into_serde_json(&js_value) {
Ok(json) => json,
@ -160,7 +160,7 @@ impl Selector {
let mut selector = _Selector::new();
if let Some(path) = &self.path {
let _ = selector.path(&path).map_err(|e| JsValue::from_str(&format!("{:?}", e)))?;
let _ = selector.str_path(&path).map_err(|e| JsValue::from_str(&format!("{:?}", e)))?;
} else {
return Err(JsValue::from_str(&format!("{:?}", JsonPathError::EmptyPath)));
}