From 8f01598e05cac96ec4f52ee27ee472e3f11c2440 Mon Sep 17 00:00:00 2001 From: freestrings Date: Wed, 26 Jun 2019 15:07:49 +0900 Subject: [PATCH] code coverage 90% --- src/lib.rs | 18 +-- src/parser/mod.rs | 19 +-- src/parser/tokenizer.rs | 1 + src/select/mod.rs | 242 +++++++++++++++++++++++++++--- tests/{mutable.rs => selector.rs} | 10 +- 5 files changed, 248 insertions(+), 42 deletions(-) rename tests/{mutable.rs => selector.rs} (80%) diff --git a/src/lib.rs b/src/lib.rs index 80e9cbb..d7caebb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -412,13 +412,8 @@ pub fn select_as( /// ``` pub fn delete(value: Value, path: &str) -> Result { let mut selector = SelectorMut::default(); - let ret = selector - .str_path(path)? - .value(value) - .delete()? - .take() - .unwrap_or(Value::Null); - Ok(ret) + let value = selector.str_path(path)?.value(value).delete()?; + Ok(value.take().unwrap_or(Value::Null)) } /// Select JSON properties using a jsonpath and transform the result and then replace it. via closure that implements `FnMut` you can transform the selected results. @@ -468,11 +463,6 @@ where F: FnMut(&Value) -> Value, { let mut selector = SelectorMut::default(); - let ret = selector - .str_path(path)? - .value(value) - .replace_with(fun)? - .take() - .unwrap_or(Value::Null); - Ok(ret) + let value = selector.str_path(path)?.value(value).replace_with(fun)?; + Ok(value.take().unwrap_or(Value::Null)) } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 59080f8..4a44b3d 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -193,13 +193,14 @@ impl Parser { fn boolean(tokenizer: &mut TokenReader) -> ParseResult { debug!("#boolean"); + + fn validation_bool_value(v: &str) -> bool { + let b = v.as_bytes(); + !b.is_empty() && (b[0] == b't' || b[0] == b'T' || b[0] == b'f' || b[0] == b'F') + } + match tokenizer.next_token() { - Ok(Token::Key(_, ref v)) - if { - let b = v.as_bytes(); - !b.is_empty() && (b[0] == b't' || b[0] == b'T' || b[0] == b'f' || b[0] == b'F') - } => - { + Ok(Token::Key(_, ref v)) if validation_bool_value(v) => { Ok(Self::node(ParseToken::Bool(v.eq_ignore_ascii_case("true")))) } _ => Err(tokenizer.err_msg()), @@ -229,10 +230,10 @@ impl Parser { debug!("#array_quote_value"); match tokenizer.next_token() { Ok(Token::SingleQuoted(_, val)) | Ok(Token::DoubleQuoted(_, val)) => { - if !tokenizer.peek_is(COMMA) { - Ok(Self::node(ParseToken::Key(val))) - } else { + if tokenizer.peek_is(COMMA) { Self::array_keys(tokenizer, val) + } else { + Ok(Self::node(ParseToken::Key(val))) } } _ => Err(tokenizer.err_msg()), diff --git a/src/parser/tokenizer.rs b/src/parser/tokenizer.rs index bd5a15f..b2e04bb 100644 --- a/src/parser/tokenizer.rs +++ b/src/parser/tokenizer.rs @@ -94,6 +94,7 @@ impl Token { self.to_simple() == str_token } + #[cfg_attr(tarpaulin, skip)] fn to_simple(&self) -> &'static str { match self { Token::Absolute(_) => ABSOLUTE, diff --git a/src/select/mod.rs b/src/select/mod.rs index 3772ba7..b240b11 100644 --- a/src/select/mod.rs +++ b/src/select/mod.rs @@ -157,8 +157,8 @@ impl Cmp for CmpAnd { v1 && v2 } - fn cmp_f64(&self, v1: f64, v2: f64) -> bool { - v1 > 0_f64 && v2 > 0_f64 + fn cmp_f64(&self, _v1: f64, _v2: f64) -> bool { + true } fn cmp_string(&self, v1: &str, v2: &str) -> bool { @@ -177,8 +177,8 @@ impl Cmp for CmpOr { v1 || v2 } - fn cmp_f64(&self, v1: f64, v2: f64) -> bool { - v1 > 0_f64 || v2 > 0_f64 + fn cmp_f64(&self, _v1: f64, _v2: f64) -> bool { + true } fn cmp_string(&self, v1: &str, v2: &str) -> bool { @@ -190,7 +190,7 @@ impl Cmp for CmpOr { } } -#[derive(Debug)] +#[derive(Debug, PartialEq)] enum ExprTerm<'a> { String(String), Number(Number), @@ -434,7 +434,7 @@ fn abs_index(n: isize, len: usize) -> usize { } } -#[derive(Debug)] +#[derive(Debug, PartialEq)] enum FilterKey { String(String), All, @@ -482,28 +482,25 @@ impl<'a, 'b> Selector<'a, 'b> { pub fn str_path(&mut self, path: &str) -> Result<&mut Self, JsonPathError> { debug!("path : {}", path); - - if self.node_ref.is_some() { - self.node_ref.take(); - } - + self.node_ref.take(); self.node = Some(Parser::compile(path).map_err(JsonPathError::Path)?); Ok(self) } pub fn node_ref(&self) -> Option<&Node> { if let Some(node) = &self.node { - Some(node) - } else { - None + return Some(node); } + + if let Some(node) = &self.node_ref { + return Some(*node); + } + + None } pub fn compiled_path(&mut self, node: &'b Node) -> &mut Self { - if self.node.is_some() { - self.node.take(); - } - + self.node.take(); self.node_ref = Some(node); self } @@ -1195,3 +1192,212 @@ impl SelectorMut { Ok(self) } } + +#[cfg(test)] +mod select_inner_tests { + use serde_json::{Number, Value}; + + use select::{Cmp, CmpAnd, CmpEq, CmpGe, CmpGt, CmpLe, CmpLt, CmpNe, CmpOr, ExprTerm}; + + #[test] + fn to_f64_i64() { + let number = 0_i64; + let v: Value = serde_json::from_str(&format!("{}", number)).unwrap(); + if let Value::Number(n) = v { + assert_eq!((super::to_f64(&n) - number as f64).abs() == 0_f64, true); + } else { + panic!(); + } + } + + #[test] + fn to_f64_f64() { + let number = 0.1_f64; + let v: Value = serde_json::from_str(&format!("{}", number)).unwrap(); + if let Value::Number(n) = v { + assert_eq!((super::to_f64(&n) - number).abs() == 0_f64, true); + } else { + panic!(); + } + } + + #[test] + fn to_f64_u64() { + let number = u64::max_value(); + let v: Value = serde_json::from_str(&format!("{}", number)).unwrap(); + if let Value::Number(n) = v { + assert_eq!((super::to_f64(&n) - number as f64).abs() == 0_f64, true); + } else { + panic!(); + } + } + + #[test] + fn cmp_eq() { + let cmp_fn = CmpEq; + assert_eq!(cmp_fn.default(), false); + assert_eq!(cmp_fn.cmp_bool(true, false), false); + assert_eq!(cmp_fn.cmp_bool(true, true), true); + assert_eq!(cmp_fn.cmp_f64(0.1, 0.1), true); + assert_eq!(cmp_fn.cmp_f64(0.1, 0.2), false); + assert_eq!(cmp_fn.cmp_string("1", "1"), true); + assert_eq!(cmp_fn.cmp_string("1", "2"), false); + } + + #[test] + fn cmp_ne() { + let cmp_fn = CmpNe; + assert_eq!(cmp_fn.default(), false); + assert_eq!(cmp_fn.cmp_bool(true, false), true); + assert_eq!(cmp_fn.cmp_bool(true, true), false); + assert_eq!(cmp_fn.cmp_f64(0.1, 0.1), false); + assert_eq!(cmp_fn.cmp_f64(0.1, 0.2), true); + assert_eq!(cmp_fn.cmp_string("1", "1"), false); + assert_eq!(cmp_fn.cmp_string("1", "2"), true); + } + + #[test] + fn cmp_gt() { + let cmp_fn = CmpGt; + assert_eq!(cmp_fn.default(), false); + assert_eq!(cmp_fn.cmp_bool(true, false), true); + assert_eq!(cmp_fn.cmp_bool(true, true), false); + assert_eq!(cmp_fn.cmp_f64(0.2, 0.1), true); + assert_eq!(cmp_fn.cmp_f64(0.1, 0.2), false); + assert_eq!(cmp_fn.cmp_string("a", "a"), false); + assert_eq!(cmp_fn.cmp_string("b", "a"), true); + assert_eq!(cmp_fn.cmp_string("1", "2"), false); + } + + #[test] + fn cmp_ge() { + let cmp_fn = CmpGe; + assert_eq!(cmp_fn.default(), false); + assert_eq!(cmp_fn.cmp_bool(true, false), true); + assert_eq!(cmp_fn.cmp_bool(true, true), true); + assert_eq!(cmp_fn.cmp_f64(0.2, 0.1), true); + assert_eq!(cmp_fn.cmp_f64(0.1, 0.1), true); + assert_eq!(cmp_fn.cmp_f64(0.1, 0.2), false); + assert_eq!(cmp_fn.cmp_string("1", "1"), true); + assert_eq!(cmp_fn.cmp_string("ab", "a"), true); + assert_eq!(cmp_fn.cmp_string("1", "2"), false); + } + + #[test] + fn cmp_lt() { + let cmp_fn = CmpLt; + assert_eq!(cmp_fn.default(), false); + assert_eq!(cmp_fn.cmp_bool(true, false), false); + assert_eq!(cmp_fn.cmp_bool(false, true), true); + assert_eq!(cmp_fn.cmp_bool(true, true), false); + assert_eq!(cmp_fn.cmp_bool(false, false), false); + assert_eq!(cmp_fn.cmp_f64(0.1, 0.2), true); + assert_eq!(cmp_fn.cmp_f64(0.1, 0.1), false); + assert_eq!(cmp_fn.cmp_f64(0.2, 0.1), false); + assert_eq!(cmp_fn.cmp_string("a", "a"), false); + assert_eq!(cmp_fn.cmp_string("ab", "b"), true); + assert_eq!(cmp_fn.cmp_string("1", "2"), true); + } + + #[test] + fn cmp_le() { + let cmp_fn = CmpLe; + assert_eq!(cmp_fn.default(), false); + assert_eq!(cmp_fn.cmp_bool(true, false), false); + assert_eq!(cmp_fn.cmp_bool(false, true), true); + assert_eq!(cmp_fn.cmp_bool(true, true), true); + assert_eq!(cmp_fn.cmp_bool(false, false), true); + assert_eq!(cmp_fn.cmp_f64(0.1, 0.2), true); + assert_eq!(cmp_fn.cmp_f64(0.1, 0.1), true); + assert_eq!(cmp_fn.cmp_f64(0.2, 0.1), false); + assert_eq!(cmp_fn.cmp_string("a", "a"), true); + assert_eq!(cmp_fn.cmp_string("ab", "b"), true); + assert_eq!(cmp_fn.cmp_string("abd", "abc"), false); + assert_eq!(cmp_fn.cmp_string("1", "2"), true); + } + + #[test] + fn cmp_and() { + let cmp_fn = CmpAnd; + assert_eq!(cmp_fn.default(), false); + assert_eq!(cmp_fn.cmp_bool(true, false), false); + assert_eq!(cmp_fn.cmp_bool(false, true), false); + assert_eq!(cmp_fn.cmp_bool(true, true), true); + assert_eq!(cmp_fn.cmp_bool(false, false), false); + assert_eq!(cmp_fn.cmp_f64(0.0, 0.0), true); + assert_eq!(cmp_fn.cmp_string("a", "a"), true); + } + + #[test] + fn cmp_or() { + let cmp_fn = CmpOr; + assert_eq!(cmp_fn.default(), false); + assert_eq!(cmp_fn.cmp_bool(true, false), true); + assert_eq!(cmp_fn.cmp_bool(false, true), true); + assert_eq!(cmp_fn.cmp_bool(true, true), true); + assert_eq!(cmp_fn.cmp_bool(false, false), false); + assert_eq!(cmp_fn.cmp_f64(0.0, 0.0), true); + assert_eq!(cmp_fn.cmp_string("a", "a"), true); + } + + #[test] + fn cmp_json() { + let v1 = Value::Bool(true); + let v2 = Value::String("1".to_string()); + let left = [&v1, &v2]; + let right = [&v1, &v2]; + let empty: Vec<&Value> = Vec::new(); + + assert_eq!(CmpEq.cmp_json(&left, &right), left.to_vec()); + assert_eq!(CmpNe.cmp_json(&left, &right), left.to_vec()); + assert_eq!(CmpGt.cmp_json(&left, &right), empty); + assert_eq!(CmpGe.cmp_json(&left, &right), empty); + assert_eq!(CmpLt.cmp_json(&left, &right), empty); + assert_eq!(CmpLe.cmp_json(&left, &right), empty); + assert_eq!(CmpAnd.cmp_json(&left, &right), left.to_vec()); + assert_eq!(CmpOr.cmp_json(&left, &right), left.to_vec()); + + assert_eq!( + CmpEq.cmp_json(&[&Value::Bool(true)], &[&Value::Bool(true)]), + vec![&Value::Bool(true)] + ); + assert_eq!( + CmpEq.cmp_json(&[&Value::Bool(true)], &[&Value::Bool(false)]), + empty + ); + assert_eq!( + CmpNe.cmp_json(&[&Value::Bool(true)], &[&Value::Bool(true)]), + empty + ); + assert_eq!( + CmpNe.cmp_json(&[&Value::Bool(false)], &[&Value::Bool(true)]), + vec![&Value::Bool(false)] + ); + assert_eq!( + CmpAnd.cmp_json(&[&Value::Bool(true)], &[&Value::Bool(true)]), + vec![&Value::Bool(true)] + ); + assert_eq!( + CmpOr.cmp_json(&[&Value::Bool(true)], &[&Value::Bool(false)]), + vec![&Value::Bool(true), &Value::Bool(false)] + ); + } + + #[test] + fn value_vec_into() { + let v = Value::Bool(true); + let vec = &vec![&v]; + let term: ExprTerm = vec.into(); + assert_eq!(term, ExprTerm::Bool(true)); + + let v = Value::String("a".to_string()); + let vec = &vec![&v]; + let term: ExprTerm = vec.into(); + assert_eq!(term, ExprTerm::String("a".to_string())); + + let v = serde_json::from_str("1.0").unwrap(); + let vec = &vec![&v]; + let term: ExprTerm = vec.into(); + assert_eq!(term, ExprTerm::Number(Number::from_f64(1.0).unwrap())); + } +} diff --git a/tests/mutable.rs b/tests/selector.rs similarity index 80% rename from tests/mutable.rs rename to tests/selector.rs index 6ae6205..7be7ea4 100644 --- a/tests/mutable.rs +++ b/tests/selector.rs @@ -3,7 +3,7 @@ extern crate jsonpath_lib as jsonpath; extern crate serde_json; use common::{read_json, setup}; -use jsonpath::{Selector, SelectorMut}; +use jsonpath::{Selector, SelectorMut, Parser}; use serde_json::Value; mod common; @@ -53,3 +53,11 @@ fn selector_mut() { result ); } + +#[test] +fn selector_node_ref() { + let node = Parser::compile("$.*").unwrap(); + let mut selector = Selector::default(); + selector.compiled_path(&node); + assert!(std::ptr::eq(selector.node_ref().unwrap(), &node)); +}