From dd9315bc90c8a50ffcba382c741c5ab7c3c0edf5 Mon Sep 17 00:00:00 2001 From: freestrings Date: Sun, 9 Jun 2019 22:55:35 +0900 Subject: [PATCH] SelectorMut first commit --- benches/bench.rs | 31 ++++++ benches/bench_example.rs | 2 +- src/lib.rs | 20 ++-- src/select/mod.rs | 199 +++++++++++++++++++++++++++++++++------ tests/common.rs | 2 +- tests/mutable.rs | 40 ++++++++ tests/readme.rs | 2 +- wasm/src/lib.rs | 4 +- 8 files changed, 254 insertions(+), 46 deletions(-) create mode 100644 tests/mutable.rs diff --git a/benches/bench.rs b/benches/bench.rs index 9a7f78c..3cc6b1c 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -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 = 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(); + } + }); } \ No newline at end of file diff --git a/benches/bench_example.rs b/benches/bench_example.rs index 3748ca7..1d00866 100644 --- a/benches/bench_example.rs +++ b/benches/bench_example.rs @@ -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() { diff --git a/src/lib.rs b/src/lib.rs index 9c9dc74..236a3f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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(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(json: &Value) -> impl FnMut(& /// ]); /// ``` pub fn select<'a>(json: &'a Value, path: &'a str) -> Result, 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, Json /// ``` pub fn select_as_str(json_str: &str, path: &str) -> Result { 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(json_str: &str, path: &str) -> Result, 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() } \ No newline at end of file diff --git a/src/select/mod.rs b/src/select/mod.rs index a3b4c5a..1888054 100644 --- a/src/select/mod.rs +++ b/src/select/mod.rs @@ -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> 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, + value: Option, } -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 { + self.value.take() + } + + fn compute_paths(&self, result: &Vec<&Value>) -> Vec> { + fn _walk(origin: &Value, target: &Value, tokens: &mut Vec, visited: &mut IndexSet>) -> 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 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 Value>(&mut self, tokens: Vec, 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::() { + 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; + } } } } \ No newline at end of file diff --git a/tests/common.rs b/tests/common.rs index 98a04fe..ecd25c0 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -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); diff --git a/tests/mutable.rs b/tests/mutable.rs new file mode 100644 index 0000000..b61fa07 --- /dev/null +++ b/tests/mutable.rs @@ -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); +} \ No newline at end of file diff --git a/tests/readme.rs b/tests/readme.rs index dc47853..cb6e38e 100644 --- a/tests/readme.rs +++ b/tests/readme.rs @@ -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(); diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index c184dc4..1288afb 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -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))); }