diff --git a/examples/get_many.rs b/examples/get_many.rs index 985a5f46..5050f116 100644 --- a/examples/get_many.rs +++ b/examples/get_many.rs @@ -8,14 +8,25 @@ fn main() { let mut tree = sonic_rs::PointerTree::new(); tree.add_path(&["u"]); + tree.add_path(&["unknown_key"]); tree.add_path(pointer!["a", "b", "c", 1]); let nodes = unsafe { sonic_rs::get_many_unchecked(json, &tree) }; - // the node order is as the order of `add_path` - for val in nodes.unwrap() { - println!("{}", val.as_raw_str()); - // 123 - // "found" + match nodes { + Ok(vals) => { + assert_eq!(vals[0].as_ref().unwrap().as_raw_str(), "123"); + assert!(vals[1].is_none()); + assert_eq!(vals[2].as_ref().unwrap().as_raw_str(), "\"found\""); + for val in vals { + match val { + Some(_) => println!("{}", val.as_ref().unwrap().as_raw_str()), + None => println!("None"), + }; + } + } + Err(e) => { + println!("err: {:?}", e) + } } } diff --git a/src/error.rs b/src/error.rs index 70cbcfb1..67b2be1b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -305,7 +305,7 @@ impl Error { pub(crate) fn syntax(code: ErrorCode, json: &[u8], index: usize) -> Self { let position = Position::from_index(index, json); // generate descript about 16 characters - let mut start = if index < 8 { 0 } else { index - 8 }; + let mut start = index.saturating_sub(8); let mut end = if index + 8 > json.len() { json.len() } else { diff --git a/src/lazyvalue/get.rs b/src/lazyvalue/get.rs index be6e2000..f4b8f8e7 100644 --- a/src/lazyvalue/get.rs +++ b/src/lazyvalue/get.rs @@ -185,9 +185,10 @@ where /// get_many returns multiple fields from the `PointerTree`. /// -/// The result is a `Result>`. The order of the `Vec` is same as the order of the -/// tree. +/// The result is a `Result>>`. The order of the `Vec` is same as the order of +/// the tree. /// +/// If a key is unknown, the value at the corresponding position in `Vec` will be None. /// If json is invalid, or the field not be found, it will return a err. /// /// # Safety @@ -195,22 +196,36 @@ where /// /// # Examples /// ``` -/// use sonic_rs::pointer; +/// # use sonic_rs::pointer; +/// # use sonic_rs::PointerTree; +/// /// let json = r#" -/// {"u": 123, "a": {"b" : {"c": [null, "found"]}}}"#; +/// {"u": 123, "a": {"b" : {"c": [null, "found"]}}}"#; +/// /// // build a pointer tree, representing multiple json path -/// let mut tree = sonic_rs::PointerTree::new(); +/// let mut tree = PointerTree::new(); +/// /// tree.add_path(&["u"]); +/// tree.add_path(&["unknown_key"]); /// tree.add_path(&pointer!["a", "b", "c", 1]); -/// let nodes = unsafe { sonic_rs::get_many_unchecked(json, &tree).unwrap() }; -/// // the node order is as the order of `add_path` -/// assert_eq!(nodes[0].as_raw_str(), "123"); -/// assert_eq!(nodes[1].as_raw_str(), "\"found\""); +/// +/// let nodes = unsafe { sonic_rs::get_many_unchecked(json, &tree) }; +/// +/// match nodes { +/// Ok(vals) => { +/// assert_eq!(vals[0].as_ref().unwrap().as_raw_str(), "123"); +/// assert!(vals[1].is_none()); +/// assert_eq!(vals[2].as_ref().unwrap().as_raw_str(), "\"found\""); +/// } +/// Err(e) => { +/// panic!("err: {:?}", e) +/// } +/// } /// ``` pub unsafe fn get_many_unchecked<'de, Input>( json: Input, tree: &PointerTree, -) -> Result>> +) -> Result>>> where Input: JsonInput<'de>, { @@ -401,25 +416,41 @@ where /// get_many returns multiple fields from the [`PointerTree`]. /// -/// The result is a `Result>`. The order of the `Vec` is same as the order of the -/// tree. +/// The result is a `Result>>`. The order of the `Vec` is same as the order of +/// the tree. +/// +/// If a key is unknown, the value at the corresponding position in `Vec` will be None. /// If json is invalid, or the field not be found, it will return a err. /// /// # Examples /// ``` -/// use sonic_rs::pointer; +/// # use sonic_rs::pointer; +/// # use sonic_rs::PointerTree; +/// /// let json = r#" -/// {"u": 123, "a": {"b" : {"c": [null, "found"]}}}"#; +/// {"u": 123, "a": {"b" : {"c": [null, "found"]}}}"#; +/// /// // build a pointer tree, representing multiple json path -/// let mut tree = sonic_rs::PointerTree::new(); +/// let mut tree = PointerTree::new(); +/// /// tree.add_path(&["u"]); +/// tree.add_path(&["unknown_key"]); /// tree.add_path(&pointer!["a", "b", "c", 1]); -/// let nodes = sonic_rs::get_many(json, &tree).unwrap(); -/// // the node order is as the order of `add_path` -/// assert_eq!(nodes[0].as_raw_str(), "123"); -/// assert_eq!(nodes[1].as_raw_str(), "\"found\""); +/// +/// let nodes = unsafe { sonic_rs::get_many(json, &tree) }; +/// +/// match nodes { +/// Ok(vals) => { +/// assert_eq!(vals[0].as_ref().unwrap().as_raw_str(), "123"); +/// assert!(vals[1].is_none()); +/// assert_eq!(vals[2].as_ref().unwrap().as_raw_str(), "\"found\""); +/// } +/// Err(e) => { +/// panic!("err: {:?}", e) +/// } +/// } /// ``` -pub fn get_many<'de, Input>(json: Input, tree: &PointerTree) -> Result>> +pub fn get_many<'de, Input>(json: Input, tree: &PointerTree) -> Result>>> where Input: JsonInput<'de>, { @@ -599,7 +630,8 @@ mod test { tree.add_path(pointer!["a"].iter()); // 4 tree.add_path(pointer!["b", 2].iter()); // 5 tree.add_path(pointer![].iter()); // 6 - assert_eq!(tree.size(), 7); + tree.add_path(pointer!["unknown_key"].iter()); // 7 + assert_eq!(tree.size(), 8); tree } @@ -622,18 +654,22 @@ mod test { test_many_ok(unsafe { get_many_unchecked(&json, &tree).unwrap() }); test_many_ok(get_many(&json, &tree).unwrap()); - fn test_many_ok(many: Vec>) { - assert_eq!(many[0].as_raw_str(), "\"hello, world!\""); + fn test_many_ok(many: Vec>>) { + assert_eq!(many[0].as_ref().unwrap().as_raw_str(), "\"hello, world!\""); assert_eq!( - many[1].as_raw_str(), + many[1].as_ref().unwrap().as_raw_str(), "{\n \"a_b_c\":\"hello, world!\"\n }" ); - assert_eq!(many[2].as_raw_str(), "1"); - assert_eq!(many[3].as_raw_str(), "{\n \"a_b\":{\n \"a_b_c\":\"hello, world!\"\n },\n \"a_a\": [0, 1, 2]\n }"); - assert_eq!(many[4].as_raw_str(), many[3].as_raw_str()); - assert_eq!(many[5].as_raw_str(), "true"); + assert_eq!(many[2].as_ref().unwrap().as_raw_str(), "1"); + assert_eq!(many[3].as_ref().unwrap().as_raw_str(), "{\n \"a_b\":{\n \"a_b_c\":\"hello, world!\"\n },\n \"a_a\": [0, 1, 2]\n }"); + assert_eq!( + many[4].as_ref().unwrap().as_raw_str(), + many[3].as_ref().unwrap().as_raw_str() + ); + assert_eq!(many[5].as_ref().unwrap().as_raw_str(), "true"); // we have strip the leading or trailing spaces - assert_eq!(many[6].as_raw_str(), "{\n \"b\": [0, 1, true],\n \"a\": {\n \"a_b\":{\n \"a_b_c\":\"hello, world!\"\n },\n \"a_a\": [0, 1, 2]\n }\n }"); + assert_eq!(many[6].as_ref().unwrap().as_raw_str(), "{\n \"b\": [0, 1, true],\n \"a\": {\n \"a_b\":{\n \"a_b_c\":\"hello, world!\"\n },\n \"a_a\": [0, 1, 2]\n }\n }"); + assert!(many[7].is_none()) } } } diff --git a/src/parser.rs b/src/parser.rs index 89316e0f..0e98c016 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1848,7 +1848,7 @@ where fn get_many_rec( &mut self, node: &PointerTreeNode, - out: &mut Vec>, + out: &mut Vec>>, strbuf: &mut Vec, remain: &mut usize, is_safe: bool, @@ -1893,7 +1893,7 @@ where slice = self.read.slice_unchecked(start, self.read.index()); let lv = LazyValue::new(slice.into(), status.into()); for p in &node.order { - out[*p] = lv.clone(); + out[*p] = Some(lv.clone()); } *remain -= node.order.len(); } @@ -1905,7 +1905,7 @@ where &mut self, mkeys: &MultiKey, strbuf: &mut Vec, - out: &mut Vec>, + out: &mut Vec>>, remain: &mut usize, ) -> Result<()> { debug_assert!(strbuf.is_empty()); @@ -1923,13 +1923,11 @@ where Some(_) => unreachable!(), } - let mut visited = 0; loop { let key = self.parse_str_impl(strbuf)?; self.parse_object_clo()?; if let Some(val) = mkeys.get(key.deref()) { self.get_many_rec(val, out, strbuf, remain, false)?; - visited += 1; if *remain == 0 { break; } @@ -1953,12 +1951,7 @@ where } } - // check whether remaining unknown keys - if visited < mkeys.len() { - perr!(self, GetUnknownKeyInObject) - } else { - Ok(()) - } + Ok(()) } #[allow(clippy::mutable_key_type)] @@ -1966,7 +1959,7 @@ where &mut self, mkeys: &MultiKey, strbuf: &mut Vec, - out: &mut Vec>, + out: &mut Vec>>, remain: &mut usize, ) -> Result<()> { debug_assert!(strbuf.is_empty()); @@ -1985,14 +1978,12 @@ where } } - let mut visited = 0; loop { let key = self.parse_str_impl(strbuf)?; self.parse_object_clo()?; if let Some(val) = mkeys.get(key.deref()) { // parse the child point tree self.get_many_rec(val, out, strbuf, remain, true)?; - visited += 1; if *remain == 0 { break; } @@ -2009,12 +2000,7 @@ where } } - // check whether remaining unknown keys - if visited < mkeys.len() { - perr!(self, GetUnknownKeyInObject) - } else { - Ok(()) - } + Ok(()) } #[cfg(test)] @@ -2035,7 +2021,7 @@ where &mut self, midx: &MultiIndex, strbuf: &mut Vec, - out: &mut Vec>, + out: &mut Vec>>, remain: &mut usize, ) -> Result<()> { match self.skip_space() { @@ -2094,7 +2080,7 @@ where &mut self, midx: &MultiIndex, strbuf: &mut Vec, - out: &mut Vec>, + out: &mut Vec>>, remain: &mut usize, ) -> Result<()> { match self.skip_space() { @@ -2146,11 +2132,11 @@ where &mut self, tree: &PointerTree, is_safe: bool, - ) -> Result>> { + ) -> Result>>> { let mut strbuf = Vec::with_capacity(DEFAULT_KEY_BUF_CAPACITY); let mut remain = tree.size(); - let mut out: Vec> = Vec::with_capacity(tree.size()); - out.resize(tree.size(), LazyValue::default()); + let mut out: Vec>> = Vec::with_capacity(tree.size()); + out.resize(tree.size(), Option::default()); let cur = &tree.root; self.get_many_rec(cur, &mut out, &mut strbuf, &mut remain, is_safe)?; Ok(out) diff --git a/src/pointer/tree.rs b/src/pointer/tree.rs index 71213fc8..320db964 100644 --- a/src/pointer/tree.rs +++ b/src/pointer/tree.rs @@ -23,15 +23,26 @@ use crate::index::Index; /// let mut tree = PointerTree::new(); /// /// tree.add_path(&["u"]); +/// tree.add_path(&["unknown_key"]); /// tree.add_path(&pointer!["a", "b", "c", 1]); /// /// let nodes = unsafe { sonic_rs::get_many_unchecked(json, &tree) }; /// -/// // the node order is as the order of `add_path` -/// for val in nodes.unwrap() { -/// println!("{}", val.as_raw_str()); -/// // 123 -/// // "found" +/// match nodes { +/// Ok(vals) => { +/// assert_eq!(vals[0].as_ref().unwrap().as_raw_str(), "123"); +/// assert!(vals[1].is_none()); +/// assert_eq!(vals[2].as_ref().unwrap().as_raw_str(), "\"found\""); +/// for val in vals { +/// match val { +/// Some(_) => println!("{}", val.as_ref().unwrap().as_raw_str()), +/// None => println!("None"), +/// }; +/// } +/// } +/// Err(e) => { +/// println!("err: {:?}", e) +/// } /// } /// ```