From 7694b9247148299d3da468bae12fa2b44c4afbc2 Mon Sep 17 00:00:00 2001 From: baishen Date: Sat, 4 Jan 2025 16:10:08 +0800 Subject: [PATCH] Refactor JSONB functions: Improved API, Documentation, and Data Structures (#75) * Refactor JSONB functions api and data structures * fix clippy * refactor RawJsonb and OwendJsonb * split function files * fix clippy * add comments --- benches/get_path.rs | 12 +- benches/strip_nulls.rs | 8 +- src/de.rs | 4 +- src/from.rs | 14 +- src/functions.rs | 3150 -------------------------------------- src/functions/array.rs | 805 ++++++++++ src/functions/core.rs | 847 ++++++++++ src/functions/mod.rs | 20 + src/functions/object.rs | 723 +++++++++ src/functions/other.rs | 792 ++++++++++ src/functions/path.rs | 941 ++++++++++++ src/functions/scalar.rs | 1395 +++++++++++++++++ src/iterator.rs | 2 +- src/jsonpath/path.rs | 10 +- src/jsonpath/selector.rs | 109 +- src/keypath.rs | 4 +- src/lazy_value.rs | 9 +- src/lib.rs | 5 +- src/owned.rs | 129 ++ src/parser.rs | 12 +- src/raw.rs | 73 + src/value.rs | 65 +- tests/it/functions.rs | 1125 +++++--------- 23 files changed, 6283 insertions(+), 3971 deletions(-) delete mode 100644 src/functions.rs create mode 100644 src/functions/array.rs create mode 100644 src/functions/core.rs create mode 100644 src/functions/mod.rs create mode 100644 src/functions/object.rs create mode 100644 src/functions/other.rs create mode 100644 src/functions/path.rs create mode 100644 src/functions/scalar.rs create mode 100644 src/owned.rs create mode 100644 src/raw.rs diff --git a/benches/get_path.rs b/benches/get_path.rs index 2211298..81e698f 100644 --- a/benches/get_path.rs +++ b/benches/get_path.rs @@ -23,13 +23,15 @@ fn jsonb_get(data: &[u8], paths: &[&str], expected: &str) { .map(|p| jsonb::jsonpath::Path::DotField(std::borrow::Cow::Borrowed(p))) .collect::>(); let json_path = jsonb::jsonpath::JsonPath { paths }; + let mode = jsonb::jsonpath::Mode::Mixed; - let mut result_data = vec![]; - let mut result_offsets = vec![]; + let raw_jsonb = jsonb::RawJsonb::new(data); + let result_jsonb = raw_jsonb.get_by_path_opt(&json_path, mode).unwrap(); + assert!(result_jsonb.is_some()); + let result_jsonb = result_jsonb.unwrap(); + let result_raw_jsonb = result_jsonb.as_raw(); - jsonb::get_by_path(data, json_path, &mut result_data, &mut result_offsets).unwrap(); - - let s = jsonb::as_str(&result_data).unwrap(); + let s = result_raw_jsonb.as_str().unwrap().unwrap(); assert_eq!(s, expected); } diff --git a/benches/strip_nulls.rs b/benches/strip_nulls.rs index f0944e6..3023a9d 100644 --- a/benches/strip_nulls.rs +++ b/benches/strip_nulls.rs @@ -15,7 +15,7 @@ use std::{fs, io::Read}; use criterion::{criterion_group, criterion_main, Criterion}; -use jsonb::{from_slice, strip_nulls, Value}; +use jsonb::{from_slice, Value}; fn read(file: &str) -> Vec { let mut f = fs::File::open(file).unwrap(); @@ -50,9 +50,9 @@ fn strip_value_nulls(val: &mut Value<'_>) { } fn strip_nulls_fast(data: &[u8]) { - let mut buf = Vec::new(); - strip_nulls(data, &mut buf).unwrap(); - assert!(!buf.is_empty()); + let raw_jsonb = jsonb::RawJsonb::new(data); + let result_jsonb = raw_jsonb.strip_nulls().unwrap(); + assert!(!result_jsonb.is_empty()); } fn add_benchmark(c: &mut Criterion) { diff --git a/src/de.rs b/src/de.rs index f9f237f..817f617 100644 --- a/src/de.rs +++ b/src/de.rs @@ -30,12 +30,12 @@ use super::value::Value; /// This structure can be nested. Each group of structures starts with a `Header`. /// The upper-level `Value` will store the `Header` length or offset of /// the lower-level `Value`. - +/// /// `Header` stores the type of the `Value`, include `Array`, `Object` and `Scalar`, /// `Scalar` has only one `Value`, and a corresponding `JEntry`. /// `Array` and `Object` are nested type, they have multiple lower-level `Values`. /// So the `Header` also stores the number of lower-level `Values`. - +/// /// `JEntry` stores the types of `Scalar Value`, including `Null`, `True`, `False`, /// `Number`, `String` and `Container`. They have three different decode methods. /// 1. `Null`, `True` and `False` can be obtained by `JEntry`, no extra work required. diff --git a/src/from.rs b/src/from.rs index e13b68d..f8803de 100644 --- a/src/from.rs +++ b/src/from.rs @@ -72,25 +72,25 @@ from_float! { f32 f64 } -impl<'a> From> for Value<'a> { +impl From> for Value<'_> { fn from(f: OrderedFloat) -> Self { Value::Number(Number::Float64(f.0 as f64)) } } -impl<'a> From> for Value<'a> { +impl From> for Value<'_> { fn from(f: OrderedFloat) -> Self { Value::Number(Number::Float64(f.0)) } } -impl<'a> From for Value<'a> { +impl From for Value<'_> { fn from(f: bool) -> Self { Value::Bool(f) } } -impl<'a> From for Value<'a> { +impl From for Value<'_> { fn from(f: String) -> Self { Value::String(f.into()) } @@ -142,13 +142,13 @@ impl<'a, K: Into, V: Into>> FromIterator<(K, V)> for Value<'a> } } -impl<'a> From<()> for Value<'a> { +impl From<()> for Value<'_> { fn from((): ()) -> Self { Value::Null } } -impl<'a> From<&JsonValue> for Value<'a> { +impl From<&JsonValue> for Value<'_> { fn from(value: &JsonValue) -> Self { match value { JsonValue::Null => Value::Null, @@ -182,7 +182,7 @@ impl<'a> From<&JsonValue> for Value<'a> { } } -impl<'a> From for Value<'a> { +impl From for Value<'_> { fn from(value: JsonValue) -> Self { (&value).into() } diff --git a/src/functions.rs b/src/functions.rs deleted file mode 100644 index e86c11f..0000000 --- a/src/functions.rs +++ /dev/null @@ -1,3150 +0,0 @@ -// Copyright 2023 Datafuse Labs. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use core::convert::TryInto; -use std::borrow::Cow; -use std::cmp::Ordering; -use std::collections::BTreeMap; -use std::collections::BTreeSet; -use std::collections::VecDeque; -use std::str::from_utf8; -use std::str::from_utf8_unchecked; -use std::str::FromStr; - -use crate::builder::ArrayBuilder; -use crate::builder::ObjectBuilder; -use crate::constants::*; -use crate::error::*; -use crate::from_slice; -use crate::iterator::iteate_object_keys; -use crate::iterator::iterate_array; -use crate::iterator::iterate_object_entries; -use crate::jentry::JEntry; -use crate::jsonpath::JsonPath; -use crate::jsonpath::Mode; -use crate::jsonpath::Selector; -use crate::keypath::KeyPath; -use crate::number::Number; -use crate::parser::parse_value; -use crate::value::Object; -use crate::value::Value; -use rand::distributions::Alphanumeric; -use rand::distributions::DistString; -use rand::thread_rng; -use rand::Rng; - -// builtin functions for `JSONB` bytes and `JSON` strings without decode all Values. -// The input value must be valid `JSONB' or `JSON`. - -/// Build `JSONB` array from items. -/// Assuming that the input values is valid JSONB data. -pub fn build_array<'a>( - items: impl IntoIterator, - buf: &mut Vec, -) -> Result<(), Error> { - let start = buf.len(); - // reserve space for header - buf.resize(start + 4, 0); - let mut len: u32 = 0; - let mut data = Vec::new(); - for value in items.into_iter() { - let header = read_u32(value, 0)?; - let encoded_jentry = match header & CONTAINER_HEADER_TYPE_MASK { - SCALAR_CONTAINER_TAG => { - let jentry = &value[4..8]; - data.extend_from_slice(&value[8..]); - jentry.try_into().unwrap() - } - ARRAY_CONTAINER_TAG | OBJECT_CONTAINER_TAG => { - data.extend_from_slice(value); - (CONTAINER_TAG | value.len() as u32).to_be_bytes() - } - _ => return Err(Error::InvalidJsonbHeader), - }; - len += 1; - buf.extend_from_slice(&encoded_jentry); - } - // write header - let header = ARRAY_CONTAINER_TAG | len; - for (i, b) in header.to_be_bytes().iter().enumerate() { - buf[start + i] = *b; - } - buf.extend_from_slice(&data); - - Ok(()) -} - -/// Build `JSONB` object from items. -/// Assuming that the input values is valid JSONB data. -pub fn build_object<'a, K: AsRef>( - items: impl IntoIterator, - buf: &mut Vec, -) -> Result<(), Error> { - let start = buf.len(); - // reserve space for header - buf.resize(start + 4, 0); - let mut len: u32 = 0; - let mut key_data = Vec::new(); - let mut val_data = Vec::new(); - let mut val_jentries = VecDeque::new(); - for (key, value) in items.into_iter() { - let key = key.as_ref(); - // write key jentry and key data - let encoded_key_jentry = (STRING_TAG | key.len() as u32).to_be_bytes(); - buf.extend_from_slice(&encoded_key_jentry); - key_data.extend_from_slice(key.as_bytes()); - - // build value jentry and write value data - let header = read_u32(value, 0)?; - let encoded_val_jentry = match header & CONTAINER_HEADER_TYPE_MASK { - SCALAR_CONTAINER_TAG => { - let jentry = &value[4..8]; - val_data.extend_from_slice(&value[8..]); - jentry.try_into().unwrap() - } - ARRAY_CONTAINER_TAG | OBJECT_CONTAINER_TAG => { - val_data.extend_from_slice(value); - (CONTAINER_TAG | value.len() as u32).to_be_bytes() - } - _ => return Err(Error::InvalidJsonbHeader), - }; - val_jentries.push_back(encoded_val_jentry); - len += 1; - } - // write header and value jentry - let header = OBJECT_CONTAINER_TAG | len; - for (i, b) in header.to_be_bytes().iter().enumerate() { - buf[start + i] = *b; - } - while let Some(val_jentry) = val_jentries.pop_front() { - buf.extend_from_slice(&val_jentry); - } - // write key data and value data - buf.extend_from_slice(&key_data); - buf.extend_from_slice(&val_data); - - Ok(()) -} - -/// Get the length of `JSONB` array. -pub fn array_length(value: &[u8]) -> Option { - if !is_jsonb(value) { - return match parse_value(value) { - Ok(val) => val.array_length(), - Err(_) => None, - }; - } - let header = read_u32(value, 0).ok()?; - match header & CONTAINER_HEADER_TYPE_MASK { - ARRAY_CONTAINER_TAG => { - let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; - Some(length) - } - _ => None, - } -} - -/// Checks whether the JSON path returns any item for the `JSONB` value. -pub fn path_exists<'a>(value: &'a [u8], json_path: JsonPath<'a>) -> Result { - let selector = Selector::new(json_path, Mode::Mixed); - if !is_jsonb(value) { - match parse_value(value) { - Ok(val) => { - let value = val.to_vec(); - selector.exists(value.as_slice()) - } - Err(_) => Ok(false), - } - } else { - selector.exists(value) - } -} - -/// Returns the result of a JSON path predicate check for the specified `JSONB` value. -pub fn path_match<'a>(value: &'a [u8], json_path: JsonPath<'a>) -> Result { - let selector = Selector::new(json_path, Mode::First); - if !is_jsonb(value) { - let val = parse_value(value)?; - selector.predicate_match(&val.to_vec()) - } else { - selector.predicate_match(value) - } -} - -/// Get the inner elements of `JSONB` value by JSON path. -/// The return value may contains multiple matching elements. -pub fn get_by_path<'a>( - value: &'a [u8], - json_path: JsonPath<'a>, - data: &mut Vec, - offsets: &mut Vec, -) -> Result<(), Error> { - let selector = Selector::new(json_path, Mode::Mixed); - if !is_jsonb(value) { - if let Ok(val) = parse_value(value) { - let value = val.to_vec(); - selector.select(value.as_slice(), data, offsets)?; - } - } else { - selector.select(value, data, offsets)?; - } - Ok(()) -} - -/// Get the inner element of `JSONB` value by JSON path. -/// If there are multiple matching elements, only the first one is returned -pub fn get_by_path_first<'a>( - value: &'a [u8], - json_path: JsonPath<'a>, - data: &mut Vec, - offsets: &mut Vec, -) -> Result<(), Error> { - let selector = Selector::new(json_path, Mode::First); - if !is_jsonb(value) { - if let Ok(val) = parse_value(value) { - let value = val.to_vec(); - selector.select(value.as_slice(), data, offsets)?; - } - } else { - selector.select(value, data, offsets)?; - } - Ok(()) -} - -/// Get the inner elements of `JSONB` value by JSON path. -/// If there are multiple matching elements, return an `JSONB` Array. -pub fn get_by_path_array<'a>( - value: &'a [u8], - json_path: JsonPath<'a>, - data: &mut Vec, - offsets: &mut Vec, -) -> Result<(), Error> { - let selector = Selector::new(json_path, Mode::Array); - if !is_jsonb(value) { - if let Ok(val) = parse_value(value) { - let value = val.to_vec(); - selector.select(value.as_slice(), data, offsets)?; - } - } else { - selector.select(value, data, offsets)?; - } - Ok(()) -} - -/// Get the inner element of `JSONB` Array by index. -pub fn get_by_index(value: &[u8], index: usize) -> Option> { - if !is_jsonb(value) { - return match parse_value(value) { - Ok(val) => match val { - Value::Array(vals) => vals.get(index).map(|v| v.to_vec()), - _ => None, - }, - Err(_) => None, - }; - } - - let header = read_u32(value, 0).ok()?; - match header & CONTAINER_HEADER_TYPE_MASK { - ARRAY_CONTAINER_TAG => { - get_jentry_by_index(value, 0, header, index).map(|(jentry, encoded, val_offset)| { - extract_by_jentry(&jentry, encoded, val_offset, value) - }) - } - _ => None, - } -} - -/// Get the inner element of `JSONB` Object by key name, -/// if `ignore_case` is true, enables case-insensitive matching. -pub fn get_by_name(value: &[u8], name: &str, ignore_case: bool) -> Option> { - if !is_jsonb(value) { - return match parse_value(value) { - Ok(val) => { - if ignore_case { - val.get_by_name_ignore_case(name).map(Value::to_vec) - } else { - match val { - Value::Object(obj) => obj.get(name).map(|v| v.to_vec()), - _ => None, - } - } - } - Err(_) => None, - }; - } - - let header = read_u32(value, 0).ok()?; - match header & CONTAINER_HEADER_TYPE_MASK { - OBJECT_CONTAINER_TAG => get_jentry_by_name(value, 0, header, name, ignore_case).map( - |(jentry, encoded, val_offset)| extract_by_jentry(&jentry, encoded, val_offset, value), - ), - _ => None, - } -} - -/// Extracts JSON sub-object at the specified path, -/// where path elements can be either field keys or array indexes. -pub fn get_by_keypath<'a, I: Iterator>>( - value: &[u8], - keypaths: I, -) -> Option> { - if !is_jsonb(value) { - return match parse_value(value) { - Ok(val) => { - let mut current_val = &val; - for path in keypaths { - let res = match path { - KeyPath::Index(idx) => match current_val { - Value::Array(arr) => { - let length = arr.len() as i32; - if *idx > length || length + *idx < 0 { - None - } else { - let idx = if *idx >= 0 { - *idx as usize - } else { - (length + *idx) as usize - }; - arr.get(idx) - } - } - _ => None, - }, - KeyPath::QuotedName(name) | KeyPath::Name(name) => match current_val { - Value::Object(obj) => obj.get(name.as_ref()), - _ => None, - }, - }; - match res { - Some(v) => current_val = v, - None => return None, - }; - } - Some(current_val.to_vec()) - } - Err(_) => None, - }; - } - - let mut curr_val_offset = 0; - let mut curr_jentry_encoded = 0; - let mut curr_jentry: Option = None; - - for path in keypaths { - if let Some(ref jentry) = curr_jentry { - if jentry.type_code != CONTAINER_TAG { - return None; - } - } - let header = read_u32(value, curr_val_offset).ok()?; - let length = (header & CONTAINER_HEADER_LEN_MASK) as i32; - match (path, header & CONTAINER_HEADER_TYPE_MASK) { - (KeyPath::QuotedName(name) | KeyPath::Name(name), OBJECT_CONTAINER_TAG) => { - match get_jentry_by_name(value, curr_val_offset, header, name, false) { - Some((jentry, encoded, value_offset)) => { - curr_jentry_encoded = encoded; - curr_jentry = Some(jentry); - curr_val_offset = value_offset; - } - None => return None, - }; - } - (KeyPath::Index(idx), ARRAY_CONTAINER_TAG) => { - if *idx > length || length + *idx < 0 { - return None; - } else { - let idx = if *idx >= 0 { - *idx as usize - } else { - (length + *idx) as usize - }; - match get_jentry_by_index(value, curr_val_offset, header, idx) { - Some((jentry, encoded, value_offset)) => { - curr_jentry_encoded = encoded; - curr_jentry = Some(jentry); - curr_val_offset = value_offset; - } - None => return None, - } - } - } - (_, _) => return None, - } - } - // If the key paths is empty, return original value. - if curr_val_offset == 0 { - return Some(value.to_vec()); - } - curr_jentry - .map(|jentry| extract_by_jentry(&jentry, curr_jentry_encoded, curr_val_offset, value)) -} - -/// Checks whether all of the strings exist as top-level keys or array elements. -pub fn exists_all_keys<'a, I: Iterator>(value: &[u8], keys: I) -> bool { - if !is_jsonb(value) { - match parse_value(value) { - Ok(val) => { - for key in keys { - match from_utf8(key) { - Ok(key) => { - if !exists_value_key(&val, key) { - return false; - } - } - Err(_) => return false, - } - } - } - Err(_) => return false, - }; - return true; - } - - let header = read_u32(value, 0).unwrap_or_default(); - - for key in keys { - match from_utf8(key) { - Ok(key) => { - if !exists_jsonb_key(value, header, key) { - return false; - } - } - Err(_) => return false, - } - } - true -} - -/// Checks whether any of the strings exist as top-level keys or array elements. -pub fn exists_any_keys<'a, I: Iterator>(value: &[u8], keys: I) -> bool { - if !is_jsonb(value) { - if let Ok(val) = parse_value(value) { - for key in keys { - if let Ok(key) = from_utf8(key) { - if exists_value_key(&val, key) { - return true; - } - } - } - } - return false; - } - - let header = read_u32(value, 0).unwrap_or_default(); - - for key in keys { - if let Ok(key) = from_utf8(key) { - if exists_jsonb_key(value, header, key) { - return true; - } - } - } - false -} - -fn exists_value_key(value: &Value, key: &str) -> bool { - match value { - Value::Array(arr) => { - let mut found = false; - for item in arr { - let matches = match item { - Value::String(v) => key.eq(v), - _ => false, - }; - if matches { - found = true; - break; - } - } - found - } - Value::Object(obj) => obj.contains_key(key), - _ => false, - } -} - -fn exists_jsonb_key(value: &[u8], header: u32, key: &str) -> bool { - match header & CONTAINER_HEADER_TYPE_MASK { - OBJECT_CONTAINER_TAG => { - let mut matches = false; - for obj_key in iteate_object_keys(value, header) { - if obj_key.eq(key) { - matches = true; - break; - } - } - matches - } - ARRAY_CONTAINER_TAG => { - let mut matches = false; - for (jentry, val) in iterate_array(value, header) { - if jentry.type_code != STRING_TAG { - continue; - } - let val = unsafe { from_utf8_unchecked(val) }; - if val.eq(key) { - matches = true; - break; - } - } - matches - } - _ => false, - } -} - -/// Checks whether the right value contains in the left value. -pub fn contains(left: &[u8], right: &[u8]) -> bool { - if !is_jsonb(left) || !is_jsonb(right) { - return match (from_slice(left), from_slice(right)) { - (Ok(left), Ok(right)) => contains_value(&left, &right), - _ => false, - }; - } - contains_jsonb(left, right).unwrap_or(false) -} - -fn contains_value(left: &Value, right: &Value) -> bool { - // special case for the left array and the right scalar - if left.is_array() && right.is_scalar() { - return left.as_array().unwrap().contains(right); - } - if !left.eq_variant(right) { - return false; - } - match right { - Value::Object(r_obj) => { - let l_obj = left.as_object().unwrap(); - if l_obj.len() < r_obj.len() { - return false; - } - for (r_key, r_val) in r_obj { - match l_obj.get(r_key) { - Some(l_val) => { - if !l_val.eq_variant(r_val) { - return false; - } - if l_val.is_scalar() { - if !l_val.eq(r_val) { - return false; - } - } else if !contains_value(l_val, r_val) { - return false; - } - } - None => return false, - }; - } - true - } - Value::Array(r_arr) => { - let l_arr = left.as_array().unwrap(); - for r_val in r_arr { - if r_val.is_scalar() { - if !l_arr.contains(r_val) { - return false; - } - } else { - let l_nested: Vec<_> = - l_arr.iter().filter(|l_val| !l_val.is_scalar()).collect(); - - let mut contains_nested = false; - - for l_nested_val in l_nested { - if contains_value(l_nested_val, r_val) { - contains_nested = true; - break; - } - } - if !contains_nested { - return false; - } - } - } - true - } - _ => left.eq(right), - } -} - -fn contains_jsonb(left: &[u8], right: &[u8]) -> Result { - let l_header = read_u32(left, 0)?; - let r_header = read_u32(right, 0)?; - - let l_type = l_header & CONTAINER_HEADER_TYPE_MASK; - let r_type = r_header & CONTAINER_HEADER_TYPE_MASK; - - // special case for the left array and the right scalar - if l_type == ARRAY_CONTAINER_TAG && r_type == SCALAR_CONTAINER_TAG { - let r_jentry = JEntry::decode_jentry(read_u32(right, 4)?); - return Ok(array_contains(left, l_header, &right[8..], r_jentry)); - } - - if l_type != r_type { - return Ok(false); - } - - match r_type { - OBJECT_CONTAINER_TAG => { - let l_size = l_header & CONTAINER_HEADER_LEN_MASK; - let r_size = r_header & CONTAINER_HEADER_LEN_MASK; - if l_size < r_size { - return Ok(false); - } - for (r_key, r_jentry, r_val) in iterate_object_entries(right, r_header) { - match get_jentry_by_name(left, 0, l_header, r_key, false) { - Some((l_jentry, _, l_val_offset)) => { - if l_jentry.type_code != r_jentry.type_code { - return Ok(false); - } - let l_val = &left[l_val_offset..l_val_offset + l_jentry.length as usize]; - if r_jentry.type_code != CONTAINER_TAG { - if !l_val.eq(r_val) { - return Ok(false); - } - } else if !contains_jsonb(l_val, r_val)? { - return Ok(false); - } - } - None => return Ok(false), - } - } - Ok(true) - } - ARRAY_CONTAINER_TAG => { - for (r_jentry, r_val) in iterate_array(right, r_header) { - if r_jentry.type_code != CONTAINER_TAG { - if !array_contains(left, l_header, r_val, r_jentry) { - return Ok(false); - } - } else { - let l_nested: Vec<_> = iterate_array(left, l_header) - .filter(|(l_jentry, _)| l_jentry.type_code == CONTAINER_TAG) - .map(|(_, l_val)| l_val) - .collect(); - - let mut contains_nested = false; - - for l_nested_val in l_nested { - if contains_jsonb(l_nested_val, r_val)? { - contains_nested = true; - break; - } - } - if !contains_nested { - return Ok(false); - } - } - } - Ok(true) - } - _ => Ok(left.eq(right)), - } -} - -fn get_jentry_by_name( - value: &[u8], - offset: usize, - header: u32, - name: &str, - ignore_case: bool, -) -> Option<(JEntry, u32, usize)> { - let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; - let mut jentry_offset = offset + 4; - let mut val_offset = offset + 8 * length + 4; - - let mut key_jentries: VecDeque = VecDeque::with_capacity(length); - for _ in 0..length { - let encoded = read_u32(value, jentry_offset).ok()?; - let key_jentry = JEntry::decode_jentry(encoded); - - jentry_offset += 4; - val_offset += key_jentry.length as usize; - key_jentries.push_back(key_jentry); - } - - let mut result = None; - let mut key_offset = offset + 8 * length + 4; - - while let Some(key_jentry) = key_jentries.pop_front() { - let prev_key_offset = key_offset; - key_offset += key_jentry.length as usize; - let key = unsafe { std::str::from_utf8_unchecked(&value[prev_key_offset..key_offset]) }; - - let val_encoded = read_u32(value, jentry_offset).ok()?; - let val_jentry = JEntry::decode_jentry(val_encoded); - let val_length = val_jentry.length as usize; - - // first match the value with the same name, if not found, - // then match the value with the ignoring case name. - if name.eq(key) { - result = Some((val_jentry, val_encoded, val_offset)); - break; - } else if ignore_case && name.eq_ignore_ascii_case(key) && result.is_none() { - result = Some((val_jentry, val_encoded, val_offset)); - } - - jentry_offset += 4; - val_offset += val_length; - } - result -} - -fn get_jentry_by_index( - value: &[u8], - offset: usize, - header: u32, - index: usize, -) -> Option<(JEntry, u32, usize)> { - let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; - if index >= length { - return None; - } - let mut jentry_offset = offset + 4; - let mut val_offset = offset + 4 * length + 4; - - for i in 0..length { - let encoded = read_u32(value, jentry_offset).ok()?; - let jentry = JEntry::decode_jentry(encoded); - let val_length = jentry.length as usize; - if i < index { - jentry_offset += 4; - val_offset += val_length; - continue; - } - return Some((jentry, encoded, val_offset)); - } - None -} - -/// Get the keys of a `JSONB` object. -pub fn object_keys(value: &[u8]) -> Option> { - if !is_jsonb(value) { - return match parse_value(value) { - Ok(val) => val.object_keys().map(|val| val.to_vec()), - Err(_) => None, - }; - } - - let header = read_u32(value, 0).ok()?; - match header & CONTAINER_HEADER_TYPE_MASK { - OBJECT_CONTAINER_TAG => { - let mut buf: Vec = Vec::new(); - let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; - let key_header = ARRAY_CONTAINER_TAG | length as u32; - buf.extend_from_slice(&key_header.to_be_bytes()); - - let mut jentry_offset = 4; - let mut key_offset = 8 * length + 4; - let mut key_offsets = Vec::with_capacity(length); - for _ in 0..length { - let key_encoded = read_u32(value, jentry_offset).ok()?; - let key_jentry = JEntry::decode_jentry(key_encoded); - buf.extend_from_slice(&key_encoded.to_be_bytes()); - - jentry_offset += 4; - key_offset += key_jentry.length as usize; - key_offsets.push(key_offset); - } - let mut prev_key_offset = 8 * length + 4; - for key_offset in key_offsets { - if key_offset > prev_key_offset { - buf.extend_from_slice(&value[prev_key_offset..key_offset]); - } - prev_key_offset = key_offset; - } - Some(buf) - } - _ => None, - } -} - -/// Convert the values of a `JSONB` object to vector of key-value pairs. -pub fn object_each(value: &[u8]) -> Option, Vec)>> { - if !is_jsonb(value) { - return match parse_value(value) { - Ok(val) => match val { - Value::Object(obj) => { - let mut result = Vec::with_capacity(obj.len()); - for (k, v) in obj { - result.push((k.as_bytes().to_vec(), v.to_vec())); - } - Some(result) - } - _ => None, - }, - Err(_) => None, - }; - } - - let header = read_u32(value, 0).ok()?; - - match header & CONTAINER_HEADER_TYPE_MASK { - OBJECT_CONTAINER_TAG => { - let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; - let mut items: Vec<(Vec, Vec)> = Vec::with_capacity(length); - let mut jentries: VecDeque<(JEntry, u32)> = VecDeque::with_capacity(length * 2); - let mut offset = 4; - - for _ in 0..length * 2 { - let encoded = read_u32(value, offset).ok()?; - offset += 4; - jentries.push_back((JEntry::decode_jentry(encoded), encoded)); - } - - let mut keys: VecDeque> = VecDeque::with_capacity(length); - for _ in 0..length { - let (jentry, _) = jentries.pop_front().unwrap(); - let key_len = jentry.length as usize; - keys.push_back(value[offset..offset + key_len].to_vec()); - offset += key_len; - } - - for _ in 0..length { - let (jentry, encoded) = jentries.pop_front().unwrap(); - let key = keys.pop_front().unwrap(); - let val_length = jentry.length as usize; - let val = extract_by_jentry(&jentry, encoded, offset, value); - offset += val_length; - items.push((key, val)); - } - Some(items) - } - _ => None, - } -} - -/// Convert the values of a `JSONB` array to vector. -pub fn array_values(value: &[u8]) -> Option>> { - if !is_jsonb(value) { - return match parse_value(value) { - Ok(val) => match val { - Value::Array(vals) => { - Some(vals.into_iter().map(|val| val.to_vec()).collect::>()) - } - _ => None, - }, - Err(_) => None, - }; - } - - let header = read_u32(value, 0).ok()?; - match header & CONTAINER_HEADER_TYPE_MASK { - ARRAY_CONTAINER_TAG => { - let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; - let mut jentry_offset = 4; - let mut val_offset = 4 * length + 4; - let mut items = Vec::with_capacity(length); - for _ in 0..length { - let encoded = read_u32(value, jentry_offset).ok()?; - let jentry = JEntry::decode_jentry(encoded); - let val_length = jentry.length as usize; - let item = extract_by_jentry(&jentry, encoded, val_offset, value); - items.push(item); - - jentry_offset += 4; - val_offset += val_length; - } - Some(items) - } - _ => None, - } -} - -/// `JSONB` values supports partial decode for comparison, -/// if the values are found to be unequal, the result will be returned immediately. -/// In first level header, values compare as the following order: -/// Scalar Null > Array > Object > Other Scalars(String > Number > Boolean). -pub fn compare(left: &[u8], right: &[u8]) -> Result { - if !is_jsonb(left) && !is_jsonb(right) { - let lres = parse_value(left); - let rres = parse_value(right); - match (lres, rres) { - (Ok(lval), Ok(rval)) => { - let lbuf = lval.to_vec(); - let rbuf = rval.to_vec(); - return compare(&lbuf, &rbuf); - } - (Ok(_), Err(_)) => { - return Ok(Ordering::Greater); - } - (Err(_), Ok(_)) => { - return Ok(Ordering::Less); - } - (Err(_), Err(_)) => { - return Ok(left.cmp(right)); - } - } - } else if !is_jsonb(left) { - match parse_value(left) { - Ok(lval) => { - let lbuf = lval.to_vec(); - return compare(&lbuf, right); - } - Err(_) => { - return Ok(Ordering::Less); - } - } - } else if !is_jsonb(right) { - match parse_value(right) { - Ok(rval) => { - let rbuf = rval.to_vec(); - return compare(left, &rbuf); - } - Err(_) => { - return Ok(Ordering::Greater); - } - } - } - - let left_header = read_u32(left, 0)?; - let right_header = read_u32(right, 0)?; - match ( - left_header & CONTAINER_HEADER_TYPE_MASK, - right_header & CONTAINER_HEADER_TYPE_MASK, - ) { - (SCALAR_CONTAINER_TAG, SCALAR_CONTAINER_TAG) => { - let left_encoded = read_u32(left, 4)?; - let left_jentry = JEntry::decode_jentry(left_encoded); - let right_encoded = read_u32(right, 4)?; - let right_jentry = JEntry::decode_jentry(right_encoded); - compare_scalar(&left_jentry, &left[8..], &right_jentry, &right[8..]) - } - (ARRAY_CONTAINER_TAG, ARRAY_CONTAINER_TAG) => { - compare_array(left_header, &left[4..], right_header, &right[4..]) - } - (OBJECT_CONTAINER_TAG, OBJECT_CONTAINER_TAG) => { - compare_object(left_header, &left[4..], right_header, &right[4..]) - } - (SCALAR_CONTAINER_TAG, ARRAY_CONTAINER_TAG | OBJECT_CONTAINER_TAG) => { - let left_encoded = read_u32(left, 4)?; - let left_jentry = JEntry::decode_jentry(left_encoded); - match left_jentry.type_code { - NULL_TAG => Ok(Ordering::Greater), - _ => Ok(Ordering::Less), - } - } - (ARRAY_CONTAINER_TAG | OBJECT_CONTAINER_TAG, SCALAR_CONTAINER_TAG) => { - let right_encoded = read_u32(right, 4)?; - let right_jentry = JEntry::decode_jentry(right_encoded); - match right_jentry.type_code { - NULL_TAG => Ok(Ordering::Less), - _ => Ok(Ordering::Greater), - } - } - (ARRAY_CONTAINER_TAG, OBJECT_CONTAINER_TAG) => Ok(Ordering::Greater), - (OBJECT_CONTAINER_TAG, ARRAY_CONTAINER_TAG) => Ok(Ordering::Less), - (_, _) => Err(Error::InvalidJsonbHeader), - } -} - -fn extract_by_jentry(jentry: &JEntry, encoded: u32, offset: usize, value: &[u8]) -> Vec { - let length = jentry.length as usize; - match jentry.type_code { - CONTAINER_TAG => value[offset..offset + length].to_vec(), - _ => { - let mut buf = Vec::with_capacity(8 + length); - buf.extend_from_slice(&SCALAR_CONTAINER_TAG.to_be_bytes()); - buf.extend_from_slice(&encoded.to_be_bytes()); - if jentry.length > 0 { - buf.extend_from_slice(&value[offset..offset + length]); - } - buf - } - } -} - -// Different types of values have different levels and are definitely not equal -fn jentry_compare_level(jentry: &JEntry) -> u8 { - match jentry.type_code { - NULL_TAG => NULL_LEVEL, - CONTAINER_TAG => OBJECT_LEVEL, - STRING_TAG => STRING_LEVEL, - NUMBER_TAG => NUMBER_LEVEL, - TRUE_TAG => TRUE_LEVEL, - FALSE_TAG => FALSE_LEVEL, - _ => INVALID_LEVEL, - } -} - -// `Scalar` values compare as the following order -// Null > Container(Array > Object) > String > Number > Boolean -fn compare_scalar( - left_jentry: &JEntry, - left: &[u8], - right_jentry: &JEntry, - right: &[u8], -) -> Result { - let left_level = jentry_compare_level(left_jentry); - let right_level = jentry_compare_level(right_jentry); - if left_level != right_level { - return Ok(left_level.cmp(&right_level)); - } - - match (left_jentry.type_code, right_jentry.type_code) { - (NULL_TAG, NULL_TAG) => Ok(Ordering::Equal), - (CONTAINER_TAG, CONTAINER_TAG) => compare_container(left, right), - (STRING_TAG, STRING_TAG) => { - let left_offset = left_jentry.length as usize; - let left_str = unsafe { std::str::from_utf8_unchecked(&left[..left_offset]) }; - let right_offset = right_jentry.length as usize; - let right_str = unsafe { std::str::from_utf8_unchecked(&right[..right_offset]) }; - Ok(left_str.cmp(right_str)) - } - (NUMBER_TAG, NUMBER_TAG) => { - let left_offset = left_jentry.length as usize; - let left_num = Number::decode(&left[..left_offset])?; - let right_offset = right_jentry.length as usize; - let right_num = Number::decode(&right[..right_offset])?; - Ok(left_num.cmp(&right_num)) - } - (TRUE_TAG, TRUE_TAG) => Ok(Ordering::Equal), - (FALSE_TAG, FALSE_TAG) => Ok(Ordering::Equal), - (_, _) => Err(Error::InvalidJsonbJEntry), - } -} - -fn compare_container(left: &[u8], right: &[u8]) -> Result { - let left_header = read_u32(left, 0)?; - let right_header = read_u32(right, 0)?; - - match ( - left_header & CONTAINER_HEADER_TYPE_MASK, - right_header & CONTAINER_HEADER_TYPE_MASK, - ) { - (ARRAY_CONTAINER_TAG, ARRAY_CONTAINER_TAG) => { - compare_array(left_header, &left[4..], right_header, &right[4..]) - } - (OBJECT_CONTAINER_TAG, OBJECT_CONTAINER_TAG) => { - compare_object(left_header, &left[4..], right_header, &right[4..]) - } - (ARRAY_CONTAINER_TAG, OBJECT_CONTAINER_TAG) => Ok(Ordering::Greater), - (OBJECT_CONTAINER_TAG, ARRAY_CONTAINER_TAG) => Ok(Ordering::Less), - (_, _) => Err(Error::InvalidJsonbHeader), - } -} - -// `Array` values compares each element in turn. -fn compare_array( - left_header: u32, - left: &[u8], - right_header: u32, - right: &[u8], -) -> Result { - let left_length = (left_header & CONTAINER_HEADER_LEN_MASK) as usize; - let right_length = (right_header & CONTAINER_HEADER_LEN_MASK) as usize; - - let mut jentry_offset = 0; - let mut left_val_offset = 4 * left_length; - let mut right_val_offset = 4 * right_length; - let length = if left_length <= right_length { - left_length - } else { - right_length - }; - for _ in 0..length { - let left_encoded = read_u32(left, jentry_offset)?; - let left_jentry = JEntry::decode_jentry(left_encoded); - let right_encoded = read_u32(right, jentry_offset)?; - let right_jentry = JEntry::decode_jentry(right_encoded); - - let order = compare_scalar( - &left_jentry, - &left[left_val_offset..], - &right_jentry, - &right[right_val_offset..], - )?; - if order != Ordering::Equal { - return Ok(order); - } - jentry_offset += 4; - - left_val_offset += left_jentry.length as usize; - right_val_offset += right_jentry.length as usize; - } - - Ok(left_length.cmp(&right_length)) -} - -// `Object` values compares each key-value in turn, -// first compare the key, and then compare the value if the key is equal. -// The larger the key/value, the larger the `Object`. -fn compare_object( - left_header: u32, - left: &[u8], - right_header: u32, - right: &[u8], -) -> Result { - let left_length = (left_header & CONTAINER_HEADER_LEN_MASK) as usize; - let right_length = (right_header & CONTAINER_HEADER_LEN_MASK) as usize; - - let mut left_jentry_offset = 0; - let mut left_val_offset = 8 * left_length; - let mut right_jentry_offset = 0; - let mut right_val_offset = 8 * right_length; - - // read all left key jentries and right key jentries first. - // Note: since the values are stored after the keys, - // we must first read all the key jentries to get the correct value offset. - let mut left_key_jentries: VecDeque = VecDeque::with_capacity(left_length); - let mut right_key_jentries: VecDeque = VecDeque::with_capacity(right_length); - for _ in 0..left_length { - let left_encoded = read_u32(left, left_jentry_offset)?; - let left_key_jentry = JEntry::decode_jentry(left_encoded); - - left_jentry_offset += 4; - left_val_offset += left_key_jentry.length as usize; - left_key_jentries.push_back(left_key_jentry); - } - for _ in 0..right_length { - let right_encoded = read_u32(right, right_jentry_offset)?; - let right_key_jentry = JEntry::decode_jentry(right_encoded); - - right_jentry_offset += 4; - right_val_offset += right_key_jentry.length as usize; - right_key_jentries.push_back(right_key_jentry); - } - - let length = if left_length <= right_length { - left_length - } else { - right_length - }; - - let mut left_key_offset = 8 * left_length; - let mut right_key_offset = 8 * right_length; - for _ in 0..length { - // first compare key, if keys are equal, then compare the value - let left_key_jentry = left_key_jentries.pop_front().unwrap(); - let right_key_jentry = right_key_jentries.pop_front().unwrap(); - - let key_order = compare_scalar( - &left_key_jentry, - &left[left_key_offset..], - &right_key_jentry, - &right[right_key_offset..], - )?; - if key_order != Ordering::Equal { - return Ok(key_order); - } - - let left_encoded = read_u32(left, left_jentry_offset)?; - let left_val_jentry = JEntry::decode_jentry(left_encoded); - let right_encoded = read_u32(right, right_jentry_offset)?; - let right_val_jentry = JEntry::decode_jentry(right_encoded); - - let val_order = compare_scalar( - &left_val_jentry, - &left[left_val_offset..], - &right_val_jentry, - &right[right_val_offset..], - )?; - if val_order != Ordering::Equal { - return Ok(val_order); - } - left_jentry_offset += 4; - right_jentry_offset += 4; - - left_key_offset += left_key_jentry.length as usize; - right_key_offset += right_key_jentry.length as usize; - left_val_offset += left_val_jentry.length as usize; - right_val_offset += right_val_jentry.length as usize; - } - - Ok(left_length.cmp(&right_length)) -} - -/// Returns true if the `JSONB` is a Null. -pub fn is_null(value: &[u8]) -> bool { - as_null(value).is_some() -} - -/// If the `JSONB` is a Null, returns (). Returns None otherwise. -pub fn as_null(value: &[u8]) -> Option<()> { - if !is_jsonb(value) { - return match parse_value(value) { - Ok(val) => val.as_null(), - Err(_) => None, - }; - } - let header = read_u32(value, 0).ok()?; - match header & CONTAINER_HEADER_TYPE_MASK { - SCALAR_CONTAINER_TAG => { - let jentry = read_u32(value, 4).ok()?; - match jentry { - NULL_TAG => Some(()), - _ => None, - } - } - _ => None, - } -} - -/// Returns true if the `JSONB` is a Boolean. Returns false otherwise. -pub fn is_boolean(value: &[u8]) -> bool { - as_bool(value).is_some() -} - -/// If the `JSONB` is a Boolean, returns the associated bool. Returns None otherwise. -pub fn as_bool(value: &[u8]) -> Option { - if !is_jsonb(value) { - return match parse_value(value) { - Ok(val) => val.as_bool(), - Err(_) => None, - }; - } - let header = read_u32(value, 0).ok()?; - match header & CONTAINER_HEADER_TYPE_MASK { - SCALAR_CONTAINER_TAG => { - let jentry = read_u32(value, 4).ok()?; - match jentry { - FALSE_TAG => Some(false), - TRUE_TAG => Some(true), - _ => None, - } - } - _ => None, - } -} - -/// Cast `JSONB` value to Boolean -pub fn to_bool(value: &[u8]) -> Result { - if let Some(v) = as_bool(value) { - return Ok(v); - } else if let Some(v) = as_str(value) { - if &v.to_lowercase() == "true" { - return Ok(true); - } else if &v.to_lowercase() == "false" { - return Ok(false); - } - } - Err(Error::InvalidCast) -} - -/// Returns true if the `JSONB` is a Number. Returns false otherwise. -pub fn is_number(value: &[u8]) -> bool { - as_number(value).is_some() -} - -/// If the `JSONB` is a Number, returns the Number. Returns None otherwise. -pub fn as_number(value: &[u8]) -> Option { - if !is_jsonb(value) { - return match parse_value(value) { - Ok(val) => val.as_number().cloned(), - Err(_) => None, - }; - } - let header = read_u32(value, 0).ok()?; - match header & CONTAINER_HEADER_TYPE_MASK { - SCALAR_CONTAINER_TAG => { - let jentry_encoded = read_u32(value, 4).ok()?; - let jentry = JEntry::decode_jentry(jentry_encoded); - match jentry.type_code { - NUMBER_TAG => { - let length = jentry.length as usize; - Number::decode(&value[8..8 + length]).ok() - } - _ => None, - } - } - _ => None, - } -} - -/// Returns true if the `JSONB` is a i64 Number. Returns false otherwise. -pub fn is_i64(value: &[u8]) -> bool { - as_i64(value).is_some() -} - -/// Cast `JSONB` value to i64 -pub fn to_i64(value: &[u8]) -> Result { - if let Some(v) = as_i64(value) { - return Ok(v); - } else if let Some(v) = as_bool(value) { - if v { - return Ok(1_i64); - } else { - return Ok(0_i64); - } - } else if let Some(v) = as_str(value) { - if let Ok(v) = v.parse::() { - return Ok(v); - } - } - Err(Error::InvalidCast) -} - -/// If the `JSONB` is a Number, represent it as i64 if possible. Returns None otherwise. -pub fn as_i64(value: &[u8]) -> Option { - match as_number(value) { - Some(num) => num.as_i64(), - None => None, - } -} - -/// Returns true if the `JSONB` is a u64 Number. Returns false otherwise. -pub fn is_u64(value: &[u8]) -> bool { - as_u64(value).is_some() -} - -/// If the `JSONB` is a Number, represent it as u64 if possible. Returns None otherwise. -pub fn as_u64(value: &[u8]) -> Option { - match as_number(value) { - Some(num) => num.as_u64(), - None => None, - } -} - -/// Cast `JSONB` value to u64 -pub fn to_u64(value: &[u8]) -> Result { - if let Some(v) = as_u64(value) { - return Ok(v); - } else if let Some(v) = as_bool(value) { - if v { - return Ok(1_u64); - } else { - return Ok(0_u64); - } - } else if let Some(v) = as_str(value) { - if let Ok(v) = v.parse::() { - return Ok(v); - } - } - Err(Error::InvalidCast) -} - -/// Returns true if the `JSONB` is a f64 Number. Returns false otherwise. -pub fn is_f64(value: &[u8]) -> bool { - as_f64(value).is_some() -} - -/// If the `JSONB` is a Number, represent it as f64 if possible. Returns None otherwise. -pub fn as_f64(value: &[u8]) -> Option { - match as_number(value) { - Some(num) => num.as_f64(), - None => None, - } -} - -/// Cast `JSONB` value to f64 -pub fn to_f64(value: &[u8]) -> Result { - if let Some(v) = as_f64(value) { - return Ok(v); - } else if let Some(v) = as_bool(value) { - if v { - return Ok(1_f64); - } else { - return Ok(0_f64); - } - } else if let Some(v) = as_str(value) { - if let Ok(v) = v.parse::() { - return Ok(v); - } - } - Err(Error::InvalidCast) -} - -/// Returns true if the `JSONB` is a String. Returns false otherwise. -pub fn is_string(value: &[u8]) -> bool { - as_str(value).is_some() -} - -/// If the `JSONB` is a String, returns the String. Returns None otherwise. -pub fn as_str(value: &[u8]) -> Option> { - if !is_jsonb(value) { - return match parse_value(value) { - Ok(val) => match val { - Value::String(s) => Some(s.clone()), - _ => None, - }, - Err(_) => None, - }; - } - let header = read_u32(value, 0).ok()?; - match header & CONTAINER_HEADER_TYPE_MASK { - SCALAR_CONTAINER_TAG => { - let jentry_encoded = read_u32(value, 4).ok()?; - let jentry = JEntry::decode_jentry(jentry_encoded); - match jentry.type_code { - STRING_TAG => { - let length = jentry.length as usize; - let s = unsafe { std::str::from_utf8_unchecked(&value[8..8 + length]) }; - Some(Cow::Borrowed(s)) - } - _ => None, - } - } - _ => None, - } -} - -/// Cast `JSONB` value to String -pub fn to_str(value: &[u8]) -> Result { - if let Some(v) = as_str(value) { - return Ok(v.to_string()); - } else if let Some(v) = as_bool(value) { - if v { - return Ok("true".to_string()); - } else { - return Ok("false".to_string()); - } - } else if let Some(v) = as_number(value) { - return Ok(format!("{}", v)); - } - Err(Error::InvalidCast) -} - -/// Returns true if the `JSONB` is An Array. Returns false otherwise. -pub fn is_array(value: &[u8]) -> bool { - if !is_jsonb(value) { - return match parse_value(value) { - Ok(val) => val.is_array(), - Err(_) => false, - }; - } - let header = read_u32(value, 0).unwrap_or_default(); - matches!(header & CONTAINER_HEADER_TYPE_MASK, ARRAY_CONTAINER_TAG) -} - -/// Returns true if the `JSONB` is An Object. Returns false otherwise. -pub fn is_object(value: &[u8]) -> bool { - if !is_jsonb(value) { - return match parse_value(value) { - Ok(val) => val.is_object(), - Err(_) => false, - }; - } - let header = read_u32(value, 0).unwrap_or_default(); - matches!(header & CONTAINER_HEADER_TYPE_MASK, OBJECT_CONTAINER_TAG) -} - -/// Convert `JSONB` value to `serde_json` Value -pub fn to_serde_json(value: &[u8]) -> Result { - if !is_jsonb(value) { - let json_str = std::str::from_utf8(value)?; - return match serde_json::Value::from_str(json_str) { - Ok(v) => Ok(v), - Err(_) => Err(Error::InvalidJson), - }; - } - - containter_to_serde_json(value) -} - -/// Convert `JSONB` value to `serde_json` Object Value -pub fn to_serde_json_object( - value: &[u8], -) -> Result>, Error> { - if !is_jsonb(value) { - let json_str = std::str::from_utf8(value)?; - return match serde_json::Value::from_str(json_str) { - Ok(v) => match v { - serde_json::Value::Object(obj) => Ok(Some(obj.clone())), - _ => Ok(None), - }, - Err(_) => Err(Error::InvalidJson), - }; - } - - containter_to_serde_json_object(value) -} - -fn containter_to_serde_json_object( - value: &[u8], -) -> Result>, Error> { - let header = read_u32(value, 0).unwrap_or_default(); - let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; - - let obj_value = match header & CONTAINER_HEADER_TYPE_MASK { - OBJECT_CONTAINER_TAG => { - let mut obj = serde_json::Map::with_capacity(length); - for (key, jentry, val) in iterate_object_entries(value, header) { - let item = scalar_to_serde_json(jentry, val)?; - obj.insert(key.to_string(), item); - } - Some(obj) - } - ARRAY_CONTAINER_TAG | SCALAR_CONTAINER_TAG => None, - _ => { - return Err(Error::InvalidJsonb); - } - }; - Ok(obj_value) -} - -fn containter_to_serde_json(value: &[u8]) -> Result { - let header = read_u32(value, 0).unwrap_or_default(); - let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; - - let json_value = match header & CONTAINER_HEADER_TYPE_MASK { - OBJECT_CONTAINER_TAG => { - let mut obj = serde_json::Map::with_capacity(length); - for (key, jentry, val) in iterate_object_entries(value, header) { - let item = scalar_to_serde_json(jentry, val)?; - obj.insert(key.to_string(), item); - } - serde_json::Value::Object(obj) - } - ARRAY_CONTAINER_TAG => { - let mut arr = Vec::with_capacity(length); - for (jentry, val) in iterate_array(value, header) { - let item = scalar_to_serde_json(jentry, val)?; - arr.push(item); - } - serde_json::Value::Array(arr) - } - SCALAR_CONTAINER_TAG => { - let encoded = match read_u32(value, 4) { - Ok(encoded) => encoded, - Err(_) => { - return Err(Error::InvalidJsonb); - } - }; - let jentry = JEntry::decode_jentry(encoded); - scalar_to_serde_json(jentry, &value[8..])? - } - _ => { - return Err(Error::InvalidJsonb); - } - }; - Ok(json_value) -} - -fn scalar_to_serde_json(jentry: JEntry, value: &[u8]) -> Result { - let json_value = match jentry.type_code { - NULL_TAG => serde_json::Value::Null, - TRUE_TAG => serde_json::Value::Bool(true), - FALSE_TAG => serde_json::Value::Bool(false), - NUMBER_TAG => { - let len = jentry.length as usize; - let n = Number::decode(&value[..len])?; - match n { - Number::Int64(v) => serde_json::Value::Number(serde_json::Number::from(v)), - Number::UInt64(v) => serde_json::Value::Number(serde_json::Number::from(v)), - Number::Float64(v) => match serde_json::Number::from_f64(v) { - Some(v) => serde_json::Value::Number(v), - None => { - return Err(Error::InvalidJson); - } - }, - } - } - STRING_TAG => { - let len = jentry.length as usize; - let s = unsafe { String::from_utf8_unchecked(value[..len].to_vec()) }; - serde_json::Value::String(s) - } - CONTAINER_TAG => containter_to_serde_json(value)?, - _ => { - return Err(Error::InvalidJsonb); - } - }; - Ok(json_value) -} - -/// Convert `JSONB` value to String -pub fn to_string(value: &[u8]) -> String { - if !is_jsonb(value) { - // empty value as default null - if value.is_empty() { - return "null".to_string(); - } else { - return String::from_utf8_lossy(value).to_string(); - } - } - - let mut json = String::new(); - if container_to_string(value, &mut 0, &mut json, &PrettyOpts::new(false)).is_err() { - json.clear(); - json.push_str("null"); - } - json -} - -/// Convert `JSONB` value to pretty String -pub fn to_pretty_string(value: &[u8]) -> String { - if !is_jsonb(value) { - // empty value as default null - if value.is_empty() { - return "null".to_string(); - } else { - return String::from_utf8_lossy(value).to_string(); - } - } - - let mut json = String::new(); - if container_to_string(value, &mut 0, &mut json, &PrettyOpts::new(true)).is_err() { - json.clear(); - json.push_str("null"); - } - json -} - -struct PrettyOpts { - enabled: bool, - indent: usize, -} - -impl PrettyOpts { - fn new(enabled: bool) -> Self { - Self { enabled, indent: 0 } - } - - fn inc_indent(&self) -> Self { - Self { - enabled: self.enabled, - indent: self.indent + 2, - } - } - - fn generate_indent(&self) -> String { - String::from_utf8(vec![0x20; self.indent]).unwrap() - } -} - -fn container_to_string( - value: &[u8], - offset: &mut usize, - json: &mut String, - pretty_opts: &PrettyOpts, -) -> Result<(), Error> { - let header = read_u32(value, *offset)?; - match header & CONTAINER_HEADER_TYPE_MASK { - SCALAR_CONTAINER_TAG => { - let mut jentry_offset = 4 + *offset; - let mut value_offset = 8 + *offset; - scalar_to_string( - value, - &mut jentry_offset, - &mut value_offset, - json, - pretty_opts, - )?; - } - ARRAY_CONTAINER_TAG => { - if pretty_opts.enabled { - json.push_str("[\n"); - } else { - json.push('['); - } - let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; - let mut jentry_offset = 4 + *offset; - let mut value_offset = 4 + *offset + 4 * length; - let inner_pretty_ops = pretty_opts.inc_indent(); - for i in 0..length { - if i > 0 { - if pretty_opts.enabled { - json.push_str(",\n"); - } else { - json.push(','); - } - } - if pretty_opts.enabled { - json.push_str(&inner_pretty_ops.generate_indent()); - } - scalar_to_string( - value, - &mut jentry_offset, - &mut value_offset, - json, - &inner_pretty_ops, - )?; - } - if pretty_opts.enabled { - json.push('\n'); - json.push_str(&pretty_opts.generate_indent()); - } - json.push(']'); - } - OBJECT_CONTAINER_TAG => { - if pretty_opts.enabled { - json.push_str("{\n"); - } else { - json.push('{'); - } - let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; - let mut jentry_offset = 4 + *offset; - let mut key_offset = 4 + *offset + 8 * length; - let mut keys = VecDeque::with_capacity(length); - for _ in 0..length { - let jentry_encoded = read_u32(value, jentry_offset)?; - let jentry = JEntry::decode_jentry(jentry_encoded); - let key_length = jentry.length as usize; - keys.push_back((key_offset, key_offset + key_length)); - jentry_offset += 4; - key_offset += key_length; - } - let mut value_offset = key_offset; - let inner_pretty_ops = pretty_opts.inc_indent(); - for i in 0..length { - if i > 0 { - if pretty_opts.enabled { - json.push_str(",\n"); - } else { - json.push(','); - } - } - let (key_start, key_end) = keys.pop_front().unwrap(); - if pretty_opts.enabled { - json.push_str(&inner_pretty_ops.generate_indent()); - escape_scalar_string(value, key_start, key_end, json); - json.push_str(": "); - } else { - escape_scalar_string(value, key_start, key_end, json); - json.push(':'); - } - scalar_to_string( - value, - &mut jentry_offset, - &mut value_offset, - json, - &inner_pretty_ops, - )?; - } - if pretty_opts.enabled { - json.push('\n'); - json.push_str(&pretty_opts.generate_indent()); - } - json.push('}'); - } - _ => {} - } - Ok(()) -} - -fn scalar_to_string( - value: &[u8], - jentry_offset: &mut usize, - value_offset: &mut usize, - json: &mut String, - pretty_opts: &PrettyOpts, -) -> Result<(), Error> { - let jentry_encoded = read_u32(value, *jentry_offset)?; - let jentry = JEntry::decode_jentry(jentry_encoded); - let length = jentry.length as usize; - match jentry.type_code { - NULL_TAG => json.push_str("null"), - TRUE_TAG => json.push_str("true"), - FALSE_TAG => json.push_str("false"), - NUMBER_TAG => { - let num = Number::decode(&value[*value_offset..*value_offset + length])?; - json.push_str(&num.to_string()); - } - STRING_TAG => { - escape_scalar_string(value, *value_offset, *value_offset + length, json); - } - CONTAINER_TAG => { - container_to_string(value, value_offset, json, pretty_opts)?; - } - _ => {} - } - *jentry_offset += 4; - *value_offset += length; - Ok(()) -} - -fn escape_scalar_string(value: &[u8], start: usize, end: usize, json: &mut String) { - json.push('\"'); - let mut last_start = start; - for i in start..end { - // add backslash for escaped characters. - let c = match value[i] { - 0x5C => "\\\\", - 0x22 => "\\\"", - 0x08 => "\\b", - 0x0C => "\\f", - 0x0A => "\\n", - 0x0D => "\\r", - 0x09 => "\\t", - _ => { - continue; - } - }; - if i > last_start { - let val = String::from_utf8_lossy(&value[last_start..i]); - json.push_str(&val); - } - json.push_str(c); - last_start = i + 1; - } - if last_start < end { - let val = String::from_utf8_lossy(&value[last_start..end]); - json.push_str(&val); - } - json.push('\"'); -} - -/// Convert `JSONB` value to comparable vector. -/// The compare rules are the same as the `compare` function. -/// Scalar Null > Array > Object > Other Scalars(String > Number > Boolean). -pub fn convert_to_comparable(value: &[u8], buf: &mut Vec) { - let depth = 0; - if !is_jsonb(value) { - match parse_value(value) { - Ok(val) => { - let val_buf = val.to_vec(); - convert_to_comparable(&val_buf, buf); - } - Err(_) => { - buf.push(depth); - buf.push(INVALID_LEVEL); - buf.extend_from_slice(value); - } - } - return; - } - let header = read_u32(value, 0).unwrap_or_default(); - match header & CONTAINER_HEADER_TYPE_MASK { - SCALAR_CONTAINER_TAG => { - let encoded = match read_u32(value, 4) { - Ok(encoded) => encoded, - Err(_) => { - return; - } - }; - let jentry = JEntry::decode_jentry(encoded); - scalar_convert_to_comparable(depth, &jentry, &value[8..], buf); - } - ARRAY_CONTAINER_TAG => { - buf.push(depth); - buf.push(ARRAY_LEVEL); - let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; - array_convert_to_comparable(depth + 1, length, &value[4..], buf); - } - OBJECT_CONTAINER_TAG => { - buf.push(depth); - buf.push(OBJECT_LEVEL); - let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; - object_convert_to_comparable(depth + 1, length, &value[4..], buf); - } - _ => {} - } -} - -fn scalar_convert_to_comparable(depth: u8, jentry: &JEntry, value: &[u8], buf: &mut Vec) { - buf.push(depth); - let level = jentry_compare_level(jentry); - match jentry.type_code { - CONTAINER_TAG => { - let header = match read_u32(value, 0) { - Ok(header) => header, - Err(_) => { - return; - } - }; - let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; - match header & CONTAINER_HEADER_TYPE_MASK { - ARRAY_CONTAINER_TAG => { - buf.push(ARRAY_LEVEL); - array_convert_to_comparable(depth + 1, length, &value[4..], buf); - } - OBJECT_CONTAINER_TAG => { - buf.push(OBJECT_LEVEL); - object_convert_to_comparable(depth + 1, length, &value[4..], buf); - } - _ => {} - } - } - _ => { - buf.push(level); - match jentry.type_code { - STRING_TAG => { - let length = jentry.length as usize; - buf.extend_from_slice(&value[..length]); - } - NUMBER_TAG => { - let length = jentry.length as usize; - if let Ok(num) = Number::decode(&value[..length]) { - let n = num.as_f64().unwrap(); - // https://github.com/rust-lang/rust/blob/9c20b2a8cc7588decb6de25ac6a7912dcef24d65/library/core/src/num/f32.rs#L1176-L1260 - let s = n.to_bits() as i64; - let v = s ^ (((s >> 63) as u64) >> 1) as i64; - let mut b = v.to_be_bytes(); - // Toggle top "sign" bit to ensure consistent sort order - b[0] ^= 0x80; - buf.extend_from_slice(&b); - } - } - _ => {} - } - } - } -} - -fn array_convert_to_comparable(depth: u8, length: usize, value: &[u8], buf: &mut Vec) { - let mut jentry_offset = 0; - let mut val_offset = 4 * length; - for _ in 0..length { - let encoded = match read_u32(value, jentry_offset) { - Ok(encoded) => encoded, - Err(_) => { - return; - } - }; - let jentry = JEntry::decode_jentry(encoded); - scalar_convert_to_comparable(depth, &jentry, &value[val_offset..], buf); - jentry_offset += 4; - val_offset += jentry.length as usize; - } -} - -fn object_convert_to_comparable(depth: u8, length: usize, value: &[u8], buf: &mut Vec) { - let mut jentry_offset = 0; - let mut val_offset = 8 * length; - - // read all key jentries first - let mut key_jentries: VecDeque = VecDeque::with_capacity(length); - for _ in 0..length { - let encoded = match read_u32(value, jentry_offset) { - Ok(encoded) => encoded, - Err(_) => { - return; - } - }; - let key_jentry = JEntry::decode_jentry(encoded); - - jentry_offset += 4; - val_offset += key_jentry.length as usize; - key_jentries.push_back(key_jentry); - } - - let mut key_offset = 8 * length; - for _ in 0..length { - let key_jentry = key_jentries.pop_front().unwrap(); - scalar_convert_to_comparable(depth, &key_jentry, &value[key_offset..], buf); - - let encoded = match read_u32(value, jentry_offset) { - Ok(encoded) => encoded, - Err(_) => { - return; - } - }; - let val_jentry = JEntry::decode_jentry(encoded); - scalar_convert_to_comparable(depth, &val_jentry, &value[val_offset..], buf); - - jentry_offset += 4; - key_offset += key_jentry.length as usize; - val_offset += val_jentry.length as usize; - } -} - -/// generate random JSONB value -pub fn rand_value() -> Value<'static> { - let mut rng = thread_rng(); - let val = match rng.gen_range(0..=2) { - 0 => { - let len = rng.gen_range(0..=5); - let mut values = Vec::with_capacity(len); - for _ in 0..len { - values.push(rand_scalar_value()); - } - Value::Array(values) - } - 1 => { - let len = rng.gen_range(0..=5); - let mut obj = Object::new(); - for _ in 0..len { - let k = Alphanumeric.sample_string(&mut rng, 5); - let v = rand_scalar_value(); - obj.insert(k, v); - } - Value::Object(obj) - } - _ => rand_scalar_value(), - }; - val -} - -fn rand_scalar_value() -> Value<'static> { - let mut rng = thread_rng(); - let val = match rng.gen_range(0..=3) { - 0 => { - let v = rng.gen_bool(0.5); - Value::Bool(v) - } - 1 => { - let s = Alphanumeric.sample_string(&mut rng, 5); - Value::String(Cow::from(s)) - } - 2 => match rng.gen_range(0..=2) { - 0 => { - let n: u64 = rng.gen_range(0..=100000); - Value::Number(Number::UInt64(n)) - } - 1 => { - let n: i64 = rng.gen_range(-100000..=100000); - Value::Number(Number::Int64(n)) - } - _ => { - let n: f64 = rng.gen_range(-4000.0..1.3e5); - Value::Number(Number::Float64(n)) - } - }, - _ => Value::Null, - }; - val -} - -/// Traverse all the string fields in a jsonb value and check whether the conditions are met. -pub fn traverse_check_string(value: &[u8], func: impl Fn(&[u8]) -> bool) -> bool { - if !is_jsonb(value) { - match parse_value(value) { - Ok(val) => { - let val_buf = val.to_vec(); - return traverse_check_string(&val_buf, func); - } - Err(_) => { - return false; - } - } - } - - let mut offsets = VecDeque::new(); - offsets.push_back(0); - - while let Some(offset) = offsets.pop_front() { - let header = match read_u32(value, offset) { - Ok(header) => header, - Err(_) => { - return false; - } - }; - let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; - - let size = match header & CONTAINER_HEADER_TYPE_MASK { - SCALAR_CONTAINER_TAG => 1, - ARRAY_CONTAINER_TAG => length, - OBJECT_CONTAINER_TAG => length * 2, - _ => unreachable!("invalid jsonb value"), - }; - - let mut jentry_offset = offset + 4; - let mut val_offset = offset + 4 + 4 * size; - for _ in 0..size { - let encoded = match read_u32(value, jentry_offset) { - Ok(encoded) => encoded, - Err(_) => { - return false; - } - }; - let jentry = JEntry::decode_jentry(encoded); - match jentry.type_code { - CONTAINER_TAG => { - offsets.push_back(val_offset); - } - STRING_TAG => { - let val_length = jentry.length as usize; - if func(&value[val_offset..val_offset + val_length]) { - return true; - } - } - _ => {} - } - jentry_offset += 4; - val_offset += jentry.length as usize; - } - } - - false -} - -/// Concatenates two jsonb values. Concatenating two arrays generates an array containing all the elements of each input. -/// Concatenating two objects generates an object containing the union of their keys, taking the second object's value when there are duplicate keys. -/// All other cases are treated by converting a non-array input into a single-element array, and then proceeding as for two arrays. -pub fn concat(left: &[u8], right: &[u8], buf: &mut Vec) -> Result<(), Error> { - if !is_jsonb(left) || !is_jsonb(right) { - let left_val = from_slice(left)?; - let right_val = from_slice(right)?; - let result = concat_values(left_val, right_val); - result.write_to_vec(buf); - return Ok(()); - } - concat_jsonb(left, right, buf) -} - -fn concat_values<'a>(left: Value<'a>, right: Value<'a>) -> Value<'a> { - match (left, right) { - (Value::Object(left), Value::Object(mut right)) => { - let mut result = left; - result.append(&mut right); - Value::Object(result) - } - (Value::Array(left), Value::Array(mut right)) => { - let mut result = left; - result.append(&mut right); - Value::Array(result) - } - (left, Value::Array(mut right)) => { - let mut result = Vec::with_capacity(right.len() + 1); - result.push(left); - result.append(&mut right); - Value::Array(result) - } - (Value::Array(left), right) => { - let mut result = left; - result.push(right); - Value::Array(result) - } - (left, right) => Value::Array(vec![left, right]), - } -} - -fn concat_jsonb(left: &[u8], right: &[u8], buf: &mut Vec) -> Result<(), Error> { - let left_header = read_u32(left, 0)?; - let right_header = read_u32(right, 0)?; - - let left_len = (left_header & CONTAINER_HEADER_LEN_MASK) as usize; - let right_len = (right_header & CONTAINER_HEADER_LEN_MASK) as usize; - - let left_type = left_header & CONTAINER_HEADER_TYPE_MASK; - let right_type = right_header & CONTAINER_HEADER_TYPE_MASK; - - match (left_type, right_type) { - (OBJECT_CONTAINER_TAG, OBJECT_CONTAINER_TAG) => { - let mut builder = ObjectBuilder::new(); - for (key, jentry, item) in iterate_object_entries(left, left_header) { - builder.push_raw(key, jentry, item); - } - for (key, jentry, item) in iterate_object_entries(right, right_header) { - builder.push_raw(key, jentry, item); - } - builder.build_into(buf); - } - (ARRAY_CONTAINER_TAG, ARRAY_CONTAINER_TAG) => { - let mut builder = ArrayBuilder::new(left_len + right_len); - for (jentry, item) in iterate_array(left, left_header) { - builder.push_raw(jentry, item); - } - for (jentry, item) in iterate_array(right, right_header) { - builder.push_raw(jentry, item); - } - builder.build_into(buf); - } - (_, ARRAY_CONTAINER_TAG) => { - let mut builder = ArrayBuilder::new(right_len + 1); - match left_type { - OBJECT_CONTAINER_TAG => { - let jentry = JEntry::make_container_jentry(left.len()); - builder.push_raw(jentry, left); - } - _ => { - let jentry = JEntry::decode_jentry(read_u32(left, 4)?); - builder.push_raw(jentry, &left[8..]); - } - }; - for (jentry, item) in iterate_array(right, right_header) { - builder.push_raw(jentry, item); - } - builder.build_into(buf); - } - (ARRAY_CONTAINER_TAG, _) => { - let mut builder = ArrayBuilder::new(left_len + 1); - for (jentry, item) in iterate_array(left, left_header) { - builder.push_raw(jentry, item); - } - match right_type { - OBJECT_CONTAINER_TAG => { - let jentry = JEntry::make_container_jentry(right.len()); - builder.push_raw(jentry, right); - } - _ => { - let jentry = JEntry::decode_jentry(read_u32(right, 4)?); - builder.push_raw(jentry, &right[8..]); - } - }; - builder.build_into(buf); - } - (_, _) => { - let mut builder = ArrayBuilder::new(2); - match left_type { - OBJECT_CONTAINER_TAG => { - let jentry = JEntry::make_container_jentry(left.len()); - builder.push_raw(jentry, left); - } - _ => { - let jentry = JEntry::decode_jentry(read_u32(left, 4)?); - builder.push_raw(jentry, &left[8..]); - } - }; - match right_type { - OBJECT_CONTAINER_TAG => { - let jentry = JEntry::make_container_jentry(right.len()); - builder.push_raw(jentry, right); - } - _ => { - let jentry = JEntry::decode_jentry(read_u32(right, 4)?); - builder.push_raw(jentry, &right[8..]); - } - }; - builder.build_into(buf); - } - } - Ok(()) -} - -/// Deletes a value from a JSON object by the specified path, -/// where path elements can be either field keys or array indexes. -pub fn delete_by_keypath<'a, I: Iterator>>( - value: &[u8], - keypath: I, - buf: &mut Vec, -) -> Result<(), Error> { - let mut keypath: VecDeque<_> = keypath.collect(); - if !is_jsonb(value) { - let mut value = parse_value(value)?; - match value { - Value::Array(ref mut arr) => delete_value_array_by_keypath(arr, &mut keypath), - Value::Object(ref mut obj) => delete_value_object_by_keypath(obj, &mut keypath), - _ => return Err(Error::InvalidJsonType), - }; - value.write_to_vec(buf); - return Ok(()); - } - delete_by_keypath_jsonb(value, keypath, buf) -} - -fn delete_value_array_by_keypath<'a>( - arr: &mut Vec>, - keypath: &mut VecDeque<&'a KeyPath<'a>>, -) { - if let Some(KeyPath::Index(idx)) = keypath.pop_front() { - let len = arr.len() as i32; - let idx = if *idx < 0 { len - idx.abs() } else { *idx }; - if idx < 0 || idx >= len { - return; - } - if keypath.is_empty() { - arr.remove(idx as usize); - } else { - match arr[idx as usize] { - Value::Array(ref mut arr) => delete_value_array_by_keypath(arr, keypath), - Value::Object(ref mut obj) => delete_value_object_by_keypath(obj, keypath), - _ => {} - } - } - } -} - -fn delete_value_object_by_keypath<'a>( - obj: &mut BTreeMap>, - keypath: &mut VecDeque<&'a KeyPath<'a>>, -) { - if let Some(KeyPath::QuotedName(name) | KeyPath::Name(name)) = keypath.pop_front() { - if keypath.is_empty() { - obj.remove(name.as_ref()); - } else if let Some(val) = obj.get_mut(name.as_ref()) { - match val { - Value::Array(ref mut arr) => delete_value_array_by_keypath(arr, keypath), - Value::Object(ref mut obj) => delete_value_object_by_keypath(obj, keypath), - _ => {} - } - } - } -} - -fn delete_by_keypath_jsonb<'a>( - value: &[u8], - mut keypath: VecDeque<&'a KeyPath<'a>>, - buf: &mut Vec, -) -> Result<(), Error> { - let header = read_u32(value, 0)?; - match header & CONTAINER_HEADER_TYPE_MASK { - ARRAY_CONTAINER_TAG => { - match delete_jsonb_array_by_keypath(value, header, &mut keypath)? { - Some(builder) => { - builder.build_into(buf); - } - None => { - buf.extend_from_slice(value); - } - }; - } - OBJECT_CONTAINER_TAG => { - match delete_jsonb_object_by_keypath(value, header, &mut keypath)? { - Some(builder) => { - builder.build_into(buf); - } - None => { - buf.extend_from_slice(value); - } - } - } - _ => return Err(Error::InvalidJsonType), - } - Ok(()) -} - -fn delete_jsonb_array_by_keypath<'a, 'b>( - value: &'b [u8], - header: u32, - keypath: &mut VecDeque<&'a KeyPath<'a>>, -) -> Result>, Error> { - let len = (header & CONTAINER_HEADER_LEN_MASK) as i32; - match keypath.pop_front() { - Some(KeyPath::Index(idx)) => { - let idx = if *idx < 0 { len - idx.abs() } else { *idx }; - if idx < 0 || idx >= len { - return Ok(None); - } - let mut builder = ArrayBuilder::new(len as usize); - let idx = idx as usize; - for (i, entry) in iterate_array(value, header).enumerate() { - if i != idx { - builder.push_raw(entry.0, entry.1); - } else if !keypath.is_empty() { - let item_value = entry.1; - match entry.0.type_code { - CONTAINER_TAG => { - let item_header = read_u32(item_value, 0)?; - match item_header & CONTAINER_HEADER_TYPE_MASK { - ARRAY_CONTAINER_TAG => { - match delete_jsonb_array_by_keypath( - item_value, - item_header, - keypath, - )? { - Some(item_builder) => builder.push_array(item_builder), - None => return Ok(None), - } - } - OBJECT_CONTAINER_TAG => { - match delete_jsonb_object_by_keypath( - item_value, - item_header, - keypath, - )? { - Some(item_builder) => builder.push_object(item_builder), - None => return Ok(None), - } - } - _ => unreachable!(), - } - } - _ => return Ok(None), - } - } - } - Ok(Some(builder)) - } - _ => Ok(None), - } -} - -fn delete_jsonb_object_by_keypath<'a, 'b>( - value: &'b [u8], - header: u32, - keypath: &mut VecDeque<&'a KeyPath<'a>>, -) -> Result>, Error> { - match keypath.pop_front() { - Some(KeyPath::QuotedName(name) | KeyPath::Name(name)) => { - let mut builder = ObjectBuilder::new(); - for (key, jentry, item) in iterate_object_entries(value, header) { - if !key.eq(name) { - builder.push_raw(key, jentry, item); - } else if !keypath.is_empty() { - match jentry.type_code { - CONTAINER_TAG => { - let item_header = read_u32(item, 0)?; - match item_header & CONTAINER_HEADER_TYPE_MASK { - ARRAY_CONTAINER_TAG => { - match delete_jsonb_array_by_keypath(item, item_header, keypath)? - { - Some(item_builder) => builder.push_array(key, item_builder), - None => return Ok(None), - } - } - OBJECT_CONTAINER_TAG => { - match delete_jsonb_object_by_keypath( - item, - item_header, - keypath, - )? { - Some(item_builder) => { - builder.push_object(key, item_builder) - } - None => return Ok(None), - } - } - _ => unreachable!(), - } - } - _ => return Ok(None), - } - } - } - Ok(Some(builder)) - } - _ => Ok(None), - } -} - -/// Deletes a key (and its value) from a JSON object, or matching string value(s) from a JSON array. -pub fn delete_by_name(value: &[u8], name: &str, buf: &mut Vec) -> Result<(), Error> { - if !is_jsonb(value) { - let mut val = parse_value(value)?; - match &mut val { - Value::Array(arr) => { - arr.retain(|item| !matches!(item, Value::String(v) if v.eq(name))); - } - Value::Object(obj) => { - obj.remove(name); - } - _ => return Err(Error::InvalidJsonType), - }; - val.write_to_vec(buf); - return Ok(()); - } - delete_jsonb_by_name(value, name, buf) -} - -fn delete_jsonb_by_name(value: &[u8], name: &str, buf: &mut Vec) -> Result<(), Error> { - let header = read_u32(value, 0)?; - - match header & CONTAINER_HEADER_TYPE_MASK { - OBJECT_CONTAINER_TAG => { - let mut builder = ObjectBuilder::new(); - for (key, jentry, item) in iterate_object_entries(value, header) { - if !key.eq(name) { - builder.push_raw(key, jentry, item); - } - } - builder.build_into(buf); - } - ARRAY_CONTAINER_TAG => { - let mut builder = ArrayBuilder::new((header & CONTAINER_HEADER_LEN_MASK) as usize); - for (jentry, item) in iterate_array(value, header) { - let matches = match jentry.type_code { - STRING_TAG => { - let v = unsafe { from_utf8_unchecked(item) }; - v.eq(name) - } - _ => false, - }; - if !matches { - builder.push_raw(jentry, item); - } - } - builder.build_into(buf); - } - _ => return Err(Error::InvalidJsonType), - } - Ok(()) -} - -/// Deletes the array element with specified index (negative integers count from the end). -pub fn delete_by_index(value: &[u8], index: i32, buf: &mut Vec) -> Result<(), Error> { - if !is_jsonb(value) { - let mut val = parse_value(value)?; - match &mut val { - Value::Array(arr) => { - let len = arr.len() as i32; - let index = if index < 0 { len - index.abs() } else { index }; - if index >= 0 && index < len { - arr.remove(index as usize); - } - } - _ => return Err(Error::InvalidJsonType), - }; - val.write_to_vec(buf); - return Ok(()); - } - delete_jsonb_by_index(value, index, buf) -} - -fn delete_jsonb_by_index(value: &[u8], index: i32, buf: &mut Vec) -> Result<(), Error> { - let header = read_u32(value, 0)?; - - match header & CONTAINER_HEADER_TYPE_MASK { - ARRAY_CONTAINER_TAG => { - let len = (header & CONTAINER_HEADER_LEN_MASK) as i32; - let index = if index < 0 { len - index.abs() } else { index }; - if index < 0 || index >= len { - buf.extend_from_slice(value); - } else { - let mut builder = ArrayBuilder::new((header & CONTAINER_HEADER_LEN_MASK) as usize); - let index = index as usize; - for (i, entry) in iterate_array(value, header).enumerate() { - if i != index { - builder.push_raw(entry.0, entry.1); - } - } - builder.build_into(buf); - } - } - _ => return Err(Error::InvalidJsonType), - } - Ok(()) -} - -/// Insert a new value into a JSONB array value by the specified position. -pub fn array_insert( - value: &[u8], - pos: i32, - new_value: &[u8], - buf: &mut Vec, -) -> Result<(), Error> { - if !is_jsonb(value) { - let value = parse_value(value)?; - let mut val_buf = Vec::new(); - value.write_to_vec(&mut val_buf); - if !is_jsonb(new_value) { - let new_value = parse_value(new_value)?; - let mut new_val_buf = Vec::new(); - new_value.write_to_vec(&mut new_val_buf); - return array_insert_jsonb(&val_buf, pos, &new_val_buf, buf); - } - return array_insert_jsonb(&val_buf, pos, new_value, buf); - } - array_insert_jsonb(value, pos, new_value, buf) -} - -fn array_insert_jsonb( - value: &[u8], - pos: i32, - new_value: &[u8], - buf: &mut Vec, -) -> Result<(), Error> { - let header = read_u32(value, 0)?; - let len = if header & CONTAINER_HEADER_TYPE_MASK == ARRAY_CONTAINER_TAG { - (header & CONTAINER_HEADER_LEN_MASK) as i32 - } else { - 1 - }; - - let idx = if pos < 0 { len - pos.abs() } else { pos }; - let idx = if idx < 0 { - 0 - } else if idx > len { - len - } else { - idx - } as usize; - let len = len as usize; - - let mut items = VecDeque::with_capacity(len); - match header & CONTAINER_HEADER_TYPE_MASK { - ARRAY_CONTAINER_TAG => { - for (jentry, item) in iterate_array(value, header) { - items.push_back((jentry, item)); - } - } - OBJECT_CONTAINER_TAG => { - let jentry = JEntry::make_container_jentry(value.len()); - items.push_back((jentry, value)); - } - _ => { - let encoded = read_u32(value, 4)?; - let jentry = JEntry::decode_jentry(encoded); - items.push_back((jentry, &value[8..])); - } - } - - let mut builder = ArrayBuilder::new(len + 1); - if idx > 0 { - let mut i = 0; - while let Some((jentry, item)) = items.pop_front() { - builder.push_raw(jentry, item); - i += 1; - if i >= idx { - break; - } - } - } - - let new_header = read_u32(new_value, 0)?; - match new_header & CONTAINER_HEADER_TYPE_MASK { - ARRAY_CONTAINER_TAG | OBJECT_CONTAINER_TAG => { - let new_jentry = JEntry::make_container_jentry(new_value.len()); - builder.push_raw(new_jentry, new_value); - } - _ => { - let encoded = read_u32(new_value, 4)?; - let new_jentry = JEntry::decode_jentry(encoded); - builder.push_raw(new_jentry, &new_value[8..]); - } - } - - while let Some((jentry, item)) = items.pop_front() { - builder.push_raw(jentry, item); - } - builder.build_into(buf); - - Ok(()) -} - -/// Return a JSONB Array that contains only the distinct elements from the input JSONB Array. -pub fn array_distinct(value: &[u8], buf: &mut Vec) -> Result<(), Error> { - if !is_jsonb(value) { - let value = parse_value(value)?; - let mut val_buf = Vec::new(); - value.write_to_vec(&mut val_buf); - return array_distinct_jsonb(&val_buf, buf); - } - array_distinct_jsonb(value, buf) -} - -fn array_distinct_jsonb(value: &[u8], buf: &mut Vec) -> Result<(), Error> { - let header = read_u32(value, 0)?; - let mut builder = ArrayBuilder::new(0); - match header & CONTAINER_HEADER_TYPE_MASK { - ARRAY_CONTAINER_TAG => { - let mut item_set = BTreeSet::new(); - for (jentry, item) in iterate_array(value, header) { - if !item_set.contains(&(jentry.clone(), item)) { - item_set.insert((jentry.clone(), item)); - builder.push_raw(jentry, item); - } - } - } - OBJECT_CONTAINER_TAG => { - let jentry = JEntry::make_container_jentry(value.len()); - builder.push_raw(jentry, value); - } - _ => { - let encoded = read_u32(value, 4)?; - let jentry = JEntry::decode_jentry(encoded); - builder.push_raw(jentry, &value[8..]); - } - } - builder.build_into(buf); - - Ok(()) -} - -/// Return a JSONB Array that contains the matching elements in the two input JSONB Arrays. -pub fn array_intersection(value1: &[u8], value2: &[u8], buf: &mut Vec) -> Result<(), Error> { - if !is_jsonb(value1) { - let value1 = parse_value(value1)?; - let mut val_buf1 = Vec::new(); - value1.write_to_vec(&mut val_buf1); - if !is_jsonb(value2) { - let value2 = parse_value(value2)?; - let mut val_buf2 = Vec::new(); - value2.write_to_vec(&mut val_buf2); - return array_intersection_jsonb(&val_buf1, &val_buf2, buf); - } - return array_intersection_jsonb(&val_buf1, value2, buf); - } - array_intersection_jsonb(value1, value2, buf) -} - -fn array_intersection_jsonb(value1: &[u8], value2: &[u8], buf: &mut Vec) -> Result<(), Error> { - let header1 = read_u32(value1, 0)?; - let header2 = read_u32(value2, 0)?; - - let mut item_map = BTreeMap::new(); - match header2 & CONTAINER_HEADER_TYPE_MASK { - ARRAY_CONTAINER_TAG => { - for (jentry2, item2) in iterate_array(value2, header2) { - if let Some(cnt) = item_map.get_mut(&(jentry2.clone(), item2)) { - *cnt += 1; - } else { - item_map.insert((jentry2, item2), 1); - } - } - } - OBJECT_CONTAINER_TAG => { - let jentry2 = JEntry::make_container_jentry(value2.len()); - item_map.insert((jentry2, value2), 1); - } - _ => { - let encoded = read_u32(value2, 4)?; - let jentry2 = JEntry::decode_jentry(encoded); - item_map.insert((jentry2, &value2[8..]), 1); - } - } - - let mut builder = ArrayBuilder::new(0); - match header1 & CONTAINER_HEADER_TYPE_MASK { - ARRAY_CONTAINER_TAG => { - for (jentry1, item1) in iterate_array(value1, header1) { - if let Some(cnt) = item_map.get_mut(&(jentry1.clone(), item1)) { - if *cnt > 0 { - *cnt -= 1; - builder.push_raw(jentry1, item1); - } - } - } - } - OBJECT_CONTAINER_TAG => { - let jentry1 = JEntry::make_container_jentry(value1.len()); - if item_map.contains_key(&(jentry1.clone(), value1)) { - builder.push_raw(jentry1, value1); - } - } - _ => { - let encoded = read_u32(value1, 4)?; - let jentry1 = JEntry::decode_jentry(encoded); - if item_map.contains_key(&(jentry1.clone(), &value1[8..])) { - builder.push_raw(jentry1, &value1[8..]); - } - } - } - builder.build_into(buf); - - Ok(()) -} - -/// Return a JSONB Array that contains the elements from one input JSONB Array -/// that are not in another input JSONB Array. -pub fn array_except(value1: &[u8], value2: &[u8], buf: &mut Vec) -> Result<(), Error> { - if !is_jsonb(value1) { - let value1 = parse_value(value1)?; - let mut val_buf1 = Vec::new(); - value1.write_to_vec(&mut val_buf1); - if !is_jsonb(value2) { - let value2 = parse_value(value2)?; - let mut val_buf2 = Vec::new(); - value2.write_to_vec(&mut val_buf2); - return array_except_jsonb(&val_buf1, &val_buf2, buf); - } - return array_except_jsonb(&val_buf1, value2, buf); - } - array_except_jsonb(value1, value2, buf) -} - -fn array_except_jsonb(value1: &[u8], value2: &[u8], buf: &mut Vec) -> Result<(), Error> { - let header1 = read_u32(value1, 0)?; - let header2 = read_u32(value2, 0)?; - - let mut item_map = BTreeMap::new(); - match header2 & CONTAINER_HEADER_TYPE_MASK { - ARRAY_CONTAINER_TAG => { - for (jentry2, item2) in iterate_array(value2, header2) { - if let Some(cnt) = item_map.get_mut(&(jentry2.clone(), item2)) { - *cnt += 1; - } else { - item_map.insert((jentry2, item2), 1); - } - } - } - OBJECT_CONTAINER_TAG => { - let jentry2 = JEntry::make_container_jentry(value2.len()); - item_map.insert((jentry2, value2), 1); - } - _ => { - let encoded = read_u32(value2, 4)?; - let jentry2 = JEntry::decode_jentry(encoded); - item_map.insert((jentry2, &value2[8..]), 1); - } - } - - let mut builder = ArrayBuilder::new(0); - match header1 & CONTAINER_HEADER_TYPE_MASK { - ARRAY_CONTAINER_TAG => { - for (jentry1, item1) in iterate_array(value1, header1) { - if let Some(cnt) = item_map.get_mut(&(jentry1.clone(), item1)) { - if *cnt > 0 { - *cnt -= 1; - continue; - } - } - builder.push_raw(jentry1, item1); - } - } - OBJECT_CONTAINER_TAG => { - let jentry1 = JEntry::make_container_jentry(value1.len()); - if !item_map.contains_key(&(jentry1.clone(), value1)) { - builder.push_raw(jentry1, value1); - } - } - _ => { - let encoded = read_u32(value1, 4)?; - let jentry1 = JEntry::decode_jentry(encoded); - if !item_map.contains_key(&(jentry1.clone(), &value1[8..])) { - builder.push_raw(jentry1, &value1[8..]); - } - } - } - builder.build_into(buf); - - Ok(()) -} - -/// Compares whether two JSONB Arrays have at least one element in common. -/// Return TRUE if there is at least one element in common; otherwise return FALSE. -pub fn array_overlap(value1: &[u8], value2: &[u8]) -> Result { - if !is_jsonb(value1) { - let value1 = parse_value(value1)?; - let mut val_buf1 = Vec::new(); - value1.write_to_vec(&mut val_buf1); - if !is_jsonb(value2) { - let value2 = parse_value(value2)?; - let mut val_buf2 = Vec::new(); - value2.write_to_vec(&mut val_buf2); - return array_overlap_jsonb(&val_buf1, &val_buf2); - } - return array_overlap_jsonb(&val_buf1, value2); - } - array_overlap_jsonb(value1, value2) -} - -fn array_overlap_jsonb(value1: &[u8], value2: &[u8]) -> Result { - let header1 = read_u32(value1, 0)?; - let header2 = read_u32(value2, 0)?; - - let mut item_set = BTreeSet::new(); - match header2 & CONTAINER_HEADER_TYPE_MASK { - ARRAY_CONTAINER_TAG => { - for (jentry2, item2) in iterate_array(value2, header2) { - if !item_set.contains(&(jentry2.clone(), item2)) { - item_set.insert((jentry2, item2)); - } - } - } - OBJECT_CONTAINER_TAG => { - let jentry2 = JEntry::make_container_jentry(value2.len()); - item_set.insert((jentry2, value2)); - } - _ => { - let encoded = read_u32(value2, 4)?; - let jentry2 = JEntry::decode_jentry(encoded); - item_set.insert((jentry2, &value2[8..])); - } - } - - match header1 & CONTAINER_HEADER_TYPE_MASK { - ARRAY_CONTAINER_TAG => { - for (jentry1, item1) in iterate_array(value1, header1) { - if item_set.contains(&(jentry1, item1)) { - return Ok(true); - } - } - } - OBJECT_CONTAINER_TAG => { - let jentry1 = JEntry::make_container_jentry(value1.len()); - if item_set.contains(&(jentry1, value1)) { - return Ok(true); - } - } - _ => { - let encoded = read_u32(value1, 4)?; - let jentry1 = JEntry::decode_jentry(encoded); - if item_set.contains(&(jentry1, &value1[8..])) { - return Ok(true); - } - } - } - - Ok(false) -} - -/// Insert a new value into a JSONB object value by the new key and new value. -pub fn object_insert( - value: &[u8], - new_key: &str, - new_value: &[u8], - update_flag: bool, - buf: &mut Vec, -) -> Result<(), Error> { - if !is_jsonb(value) { - let value = parse_value(value)?; - let mut val_buf = Vec::new(); - value.write_to_vec(&mut val_buf); - if !is_jsonb(new_value) { - let new_value = parse_value(new_value)?; - let mut new_val_buf = Vec::new(); - new_value.write_to_vec(&mut new_val_buf); - return object_insert_jsonb(&val_buf, new_key, &new_val_buf, update_flag, buf); - } - return object_insert_jsonb(&val_buf, new_key, new_value, update_flag, buf); - } - object_insert_jsonb(value, new_key, new_value, update_flag, buf) -} - -fn object_insert_jsonb( - value: &[u8], - new_key: &str, - new_value: &[u8], - update_flag: bool, - buf: &mut Vec, -) -> Result<(), Error> { - let header = read_u32(value, 0)?; - if header & CONTAINER_HEADER_TYPE_MASK != OBJECT_CONTAINER_TAG { - return Err(Error::InvalidObject); - } - - let mut idx = 0; - let mut duplicate_key = false; - for (i, obj_key) in iteate_object_keys(value, header).enumerate() { - if new_key.eq(obj_key) { - if !update_flag { - return Err(Error::ObjectDuplicateKey); - } - idx = i; - duplicate_key = true; - break; - } else if new_key > obj_key { - idx = i + 1; - } else { - break; - } - } - - let mut builder = ObjectBuilder::new(); - let mut obj_iter = iterate_object_entries(value, header); - for _ in 0..idx { - if let Some((key, jentry, item)) = obj_iter.next() { - builder.push_raw(key, jentry, item); - } - } - // insert new key and value - let new_header = read_u32(new_value, 0)?; - match new_header & CONTAINER_HEADER_TYPE_MASK { - ARRAY_CONTAINER_TAG | OBJECT_CONTAINER_TAG => { - let new_jentry = JEntry::make_container_jentry(new_value.len()); - builder.push_raw(new_key, new_jentry, new_value); - } - _ => { - let encoded = read_u32(new_value, 4)?; - let new_jentry = JEntry::decode_jentry(encoded); - builder.push_raw(new_key, new_jentry, &new_value[8..]); - } - } - // if the key is duplicated, ignore the original key and value. - if duplicate_key { - let _ = obj_iter.next(); - } - for (key, jentry, item) in obj_iter { - builder.push_raw(key, jentry, item); - } - builder.build_into(buf); - - Ok(()) -} - -/// Delete keys and values from a JSONB object value by keys. -pub fn object_delete(value: &[u8], keys: &BTreeSet<&str>, buf: &mut Vec) -> Result<(), Error> { - if !is_jsonb(value) { - let value = parse_value(value)?; - let mut val_buf = Vec::new(); - value.write_to_vec(&mut val_buf); - return object_delete_jsonb(&val_buf, keys, buf); - } - object_delete_jsonb(value, keys, buf) -} - -fn object_delete_jsonb( - value: &[u8], - keys: &BTreeSet<&str>, - buf: &mut Vec, -) -> Result<(), Error> { - let header = read_u32(value, 0)?; - if header & CONTAINER_HEADER_TYPE_MASK != OBJECT_CONTAINER_TAG { - return Err(Error::InvalidObject); - } - - let mut builder = ObjectBuilder::new(); - for (key, jentry, item) in iterate_object_entries(value, header) { - if keys.contains(key) { - continue; - } - builder.push_raw(key, jentry, item); - } - builder.build_into(buf); - - Ok(()) -} - -/// Pick keys and values from a JSONB object value by keys. -pub fn object_pick(value: &[u8], keys: &BTreeSet<&str>, buf: &mut Vec) -> Result<(), Error> { - if !is_jsonb(value) { - let value = parse_value(value)?; - let mut val_buf = Vec::new(); - value.write_to_vec(&mut val_buf); - return object_pick_jsonb(&val_buf, keys, buf); - } - object_pick_jsonb(value, keys, buf) -} - -fn object_pick_jsonb(value: &[u8], keys: &BTreeSet<&str>, buf: &mut Vec) -> Result<(), Error> { - let header = read_u32(value, 0)?; - if header & CONTAINER_HEADER_TYPE_MASK != OBJECT_CONTAINER_TAG { - return Err(Error::InvalidObject); - } - - let mut builder = ObjectBuilder::new(); - for (key, jentry, item) in iterate_object_entries(value, header) { - if !keys.contains(key) { - continue; - } - builder.push_raw(key, jentry, item); - } - builder.build_into(buf); - - Ok(()) -} - -/// Deletes all object fields that have null values from the given JSON value, recursively. -/// Null values that are not object fields are untouched. -pub fn strip_nulls(value: &[u8], buf: &mut Vec) -> Result<(), Error> { - if !is_jsonb(value) { - let mut json = parse_value(value)?; - strip_value_nulls(&mut json); - json.write_to_vec(buf); - return Ok(()); - } - strip_nulls_jsonb(value, buf) -} - -fn strip_value_nulls(val: &mut Value<'_>) { - match val { - Value::Array(arr) => { - for v in arr { - strip_value_nulls(v); - } - } - Value::Object(ref mut obj) => { - for (_, v) in obj.iter_mut() { - strip_value_nulls(v); - } - obj.retain(|_, v| !matches!(v, Value::Null)); - } - _ => {} - } -} - -fn strip_nulls_jsonb(value: &[u8], buf: &mut Vec) -> Result<(), Error> { - let header = read_u32(value, 0)?; - - match header & CONTAINER_HEADER_TYPE_MASK { - OBJECT_CONTAINER_TAG => { - let builder = strip_nulls_object(header, value)?; - builder.build_into(buf); - } - ARRAY_CONTAINER_TAG => { - let builder = strip_nulls_array(header, value)?; - builder.build_into(buf); - } - _ => buf.extend_from_slice(value), - } - Ok(()) -} - -fn strip_nulls_array(header: u32, value: &[u8]) -> Result, Error> { - let len = (header & CONTAINER_HEADER_LEN_MASK) as usize; - let mut builder = ArrayBuilder::new(len); - - for (jentry, item) in iterate_array(value, header) { - match jentry.type_code { - CONTAINER_TAG => { - let item_header = read_u32(item, 0)?; - match item_header & CONTAINER_HEADER_TYPE_MASK { - OBJECT_CONTAINER_TAG => { - builder.push_object(strip_nulls_object(item_header, item)?); - } - ARRAY_CONTAINER_TAG => { - builder.push_array(strip_nulls_array(item_header, item)?); - } - _ => unreachable!(), - } - } - _ => builder.push_raw(jentry, item), - } - } - Ok(builder) -} - -fn strip_nulls_object(header: u32, value: &[u8]) -> Result, Error> { - let mut builder = ObjectBuilder::new(); - for (key, jentry, item) in iterate_object_entries(value, header) { - match jentry.type_code { - CONTAINER_TAG => { - let item_header = read_u32(item, 0)?; - match item_header & CONTAINER_HEADER_TYPE_MASK { - OBJECT_CONTAINER_TAG => { - builder.push_object(key, strip_nulls_object(item_header, item)?); - } - ARRAY_CONTAINER_TAG => { - builder.push_array(key, strip_nulls_array(item_header, item)?); - } - _ => unreachable!(), - } - } - NULL_TAG => continue, - _ => builder.push_raw(key, jentry, item), - } - } - Ok(builder) -} - -/// Returns the type of the top-level JSON value as a text string. -/// Possible types are object, array, string, number, boolean, and null. -pub fn type_of(value: &[u8]) -> Result<&'static str, Error> { - if !is_jsonb(value) { - return match value.first() { - Some(v) => match v { - b'n' => Ok(TYPE_NULL), - b't' | b'f' => Ok(TYPE_BOOLEAN), - b'0'..=b'9' | b'-' => Ok(TYPE_NUMBER), - b'"' => Ok(TYPE_STRING), - b'[' => Ok(TYPE_ARRAY), - b'{' => Ok(TYPE_OBJECT), - _ => Err(Error::Syntax(ParseErrorCode::ExpectedSomeValue, 0)), - }, - None => Err(Error::Syntax(ParseErrorCode::InvalidEOF, 0)), - }; - } - - let header = read_u32(value, 0)?; - - match header & CONTAINER_HEADER_TYPE_MASK { - SCALAR_CONTAINER_TAG => { - let encoded = read_u32(value, 4)?; - let jentry = JEntry::decode_jentry(encoded); - match jentry.type_code { - NULL_TAG => Ok(TYPE_NULL), - TRUE_TAG | FALSE_TAG => Ok(TYPE_BOOLEAN), - NUMBER_TAG => Ok(TYPE_NUMBER), - STRING_TAG => Ok(TYPE_STRING), - _ => Err(Error::InvalidJsonbJEntry), - } - } - ARRAY_CONTAINER_TAG => Ok(TYPE_ARRAY), - OBJECT_CONTAINER_TAG => Ok(TYPE_OBJECT), - _ => Err(Error::InvalidJsonbHeader), - } -} - -// Check whether the value is `JSONB` format, -// for compatibility with previous `JSON` string. -pub(crate) fn is_jsonb(value: &[u8]) -> bool { - if let Some(v) = value.first() { - if matches!(*v, ARRAY_PREFIX | OBJECT_PREFIX | SCALAR_PREFIX) { - return true; - } - } - false -} - -fn read_u32(buf: &[u8], idx: usize) -> Result { - let bytes: [u8; 4] = buf - .get(idx..idx + 4) - .ok_or(Error::InvalidEOF)? - .try_into() - .unwrap(); - Ok(u32::from_be_bytes(bytes)) -} - -fn array_contains(arr: &[u8], arr_header: u32, val: &[u8], val_jentry: JEntry) -> bool { - for (jentry, arr_val) in iterate_array(arr, arr_header) { - if jentry.type_code != val_jentry.type_code { - continue; - } - if val.eq(arr_val) { - return true; - } - } - false -} diff --git a/src/functions/array.rs b/src/functions/array.rs new file mode 100644 index 0000000..f3a0097 --- /dev/null +++ b/src/functions/array.rs @@ -0,0 +1,805 @@ +// Copyright 2023 Datafuse Labs. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains functions that specifically operate on JSONB array values. + +use core::convert::TryInto; +use std::collections::BTreeMap; +use std::collections::BTreeSet; +use std::collections::VecDeque; + +use crate::builder::ArrayBuilder; +use crate::constants::*; +use crate::error::*; +use crate::functions::core::extract_by_jentry; +use crate::functions::core::read_u32; +use crate::iterator::iterate_array; +use crate::jentry::JEntry; + +use crate::OwnedJsonb; +use crate::RawJsonb; + +impl OwnedJsonb { + /// Builds a JSONB array from a collection of RawJsonb values. + /// + /// This function constructs a new JSONB array from an iterator of `RawJsonb` values. The resulting `OwnedJsonb` represents the binary encoding of the array. The input iterator must be an `ExactSizeIterator` to allow for efficient pre-allocation of the output buffer. + /// + /// # Arguments + /// + /// * `items` - An iterator of `RawJsonb` values representing the elements of the array. Must be an `ExactSizeIterator`. + /// + /// # Returns + /// + /// * `Ok(OwnedJsonb)` - The newly created JSONB array. + /// * `Err(Error)` - If any of the input `RawJsonb` values are invalid or if an error occurs during array construction. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::{OwnedJsonb, RawJsonb}; + /// + /// // Create some RawJsonb values + /// let owned_num = "1".parse::().unwrap(); + /// let owned_str = r#""hello""#.parse::().unwrap(); + /// let owned_arr = "[1,2,3]".parse::().unwrap(); + /// + /// // Build the array + /// let raw_jsonbs = vec![owned_num.as_raw(), owned_str.as_raw(), owned_arr.as_raw()]; + /// let array_result = OwnedJsonb::build_array(raw_jsonbs.into_iter()); + /// assert!(array_result.is_ok()); + /// let array = array_result.unwrap(); + /// + /// // Convert to string for easy verification + /// assert_eq!(array.to_string(), "[1,\"hello\",[1,2,3]]"); + /// + /// // Example with an empty iterator + /// let empty_array = OwnedJsonb::build_array(<[RawJsonb<'_>; 0] as IntoIterator>::into_iter([])).unwrap(); + /// assert_eq!(empty_array.to_string(), "[]"); + /// + /// // Example with invalid input (this will cause an error) + /// let invalid_data = OwnedJsonb::new(vec![1,2,3,4]); + /// let result = OwnedJsonb::build_array([invalid_data.as_raw()].into_iter()); + /// assert!(result.is_err()); + /// ``` + pub fn build_array<'a>( + items: impl IntoIterator>, + ) -> Result { + let mut jentries = Vec::new(); + let mut data = Vec::new(); + for value in items.into_iter() { + let header = read_u32(value.data, 0)?; + let encoded_jentry = match header & CONTAINER_HEADER_TYPE_MASK { + SCALAR_CONTAINER_TAG => { + let jentry = &value.data[4..8]; + data.extend_from_slice(&value.data[8..]); + jentry.try_into().unwrap() + } + ARRAY_CONTAINER_TAG | OBJECT_CONTAINER_TAG => { + data.extend_from_slice(value.data); + (CONTAINER_TAG | value.data.len() as u32).to_be_bytes() + } + _ => return Err(Error::InvalidJsonbHeader), + }; + jentries.push(encoded_jentry); + } + let len = jentries.len(); + // reserve space for header, jentries and value data + let mut buf = Vec::with_capacity(data.len() + len * 4 + 4); + // write header + let header = ARRAY_CONTAINER_TAG | (len as u32); + buf.extend_from_slice(&header.to_be_bytes()); + // write jentries + for jentry in jentries.into_iter() { + buf.extend_from_slice(&jentry); + } + buf.extend_from_slice(&data); + Ok(OwnedJsonb::new(buf)) + } +} + +impl RawJsonb<'_> { + /// Returns the number of elements in a JSONB array. + /// + /// This function checks the header of the JSONB data to determine if it represents an array. + /// If it is an array, the function returns the number of elements in the array. If the JSONB + /// data is not an array (e.g., it's an object or a scalar value), the function returns `None`. + /// An error is returned if the JSONB data is invalid. + /// + /// # Examples + /// + /// ``` + /// use jsonb::OwnedJsonb; + /// + /// let arr_jsonb = "[1,2,3]".parse::().unwrap(); + /// let raw_jsonb = arr_jsonb.as_raw(); + /// let len = raw_jsonb.array_length().unwrap(); + /// assert_eq!(len, Some(3)); + /// + /// let obj_jsonb = r#"{"a": 1, "b": {"c": 2, "d": 3}}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// let len = raw_jsonb.array_length().unwrap(); + /// assert_eq!(len, None); + /// ``` + pub fn array_length(&self) -> Result, Error> { + let header = read_u32(self.data, 0)?; + let len = match header & CONTAINER_HEADER_TYPE_MASK { + ARRAY_CONTAINER_TAG => { + let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; + Some(length) + } + OBJECT_CONTAINER_TAG | SCALAR_CONTAINER_TAG => None, + _ => { + return Err(Error::InvalidJsonb); + } + }; + Ok(len) + } + + /// Extracts the values from a JSONB array. + /// + /// If the JSONB value is an array, this function returns a vector of `OwnedJsonb` representing the array elements. + /// If the JSONB value is not an array (e.g., it's an object or a scalar), this function returns `Ok(None)`. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(Some(Vec))` - A vector of `OwnedJsonb` values if the input is an array. + /// * `Ok(None)` - If the input is not an array. + /// * `Err(Error)` - If an error occurred during decoding (e.g., invalid JSONB data). + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // Array values extraction + /// let arr_jsonb = r#"[1, "hello", {"a": 1}]"#.parse::().unwrap(); + /// let raw_jsonb = arr_jsonb.as_raw(); + /// let values_result = raw_jsonb.array_values(); + /// assert!(values_result.is_ok()); + /// + /// let values = values_result.unwrap().unwrap(); + /// assert_eq!(values.len(), 3); + /// + /// assert_eq!(values[0].to_string(), "1"); + /// assert_eq!(values[1].to_string(), r#""hello""#); + /// assert_eq!(values[2].to_string(), r#"{"a":1}"#); + /// + /// // Object - returns None + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// let values_result = raw_jsonb.array_values(); + /// assert!(values_result.is_ok()); + /// assert!(values_result.unwrap().is_none()); + /// + /// // Scalar - returns None + /// let scalar_jsonb = "1".parse::().unwrap(); + /// let raw_jsonb = scalar_jsonb.as_raw(); + /// let values_result = raw_jsonb.array_values(); + /// assert!(values_result.is_ok()); + /// assert!(values_result.unwrap().is_none()); + /// ``` + pub fn array_values(&self) -> Result>, Error> { + let header = read_u32(self.data, 0)?; + match header & CONTAINER_HEADER_TYPE_MASK { + ARRAY_CONTAINER_TAG => { + let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; + let mut jentry_offset = 4; + let mut val_offset = 4 * length + 4; + let mut items = Vec::with_capacity(length); + for _ in 0..length { + let encoded = read_u32(self.data, jentry_offset)?; + let jentry = JEntry::decode_jentry(encoded); + let val_length = jentry.length as usize; + let item_data = extract_by_jentry(&jentry, encoded, val_offset, self.data); + let item = OwnedJsonb::new(item_data); + items.push(item); + + jentry_offset += 4; + val_offset += val_length; + } + Ok(Some(items)) + } + OBJECT_CONTAINER_TAG | SCALAR_CONTAINER_TAG => Ok(None), + _ => Err(Error::InvalidJsonb), + } + } + + /// Returns a JSONB array with duplicate elements removed. + /// + /// This function takes a JSONB value as input and returns a new JSONB array containing only the unique elements from the input. + /// The behavior depends on the input type: + /// + /// * **Array:** Returns a new array containing only the unique elements from the input array. The order of elements in the output array is not guaranteed to be the same as the input array. + /// * **Object:** Returns a new array containing the original object as its only element. + /// * **Scalar:** Returns a new array containing the original scalar value as its only element. + /// * **Invalid JSONB:** Returns an error. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(OwnedJsonb)` - A JSONB array containing only the unique elements from the input. + /// * `Err(Error)` - If the input JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // Array with duplicates + /// let arr_jsonb = r#"[1, 2, 2, 3, 1, 4]"#.parse::().unwrap(); + /// let distinct = arr_jsonb.as_raw().array_distinct().unwrap(); + /// assert_eq!(distinct.to_string(), "[1,2,3,4]"); // Order may vary + /// + /// // Array with only unique elements + /// let arr_jsonb = r#"[1, 2, 3, 4]"#.parse::().unwrap(); + /// let distinct = arr_jsonb.as_raw().array_distinct().unwrap(); + /// assert_eq!(distinct.to_string(), "[1,2,3,4]"); // Order may vary + /// + /// // Object + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let distinct = obj_jsonb.as_raw().array_distinct().unwrap(); + /// assert_eq!(distinct.to_string(), r#"[{"a":1}]"#); + /// + /// // Scalar + /// let scalar_jsonb = "1".parse::().unwrap(); + /// let distinct = scalar_jsonb.as_raw().array_distinct().unwrap(); + /// assert_eq!(distinct.to_string(), "[1]"); + /// + /// // Invalid JSONB data + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.array_distinct(); + /// assert!(result.is_err()); + /// ``` + pub fn array_distinct(&self) -> Result { + let mut buf = Vec::new(); + let value = self.data; + let header = read_u32(value, 0)?; + let mut builder = ArrayBuilder::new(0); + match header & CONTAINER_HEADER_TYPE_MASK { + ARRAY_CONTAINER_TAG => { + let mut item_set = BTreeSet::new(); + for (jentry, item) in iterate_array(value, header) { + if !item_set.contains(&(jentry.clone(), item)) { + item_set.insert((jentry.clone(), item)); + builder.push_raw(jentry, item); + } + } + } + OBJECT_CONTAINER_TAG => { + let jentry = JEntry::make_container_jentry(value.len()); + builder.push_raw(jentry, value); + } + SCALAR_CONTAINER_TAG => { + let encoded = read_u32(value, 4)?; + let jentry = JEntry::decode_jentry(encoded); + builder.push_raw(jentry, &value[8..]); + } + _ => { + return Err(Error::InvalidJsonb); + } + } + builder.build_into(&mut buf); + + Ok(OwnedJsonb::new(buf)) + } + + /// Computes the intersection of two JSONB arrays or the containment check for objects and scalars. + /// + /// This function calculates the intersection of two JSONB arrays or checks if one JSONB value is contained within another. + /// The behavior depends on the input types: + /// + /// * **Array + Array:** Returns a new array containing only the elements that are present in *both* input arrays. The order of elements is not guaranteed. Duplicate elements are handled correctly; the multiplicity of elements in the intersection is the minimum of their multiplicities in the input arrays. + /// * **Object/Scalar + Object/Scalar:** Returns a new array containing the `self` value only if it's present in the `other` value. This effectively checks for containment. The contained value must be completely equal to the other value, including any nested structures. For arrays, this containment check would require a recursive check for each element in both arrays. + /// * **Invalid input:** Returns an error if either input is not an array, object, or scalar. + /// + /// # Arguments + /// + /// * `self` - The first JSONB value. + /// * `other` - The second JSONB value. + /// + /// # Returns + /// + /// * `Ok(OwnedJsonb)` - The intersection array (for array + array) or a single-element array indicating containment (for other combinations). The empty array `[]` indicates that there's no intersection or containment. + /// * `Err(Error)` - If the input JSONB data is invalid or if the input types are incompatible (e.g., trying to find the intersection of an array and an object). + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // Array intersection + /// let arr1 = r#"[1, 2, 2, 3]"#.parse::().unwrap(); + /// let arr2 = r#"[2, 3, 4]"#.parse::().unwrap(); + /// let intersection = arr1.as_raw().array_intersection(arr2.as_raw()).unwrap(); + /// assert_eq!(intersection.to_string(), "[2,3]"); // Order may vary, duplicates handled + /// + /// let arr1 = r#"[1, 1, 2, 3]"#.parse::().unwrap(); + /// let arr2 = r#"[1, 1, 1, 3]"#.parse::().unwrap(); + /// let intersection = arr1.as_raw().array_intersection(arr2.as_raw()).unwrap(); + /// assert_eq!(intersection.to_string(), "[1,1,3]"); //Order may vary + /// + /// // Object containment (checks for complete equality) + /// let obj1 = r#"{"a": 1}"#.parse::().unwrap(); + /// let obj2 = r#"{"a": 1}"#.parse::().unwrap(); + /// let contained = obj1.as_raw().array_intersection(obj2.as_raw()).unwrap(); + /// assert_eq!(contained.to_string(), r#"[{"a":1}]"#); + /// + /// let obj1 = r#"{"a": 1}"#.parse::().unwrap(); + /// let obj2 = r#"{"a": 2}"#.parse::().unwrap(); + /// let contained = obj1.as_raw().array_intersection(obj2.as_raw()).unwrap(); + /// assert_eq!(contained.to_string(), "[]"); // Not contained + /// + /// let scalar1 = "1".parse::().unwrap(); + /// let scalar2 = "1".parse::().unwrap(); + /// let contained = scalar1.as_raw().array_intersection(scalar2.as_raw()).unwrap(); + /// assert_eq!(contained.to_string(), "[1]"); // Contained + /// + /// let scalar1 = "1".parse::().unwrap(); + /// let scalar2 = "2".parse::().unwrap(); + /// let contained = scalar1.as_raw().array_intersection(scalar2.as_raw()).unwrap(); + /// assert_eq!(contained.to_string(), "[]"); // Not contained + /// ``` + pub fn array_intersection(&self, other: RawJsonb) -> Result { + let mut buf = Vec::new(); + let left = self.data; + let right = other.data; + + let left_header = read_u32(left, 0)?; + let right_header = read_u32(right, 0)?; + + let mut item_map = BTreeMap::new(); + match right_header & CONTAINER_HEADER_TYPE_MASK { + ARRAY_CONTAINER_TAG => { + for (jentry, item) in iterate_array(right, right_header) { + if let Some(cnt) = item_map.get_mut(&(jentry.clone(), item)) { + *cnt += 1; + } else { + item_map.insert((jentry, item), 1); + } + } + } + OBJECT_CONTAINER_TAG => { + let jentry = JEntry::make_container_jentry(right.len()); + item_map.insert((jentry, right), 1); + } + SCALAR_CONTAINER_TAG => { + let encoded = read_u32(right, 4)?; + let jentry = JEntry::decode_jentry(encoded); + item_map.insert((jentry, &right[8..]), 1); + } + _ => { + return Err(Error::InvalidJsonb); + } + } + + let mut builder = ArrayBuilder::new(0); + match left_header & CONTAINER_HEADER_TYPE_MASK { + ARRAY_CONTAINER_TAG => { + for (jentry, item) in iterate_array(left, left_header) { + if let Some(cnt) = item_map.get_mut(&(jentry.clone(), item)) { + if *cnt > 0 { + *cnt -= 1; + builder.push_raw(jentry, item); + } + } + } + } + OBJECT_CONTAINER_TAG => { + let jentry = JEntry::make_container_jentry(left.len()); + if item_map.contains_key(&(jentry.clone(), left)) { + builder.push_raw(jentry, left); + } + } + SCALAR_CONTAINER_TAG => { + let encoded = read_u32(left, 4)?; + let jentry = JEntry::decode_jentry(encoded); + if item_map.contains_key(&(jentry.clone(), &left[8..])) { + builder.push_raw(jentry, &left[8..]); + } + } + _ => { + return Err(Error::InvalidJsonb); + } + } + builder.build_into(&mut buf); + + Ok(OwnedJsonb::new(buf)) + } + + /// Computes the set difference between two JSONB arrays or checks for non-containment of objects and scalars. + /// + /// This function calculates the set difference between two JSONB arrays or checks if one JSONB value is *not* contained within another. The behavior depends on the input types: + /// + /// * **Array + Array:** Returns a new array containing only the elements that are present in the `self` array but *not* in the `other` array. The order of elements is not guaranteed. Duplicate elements are handled correctly; if an element appears multiple times in `self` but is present in `other`, it will be removed from the result only up to the number of times it appears in `other`. + /// * **Object/Scalar + Object/Scalar:** Returns a new array containing the `self` value if it's *not* contained in the `other` value. This effectively checks for non-containment. For arrays, this non-containment check would require a recursive check for each element in both arrays. Complete equality is required for containment; even a slight difference (e.g., a different number of duplicate elements) means the value is not contained. + /// * **Invalid input:** Returns an error if either input is not an array, object, or scalar. + /// + /// # Arguments + /// + /// * `self` - The first JSONB value. + /// * `other` - The second JSONB value. + /// + /// # Returns + /// + /// * `Ok(OwnedJsonb)` - The resulting array after removing elements from `self` that are present in `other` (for array + array), or a single-element array indicating non-containment (for other combinations). An empty array `[]` indicates that all elements of `self` are present in `other`. + /// * `Err(Error)` - If the input JSONB data is invalid or if the input types are incompatible (e.g., trying to find the set difference between an array and an object). + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // Array except + /// let arr1 = r#"[1, 2, 2, 3]"#.parse::().unwrap(); + /// let arr2 = r#"[2, 3, 4]"#.parse::().unwrap(); + /// let except = arr1.as_raw().array_except(arr2.as_raw()).unwrap(); + /// assert_eq!(except.to_string(), "[1,2]"); // Order may vary, duplicates handled + /// + /// let arr1 = r#"[1, 1, 2, 3, 3]"#.parse::().unwrap(); + /// let arr2 = r#"[1, 3, 3]"#.parse::().unwrap(); + /// let except = arr1.as_raw().array_except(arr2.as_raw()).unwrap(); + /// assert_eq!(except.to_string(), "[1,2]"); // Order may vary + /// + /// // Object non-containment + /// let obj1 = r#"{"a": 1}"#.parse::().unwrap(); + /// let obj2 = r#"{"a": 1}"#.parse::().unwrap(); + /// let not_contained = obj1.as_raw().array_except(obj2.as_raw()).unwrap(); + /// assert_eq!(not_contained.to_string(), "[]"); // Completely contained + /// + /// let obj1 = r#"{"a": 1}"#.parse::().unwrap(); + /// let obj2 = r#"{"a": 2}"#.parse::().unwrap(); + /// let not_contained = obj1.as_raw().array_except(obj2.as_raw()).unwrap(); + /// assert_eq!(not_contained.to_string(), r#"[{"a":1}]"#); // Not contained + /// + /// let scalar1 = "1".parse::().unwrap(); + /// let scalar2 = "1".parse::().unwrap(); + /// let not_contained = scalar1.as_raw().array_except(scalar2.as_raw()).unwrap(); + /// assert_eq!(not_contained.to_string(), "[]"); // Contained + /// + /// let scalar1 = "1".parse::().unwrap(); + /// let scalar2 = "2".parse::().unwrap(); + /// let not_contained = scalar1.as_raw().array_except(scalar2.as_raw()).unwrap(); + /// assert_eq!(not_contained.to_string(), "[1]"); // Not contained + /// ``` + pub fn array_except(&self, other: RawJsonb) -> Result { + let mut buf = Vec::new(); + let left = self.data; + let right = other.data; + + let left_header = read_u32(left, 0)?; + let right_header = read_u32(right, 0)?; + + let mut item_map = BTreeMap::new(); + match right_header & CONTAINER_HEADER_TYPE_MASK { + ARRAY_CONTAINER_TAG => { + for (jentry, item) in iterate_array(right, right_header) { + if let Some(cnt) = item_map.get_mut(&(jentry.clone(), item)) { + *cnt += 1; + } else { + item_map.insert((jentry, item), 1); + } + } + } + OBJECT_CONTAINER_TAG => { + let jentry = JEntry::make_container_jentry(right.len()); + item_map.insert((jentry, right), 1); + } + SCALAR_CONTAINER_TAG => { + let encoded = read_u32(right, 4)?; + let jentry = JEntry::decode_jentry(encoded); + item_map.insert((jentry, &right[8..]), 1); + } + _ => { + return Err(Error::InvalidJsonb); + } + } + + let mut builder = ArrayBuilder::new(0); + match left_header & CONTAINER_HEADER_TYPE_MASK { + ARRAY_CONTAINER_TAG => { + for (jentry, item) in iterate_array(left, left_header) { + if let Some(cnt) = item_map.get_mut(&(jentry.clone(), item)) { + if *cnt > 0 { + *cnt -= 1; + continue; + } + } + builder.push_raw(jentry, item); + } + } + OBJECT_CONTAINER_TAG => { + let jentry = JEntry::make_container_jentry(left.len()); + if !item_map.contains_key(&(jentry.clone(), left)) { + builder.push_raw(jentry, left); + } + } + SCALAR_CONTAINER_TAG => { + let encoded = read_u32(left, 4)?; + let jentry = JEntry::decode_jentry(encoded); + if !item_map.contains_key(&(jentry.clone(), &left[8..])) { + builder.push_raw(jentry, &left[8..]); + } + } + _ => { + return Err(Error::InvalidJsonb); + } + } + builder.build_into(&mut buf); + + Ok(OwnedJsonb::new(buf)) + } + + /// Checks if two JSONB arrays or a JSONB array and an object/scalar have any elements in common. + /// + /// This function determines whether two JSONB arrays, or a JSONB array and an object/scalar, share any common elements. The behavior depends on the input types: + /// + /// * **Array + Array:** Returns `true` if the two arrays have at least one element in common; otherwise, returns `false`. Duplicate elements are considered; if an element appears multiple times in one array, it only needs to appear at least once in the other array for the function to return `true`. + /// * **Array + Object/Scalar:** Returns `true` if the array contains the object/scalar; otherwise, returns `false`. + /// * **Object/Scalar + Array:** Returns `true` if the array contains the object/scalar; otherwise, returns `false`. + /// * **Object/Scalar + Object/Scalar:** Returns `true` only if both values are exactly equal. This is effectively an equality check. The values must be completely equal, including any nested structures. For arrays, this would require a recursive equality check for each element in both arrays. + /// * **Invalid input:** Returns an error if either input is invalid JSONB data. + /// + /// # Arguments + /// + /// * `self` - The first JSONB value. + /// * `other` - The second JSONB value. + /// + /// # Returns + /// + /// * `Ok(true)` - If the two JSONB values have at least one element in common. + /// * `Ok(false)` - If the two JSONB values have no elements in common. + /// * `Err(Error)` - If the input JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // Array overlap + /// let arr1 = r#"[1, 2, 3]"#.parse::().unwrap(); + /// let arr2 = r#"[3, 4, 5]"#.parse::().unwrap(); + /// assert!(arr1.as_raw().array_overlap(arr2.as_raw()).unwrap()); // True because of '3' + /// + /// let arr1 = r#"[1, 2]"#.parse::().unwrap(); + /// let arr2 = r#"[3, 4]"#.parse::().unwrap(); + /// assert!(!arr1.as_raw().array_overlap(arr2.as_raw()).unwrap()); // False, no common elements + /// + /// let arr1 = r#"[1, 2, 2]"#.parse::().unwrap(); + /// let arr2 = r#"[2, 3]"#.parse::().unwrap(); + /// assert!(arr1.as_raw().array_overlap(arr2.as_raw()).unwrap()); // True, '2' is common + /// + /// // Object/scalar overlap (requires complete equality for true) + /// let obj1 = r#"{"a": 1}"#.parse::().unwrap(); + /// let obj2 = r#"{"a": 1}"#.parse::().unwrap(); + /// assert!(obj1.as_raw().array_overlap(obj2.as_raw()).unwrap()); // True, completely equal + /// + /// let obj1 = r#"{"a": 1}"#.parse::().unwrap(); + /// let obj2 = r#"{"a": 2}"#.parse::().unwrap(); + /// assert!(!obj1.as_raw().array_overlap(obj2.as_raw()).unwrap()); // False, not equal + /// + /// let scalar1 = "1".parse::().unwrap(); + /// let scalar2 = "1".parse::().unwrap(); + /// assert!(scalar1.as_raw().array_overlap(scalar2.as_raw()).unwrap()); // True, equal + /// + /// let scalar1 = "1".parse::().unwrap(); + /// let scalar2 = "2".parse::().unwrap(); + /// assert!(!scalar1.as_raw().array_overlap(scalar2.as_raw()).unwrap()); // False, not equal + /// + /// // Invalid input + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.array_overlap(arr1.as_raw()); + /// assert!(result.is_err()); // Returns an error + /// ``` + pub fn array_overlap(&self, other: RawJsonb) -> Result { + let left = self.data; + let right = other.data; + + let left_header = read_u32(left, 0)?; + let right_header = read_u32(right, 0)?; + + let mut item_set = BTreeSet::new(); + match right_header & CONTAINER_HEADER_TYPE_MASK { + ARRAY_CONTAINER_TAG => { + for (jentry, item) in iterate_array(right, right_header) { + if !item_set.contains(&(jentry.clone(), item)) { + item_set.insert((jentry, item)); + } + } + } + OBJECT_CONTAINER_TAG => { + let jentry = JEntry::make_container_jentry(right.len()); + item_set.insert((jentry, right)); + } + SCALAR_CONTAINER_TAG => { + let encoded = read_u32(right, 4)?; + let jentry = JEntry::decode_jentry(encoded); + item_set.insert((jentry, &right[8..])); + } + _ => { + return Err(Error::InvalidJsonb); + } + } + + match left_header & CONTAINER_HEADER_TYPE_MASK { + ARRAY_CONTAINER_TAG => { + for (jentry, item) in iterate_array(left, left_header) { + if item_set.contains(&(jentry, item)) { + return Ok(true); + } + } + } + OBJECT_CONTAINER_TAG => { + let jentry = JEntry::make_container_jentry(left.len()); + if item_set.contains(&(jentry, left)) { + return Ok(true); + } + } + SCALAR_CONTAINER_TAG => { + let encoded = read_u32(left, 4)?; + let jentry = JEntry::decode_jentry(encoded); + if item_set.contains(&(jentry, &left[8..])) { + return Ok(true); + } + } + _ => { + return Err(Error::InvalidJsonb); + } + } + + Ok(false) + } + + /// Inserts a new element into a JSONB array at the specified position. + /// + /// This function inserts the `new_val` into the JSONB array at the position specified by `pos`. The `pos` parameter can be positive or negative: + /// + /// * **Positive index:** 0-based index from the beginning of the array. + /// * **Negative index:** 1-based index from the end of the array (e.g., -1 refers to the last element). + /// + /// If `pos` is less than 0, the element is inserted at the beginning of the array. If `pos` is greater than or equal to the length of the array, the element is appended to the end. If the input JSONB value is not an array, object or scalar, an error is returned (`Error::InvalidJsonb`). If the input is an object or scalar, it's treated as a single element array. + /// + /// # Arguments + /// + /// * `self` - The JSONB array. + /// * `pos` - The position at which to insert the new element (positive or negative index). + /// * `new_val` - The new element to insert. + /// + /// # Returns + /// + /// * `Ok(OwnedJsonb)` - The modified JSONB array with the new element inserted. + /// * `Err(Error)` - If the input JSONB value is not an array or if the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_jsonb = arr_jsonb.as_raw(); + /// let new_jsonb = "4".parse::().unwrap(); + /// let new_raw_jsonb = new_jsonb.as_raw(); + /// + /// // Insert at index 1 + /// let inserted = raw_jsonb.array_insert(1, new_raw_jsonb).unwrap(); + /// assert_eq!(inserted.to_string(), "[1,4,2,3]"); + /// + /// // Insert at the beginning (pos = 0) + /// let new_raw_jsonb = new_jsonb.as_raw(); + /// let inserted = raw_jsonb.array_insert(0, new_raw_jsonb).unwrap(); + /// assert_eq!(inserted.to_string(), "[4,1,2,3]"); + /// + /// // Insert at the end (pos >= length) + /// let new_raw_jsonb = new_jsonb.as_raw(); + /// let inserted = raw_jsonb.array_insert(10, new_raw_jsonb).unwrap(); + /// assert_eq!(inserted.to_string(), "[1,2,3,4]"); + /// + /// // Insert into an object + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// let new_jsonb = "2".parse::().unwrap(); + /// let new_raw_jsonb = new_jsonb.as_raw(); + /// let inserted = raw_jsonb.array_insert(0, new_raw_jsonb); + /// assert_eq!(inserted.unwrap().to_string(), r#"[2,{"a":1}]"#); + /// + /// // Insert into a scalar + /// let scalar_jsonb = "1".parse::().unwrap(); + /// let raw_jsonb = scalar_jsonb.as_raw(); + /// let new_jsonb = "2".parse::().unwrap(); + /// let new_raw_jsonb = new_jsonb.as_raw(); + /// let inserted = raw_jsonb.array_insert(0, new_raw_jsonb); + /// assert_eq!(inserted.unwrap().to_string(), "[2,1]"); + /// ``` + pub fn array_insert(&self, pos: i32, new_val: RawJsonb) -> Result { + let mut buf = Vec::new(); + let value = self.data; + let new_value = new_val.data; + let header = read_u32(value, 0)?; + let len = match header & CONTAINER_HEADER_TYPE_MASK { + ARRAY_CONTAINER_TAG => (header & CONTAINER_HEADER_LEN_MASK) as i32, + SCALAR_CONTAINER_TAG | OBJECT_CONTAINER_TAG => 1, + _ => { + return Err(Error::InvalidJsonb); + } + }; + + let idx = if pos < 0 { len - pos.abs() } else { pos }; + let idx = if idx < 0 { + 0 + } else if idx > len { + len + } else { + idx + } as usize; + let len = len as usize; + + let mut items = VecDeque::with_capacity(len); + match header & CONTAINER_HEADER_TYPE_MASK { + ARRAY_CONTAINER_TAG => { + for (jentry, item) in iterate_array(value, header) { + items.push_back((jentry, item)); + } + } + OBJECT_CONTAINER_TAG => { + let jentry = JEntry::make_container_jentry(value.len()); + items.push_back((jentry, value)); + } + _ => { + let encoded = read_u32(value, 4)?; + let jentry = JEntry::decode_jentry(encoded); + items.push_back((jentry, &value[8..])); + } + } + + let mut builder = ArrayBuilder::new(len + 1); + if idx > 0 { + let mut i = 0; + while let Some((jentry, item)) = items.pop_front() { + builder.push_raw(jentry, item); + i += 1; + if i >= idx { + break; + } + } + } + + let new_header = read_u32(new_value, 0)?; + match new_header & CONTAINER_HEADER_TYPE_MASK { + ARRAY_CONTAINER_TAG | OBJECT_CONTAINER_TAG => { + let new_jentry = JEntry::make_container_jentry(new_value.len()); + builder.push_raw(new_jentry, new_value); + } + _ => { + let encoded = read_u32(new_value, 4)?; + let new_jentry = JEntry::decode_jentry(encoded); + builder.push_raw(new_jentry, &new_value[8..]); + } + } + + while let Some((jentry, item)) = items.pop_front() { + builder.push_raw(jentry, item); + } + builder.build_into(&mut buf); + + Ok(OwnedJsonb::new(buf)) + } +} diff --git a/src/functions/core.rs b/src/functions/core.rs new file mode 100644 index 0000000..257bebc --- /dev/null +++ b/src/functions/core.rs @@ -0,0 +1,847 @@ +// Copyright 2023 Datafuse Labs. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains the fundamental functions. + +use core::convert::TryInto; +use std::cmp::Ordering; +use std::collections::VecDeque; + +use crate::constants::*; +use crate::error::*; +use crate::iterator::iterate_array; +use crate::iterator::iterate_object_entries; +use crate::jentry::JEntry; +use crate::number::Number; +use crate::parser::parse_value; + +use crate::RawJsonb; + +impl RawJsonb<'_> { + /// Converts a JSONB value to a serde_json::Value. + /// + /// This function converts a JSONB value into a `serde_json::Value`. This allows you to use the powerful features of the `serde_json` crate for further manipulation and processing of the data. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(serde_json::Value)` - The `serde_json::Value` representation of the JSONB data. + /// * `Err(Error)` - If the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// use serde_json::json; + /// + /// // Convert a JSONB object + /// let obj_jsonb = r#"{"a": 1, "b": "hello"}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// let serde_value = raw_jsonb.to_serde_json().unwrap(); + /// assert_eq!(serde_value, json!({"a": 1, "b": "hello"})); + /// + /// // Convert a JSONB array + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_jsonb = arr_jsonb.as_raw(); + /// let serde_value = raw_jsonb.to_serde_json().unwrap(); + /// assert_eq!(serde_value, json!([1, 2, 3])); + /// + /// // Convert a JSONB scalar + /// let num_jsonb = "123".parse::().unwrap(); + /// let raw_jsonb = num_jsonb.as_raw(); + /// let serde_value = raw_jsonb.to_serde_json().unwrap(); + /// assert_eq!(serde_value, json!(123)); + /// + /// // Invalid JSONB data + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.to_serde_json(); + /// assert!(result.is_err()); + /// ``` + pub fn to_serde_json(&self) -> Result { + let value = self.data; + Self::containter_to_serde_json(value) + } + + /// Converts a JSONB value to a serde_json::Map (if it's a JSON object). + /// + /// This function attempts to convert a JSONB value into a `serde_json::Map`. If the JSONB value is a JSON object, the function returns `Some` containing the converted map; otherwise, it returns `None`. This allows using the powerful features of the `serde_json` crate for further manipulation and processing of the object data. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(Some(serde_json::Map))` - If the value is a JSON object, the converted map. + /// * `Ok(None)` - If the value is not a JSON object (e.g., array, scalar). + /// * `Err(Error)` - If the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// use serde_json::json; + /// use serde_json::Map; + /// + /// // JSON object + /// let obj_jsonb = r#"{"a": 1, "b": "hello"}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// let serde_map = raw_jsonb.to_serde_json_object().unwrap(); + /// assert_eq!(serde_map, Some(json!({"a": 1, "b": "hello"}).as_object().unwrap().clone())); + /// + /// // Non-object values + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_jsonb = arr_jsonb.as_raw(); + /// assert_eq!(raw_jsonb.to_serde_json_object().unwrap(), None); + /// + /// let num_jsonb = "123".parse::().unwrap(); + /// let raw_jsonb = num_jsonb.as_raw(); + /// assert_eq!(raw_jsonb.to_serde_json_object().unwrap(), None); + /// + /// // Invalid JSONB data + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.to_serde_json_object(); + /// assert!(result.is_err()); + /// ``` + pub fn to_serde_json_object( + &self, + ) -> Result>, Error> { + let value = self.data; + Self::containter_to_serde_json_object(value) + } + + fn containter_to_serde_json_object( + value: &[u8], + ) -> Result>, Error> { + let header = read_u32(value, 0).unwrap_or_default(); + let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; + + let obj_value = match header & CONTAINER_HEADER_TYPE_MASK { + OBJECT_CONTAINER_TAG => { + let mut obj = serde_json::Map::with_capacity(length); + for (key, jentry, val) in iterate_object_entries(value, header) { + let item = Self::scalar_to_serde_json(jentry, val)?; + obj.insert(key.to_string(), item); + } + Some(obj) + } + ARRAY_CONTAINER_TAG | SCALAR_CONTAINER_TAG => None, + _ => { + return Err(Error::InvalidJsonb); + } + }; + Ok(obj_value) + } + + fn containter_to_serde_json(value: &[u8]) -> Result { + let header = read_u32(value, 0).unwrap_or_default(); + let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; + + let json_value = match header & CONTAINER_HEADER_TYPE_MASK { + OBJECT_CONTAINER_TAG => { + let mut obj = serde_json::Map::with_capacity(length); + for (key, jentry, val) in iterate_object_entries(value, header) { + let item = Self::scalar_to_serde_json(jentry, val)?; + obj.insert(key.to_string(), item); + } + serde_json::Value::Object(obj) + } + ARRAY_CONTAINER_TAG => { + let mut arr = Vec::with_capacity(length); + for (jentry, val) in iterate_array(value, header) { + let item = Self::scalar_to_serde_json(jentry, val)?; + arr.push(item); + } + serde_json::Value::Array(arr) + } + SCALAR_CONTAINER_TAG => { + let encoded = match read_u32(value, 4) { + Ok(encoded) => encoded, + Err(_) => { + return Err(Error::InvalidJsonb); + } + }; + let jentry = JEntry::decode_jentry(encoded); + Self::scalar_to_serde_json(jentry, &value[8..])? + } + _ => { + return Err(Error::InvalidJsonb); + } + }; + Ok(json_value) + } + + fn scalar_to_serde_json(jentry: JEntry, value: &[u8]) -> Result { + let json_value = match jentry.type_code { + NULL_TAG => serde_json::Value::Null, + TRUE_TAG => serde_json::Value::Bool(true), + FALSE_TAG => serde_json::Value::Bool(false), + NUMBER_TAG => { + let len = jentry.length as usize; + let n = Number::decode(&value[..len])?; + match n { + Number::Int64(v) => serde_json::Value::Number(serde_json::Number::from(v)), + Number::UInt64(v) => serde_json::Value::Number(serde_json::Number::from(v)), + Number::Float64(v) => match serde_json::Number::from_f64(v) { + Some(v) => serde_json::Value::Number(v), + None => { + return Err(Error::InvalidJson); + } + }, + } + } + STRING_TAG => { + let len = jentry.length as usize; + let s = unsafe { String::from_utf8_unchecked(value[..len].to_vec()) }; + serde_json::Value::String(s) + } + CONTAINER_TAG => Self::containter_to_serde_json(value)?, + _ => { + return Err(Error::InvalidJsonb); + } + }; + Ok(json_value) + } + + /// Converts the JSONB value to a JSON string. + /// + /// This function serializes the JSONB value into a human-readable JSON string representation. + /// It handles both container types (objects and arrays) and scalar types. If the JSONB data is invalid, + /// it attempts to parse it as text JSON and falls back to "null" if parsing fails. + /// + /// # Returns + /// + /// * `String` - The JSON string representation of the value. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_jsonb = arr_jsonb.as_raw(); + /// assert_eq!(raw_jsonb.to_string(), "[1,2,3]"); + /// + /// let obj_jsonb = r#"{"a": 1, "b": "hello"}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// assert_eq!(raw_jsonb.to_string(), r#"{"a":1,"b":"hello"}"#); + /// + /// let num_jsonb = "123.45".parse::().unwrap(); + /// let raw_jsonb = num_jsonb.as_raw(); + /// assert_eq!(raw_jsonb.to_string(), "123.45"); + /// + /// let string_jsonb = r#""hello, world!""#.parse::().unwrap(); + /// let raw_jsonb = string_jsonb.as_raw(); + /// assert_eq!(raw_jsonb.to_string(), r#""hello, world!""#); + /// + /// let true_jsonb = "true".parse::().unwrap(); + /// let raw_jsonb = true_jsonb.as_raw(); + /// assert_eq!(raw_jsonb.to_string(), "true"); + /// + /// // Example with invalid JSONB data (fallback to text JSON parsing) + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); // Invalid binary JSONB + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// + /// // It will try to parse it as text JSON, in this case fails and return "null" + /// assert_eq!(invalid_raw_jsonb.to_string(), "null"); + /// ``` + #[allow(clippy::inherent_to_string)] + pub fn to_string(&self) -> String { + let value = self.data; + let mut json = String::with_capacity(value.len()); + if Self::container_to_string(value, &mut 0, &mut json, &PrettyOpts::new(false)).is_err() { + json.clear(); + // Compatible with text json data + match parse_value(value) { + Ok(val) => { + json = format!("{}", val); + } + Err(_) => { + json.push_str("null"); + } + } + } + json + } + + /// Converts the JSONB value to a pretty-printed JSON string. + /// + /// This function serializes the JSONB value into a human-readable JSON string representation with indentation for formatting. + /// Like `to_string()`, it handles both container types (objects and arrays) and scalar types. + /// If the JSONB data is invalid, it attempts to parse it as text JSON, pretty-prints the result if successful, + /// and falls back to "null" if either binary or text JSON parsing fails. + /// + /// # Returns + /// + /// * `String` - The pretty-printed JSON string representation of the value. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_jsonb = arr_jsonb.as_raw(); + /// assert_eq!(raw_jsonb.to_pretty_string(), "[\n 1,\n 2,\n 3\n]"); + /// + /// let obj_jsonb = r#"{"a": 1, "b": "hello"}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// assert_eq!(raw_jsonb.to_pretty_string(), "{\n \"a\": 1,\n \"b\": \"hello\"\n}"); + /// + /// let num_jsonb = "123.45".parse::().unwrap(); + /// let raw_jsonb = num_jsonb.as_raw(); + /// assert_eq!(raw_jsonb.to_pretty_string(), "123.45"); + /// + /// let string_jsonb = r#""hello, world!""#.parse::().unwrap(); + /// let raw_jsonb = string_jsonb.as_raw(); + /// assert_eq!(raw_jsonb.to_pretty_string(), r#""hello, world!""#); + /// + /// // Example with invalid JSONB data (fallback to text JSON parsing) + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); // Invalid binary JSONB + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// assert_eq!(invalid_raw_jsonb.to_pretty_string(), "null"); // Fails and returns "null" + /// ``` + pub fn to_pretty_string(&self) -> String { + let value = self.data; + let mut json = String::with_capacity(value.len()); + if Self::container_to_string(value, &mut 0, &mut json, &PrettyOpts::new(true)).is_err() { + json.clear(); + // Compatible with text json data + match parse_value(value) { + Ok(val) => { + let mut buf = Vec::with_capacity(value.len()); + val.write_to_vec(&mut buf); + if Self::container_to_string( + buf.as_ref(), + &mut 0, + &mut json, + &PrettyOpts::new(true), + ) + .is_err() + { + json.clear(); + json.push_str("null"); + } + } + Err(_) => { + json.push_str("null"); + } + } + } + json + } + + fn container_to_string( + value: &[u8], + offset: &mut usize, + json: &mut String, + pretty_opts: &PrettyOpts, + ) -> Result<(), Error> { + let header = read_u32(value, *offset)?; + match header & CONTAINER_HEADER_TYPE_MASK { + SCALAR_CONTAINER_TAG => { + let mut jentry_offset = 4 + *offset; + let mut value_offset = 8 + *offset; + Self::scalar_to_string( + value, + &mut jentry_offset, + &mut value_offset, + json, + pretty_opts, + )?; + } + ARRAY_CONTAINER_TAG => { + if pretty_opts.enabled { + json.push_str("[\n"); + } else { + json.push('['); + } + let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; + let mut jentry_offset = 4 + *offset; + let mut value_offset = 4 + *offset + 4 * length; + let inner_pretty_ops = pretty_opts.inc_indent(); + for i in 0..length { + if i > 0 { + if pretty_opts.enabled { + json.push_str(",\n"); + } else { + json.push(','); + } + } + if pretty_opts.enabled { + json.push_str(&inner_pretty_ops.generate_indent()); + } + Self::scalar_to_string( + value, + &mut jentry_offset, + &mut value_offset, + json, + &inner_pretty_ops, + )?; + } + if pretty_opts.enabled { + json.push('\n'); + json.push_str(&pretty_opts.generate_indent()); + } + json.push(']'); + } + OBJECT_CONTAINER_TAG => { + if pretty_opts.enabled { + json.push_str("{\n"); + } else { + json.push('{'); + } + let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; + let mut jentry_offset = 4 + *offset; + let mut key_offset = 4 + *offset + 8 * length; + let mut keys = VecDeque::with_capacity(length); + for _ in 0..length { + let jentry_encoded = read_u32(value, jentry_offset)?; + let jentry = JEntry::decode_jentry(jentry_encoded); + let key_length = jentry.length as usize; + keys.push_back((key_offset, key_offset + key_length)); + jentry_offset += 4; + key_offset += key_length; + } + let mut value_offset = key_offset; + let inner_pretty_ops = pretty_opts.inc_indent(); + for i in 0..length { + if i > 0 { + if pretty_opts.enabled { + json.push_str(",\n"); + } else { + json.push(','); + } + } + let (key_start, key_end) = keys.pop_front().unwrap(); + if pretty_opts.enabled { + json.push_str(&inner_pretty_ops.generate_indent()); + Self::escape_scalar_string(value, key_start, key_end, json); + json.push_str(": "); + } else { + Self::escape_scalar_string(value, key_start, key_end, json); + json.push(':'); + } + Self::scalar_to_string( + value, + &mut jentry_offset, + &mut value_offset, + json, + &inner_pretty_ops, + )?; + } + if pretty_opts.enabled { + json.push('\n'); + json.push_str(&pretty_opts.generate_indent()); + } + json.push('}'); + } + _ => { + return Err(Error::InvalidJsonb); + } + } + Ok(()) + } + + fn scalar_to_string( + value: &[u8], + jentry_offset: &mut usize, + value_offset: &mut usize, + json: &mut String, + pretty_opts: &PrettyOpts, + ) -> Result<(), Error> { + let jentry_encoded = read_u32(value, *jentry_offset)?; + let jentry = JEntry::decode_jentry(jentry_encoded); + let length = jentry.length as usize; + match jentry.type_code { + NULL_TAG => json.push_str("null"), + TRUE_TAG => json.push_str("true"), + FALSE_TAG => json.push_str("false"), + NUMBER_TAG => { + let num = Number::decode(&value[*value_offset..*value_offset + length])?; + json.push_str(&num.to_string()); + } + STRING_TAG => { + Self::escape_scalar_string(value, *value_offset, *value_offset + length, json); + } + CONTAINER_TAG => { + Self::container_to_string(value, value_offset, json, pretty_opts)?; + } + _ => {} + } + *jentry_offset += 4; + *value_offset += length; + Ok(()) + } + + fn escape_scalar_string(value: &[u8], start: usize, end: usize, json: &mut String) { + json.push('\"'); + let mut last_start = start; + for i in start..end { + // add backslash for escaped characters. + let c = match value[i] { + 0x5C => "\\\\", + 0x22 => "\\\"", + 0x08 => "\\b", + 0x0C => "\\f", + 0x0A => "\\n", + 0x0D => "\\r", + 0x09 => "\\t", + _ => { + continue; + } + }; + if i > last_start { + let val = String::from_utf8_lossy(&value[last_start..i]); + json.push_str(&val); + } + json.push_str(c); + last_start = i + 1; + } + if last_start < end { + let val = String::from_utf8_lossy(&value[last_start..end]); + json.push_str(&val); + } + json.push('\"'); + } +} + +impl Eq for RawJsonb<'_> {} + +/// Implements `PartialOrd` for `RawJsonb`, allowing comparison of two `RawJsonb` values. +/// +/// The comparison logic handles different JSONB types (scalar, array, object) and considers null values. +/// The ordering is defined as follows: +/// +/// 1. Null is considered greater than any other type. +/// 2. Scalars are compared based on their type and value (String > Number > Boolean). +/// 3. Arrays are compared element by element. +/// 4. Objects are compared based on their keys and values. +/// 5. Arrays are greater than objects and scalars. +/// 6. Objects are greater than scalars. +/// 7. If the types are incompatible, None is returned. +#[allow(clippy::non_canonical_partial_ord_impl)] +impl PartialOrd for RawJsonb<'_> { + fn partial_cmp(&self, other: &Self) -> Option { + let left = self.data; + let right = other.data; + let left_header = read_u32(left, 0).ok()?; + let right_header = read_u32(right, 0).ok()?; + match ( + left_header & CONTAINER_HEADER_TYPE_MASK, + right_header & CONTAINER_HEADER_TYPE_MASK, + ) { + (SCALAR_CONTAINER_TAG, SCALAR_CONTAINER_TAG) => { + let left_encoded = read_u32(left, 4).ok()?; + let left_jentry = JEntry::decode_jentry(left_encoded); + let right_encoded = read_u32(right, 4).ok()?; + let right_jentry = JEntry::decode_jentry(right_encoded); + compare_scalar(&left_jentry, &left[8..], &right_jentry, &right[8..]) + } + (ARRAY_CONTAINER_TAG, ARRAY_CONTAINER_TAG) => { + compare_array(left_header, &left[4..], right_header, &right[4..]) + } + (OBJECT_CONTAINER_TAG, OBJECT_CONTAINER_TAG) => { + compare_object(left_header, &left[4..], right_header, &right[4..]) + } + (SCALAR_CONTAINER_TAG, ARRAY_CONTAINER_TAG | OBJECT_CONTAINER_TAG) => { + let left_encoded = read_u32(left, 4).ok()?; + let left_jentry = JEntry::decode_jentry(left_encoded); + match left_jentry.type_code { + NULL_TAG => Some(Ordering::Greater), + _ => Some(Ordering::Less), + } + } + (ARRAY_CONTAINER_TAG | OBJECT_CONTAINER_TAG, SCALAR_CONTAINER_TAG) => { + let right_encoded = read_u32(right, 4).ok()?; + let right_jentry = JEntry::decode_jentry(right_encoded); + match right_jentry.type_code { + NULL_TAG => Some(Ordering::Less), + _ => Some(Ordering::Greater), + } + } + (ARRAY_CONTAINER_TAG, OBJECT_CONTAINER_TAG) => Some(Ordering::Greater), + (OBJECT_CONTAINER_TAG, ARRAY_CONTAINER_TAG) => Some(Ordering::Less), + (_, _) => None, + } + } +} + +/// Implements `Ord` for `RawJsonb`, allowing comparison of two `RawJsonb` values using the total ordering. +/// This implementation leverages the `PartialOrd` implementation, returning `Ordering::Equal` for incomparable values. +impl Ord for RawJsonb<'_> { + fn cmp(&self, other: &Self) -> Ordering { + match self.partial_cmp(other) { + Some(ordering) => ordering, + None => Ordering::Equal, + } + } +} + +pub(crate) fn extract_by_jentry( + jentry: &JEntry, + encoded: u32, + offset: usize, + value: &[u8], +) -> Vec { + let length = jentry.length as usize; + match jentry.type_code { + CONTAINER_TAG => value[offset..offset + length].to_vec(), + _ => { + let mut buf = Vec::with_capacity(8 + length); + buf.extend_from_slice(&SCALAR_CONTAINER_TAG.to_be_bytes()); + buf.extend_from_slice(&encoded.to_be_bytes()); + if jentry.length > 0 { + buf.extend_from_slice(&value[offset..offset + length]); + } + buf + } + } +} + +// Different types of values have different levels and are definitely not equal +pub(crate) fn jentry_compare_level(jentry: &JEntry) -> u8 { + match jentry.type_code { + NULL_TAG => NULL_LEVEL, + CONTAINER_TAG => OBJECT_LEVEL, + STRING_TAG => STRING_LEVEL, + NUMBER_TAG => NUMBER_LEVEL, + TRUE_TAG => TRUE_LEVEL, + FALSE_TAG => FALSE_LEVEL, + _ => INVALID_LEVEL, + } +} + +// `Scalar` values compare as the following order +// Null > Container(Array > Object) > String > Number > Boolean +fn compare_scalar( + left_jentry: &JEntry, + left: &[u8], + right_jentry: &JEntry, + right: &[u8], +) -> Option { + let left_level = jentry_compare_level(left_jentry); + let right_level = jentry_compare_level(right_jentry); + if left_level != right_level { + return Some(left_level.cmp(&right_level)); + } + + match (left_jentry.type_code, right_jentry.type_code) { + (NULL_TAG, NULL_TAG) => Some(Ordering::Equal), + (CONTAINER_TAG, CONTAINER_TAG) => compare_container(left, right), + (STRING_TAG, STRING_TAG) => { + let left_offset = left_jentry.length as usize; + let left_str = unsafe { std::str::from_utf8_unchecked(&left[..left_offset]) }; + let right_offset = right_jentry.length as usize; + let right_str = unsafe { std::str::from_utf8_unchecked(&right[..right_offset]) }; + Some(left_str.cmp(right_str)) + } + (NUMBER_TAG, NUMBER_TAG) => { + let left_offset = left_jentry.length as usize; + let left_num = Number::decode(&left[..left_offset]).ok()?; + let right_offset = right_jentry.length as usize; + let right_num = Number::decode(&right[..right_offset]).ok()?; + Some(left_num.cmp(&right_num)) + } + (TRUE_TAG, TRUE_TAG) => Some(Ordering::Equal), + (FALSE_TAG, FALSE_TAG) => Some(Ordering::Equal), + (_, _) => None, + } +} + +fn compare_container(left: &[u8], right: &[u8]) -> Option { + let left_header = read_u32(left, 0).ok()?; + let right_header = read_u32(right, 0).ok()?; + + match ( + left_header & CONTAINER_HEADER_TYPE_MASK, + right_header & CONTAINER_HEADER_TYPE_MASK, + ) { + (ARRAY_CONTAINER_TAG, ARRAY_CONTAINER_TAG) => { + compare_array(left_header, &left[4..], right_header, &right[4..]) + } + (OBJECT_CONTAINER_TAG, OBJECT_CONTAINER_TAG) => { + compare_object(left_header, &left[4..], right_header, &right[4..]) + } + (ARRAY_CONTAINER_TAG, OBJECT_CONTAINER_TAG) => Some(Ordering::Greater), + (OBJECT_CONTAINER_TAG, ARRAY_CONTAINER_TAG) => Some(Ordering::Less), + (_, _) => None, + } +} + +// `Array` values compares each element in turn. +fn compare_array( + left_header: u32, + left: &[u8], + right_header: u32, + right: &[u8], +) -> Option { + let left_length = (left_header & CONTAINER_HEADER_LEN_MASK) as usize; + let right_length = (right_header & CONTAINER_HEADER_LEN_MASK) as usize; + + let mut jentry_offset = 0; + let mut left_val_offset = 4 * left_length; + let mut right_val_offset = 4 * right_length; + let length = if left_length <= right_length { + left_length + } else { + right_length + }; + for _ in 0..length { + let left_encoded = read_u32(left, jentry_offset).ok()?; + let left_jentry = JEntry::decode_jentry(left_encoded); + let right_encoded = read_u32(right, jentry_offset).ok()?; + let right_jentry = JEntry::decode_jentry(right_encoded); + + let order = compare_scalar( + &left_jentry, + &left[left_val_offset..], + &right_jentry, + &right[right_val_offset..], + )?; + if order != Ordering::Equal { + return Some(order); + } + jentry_offset += 4; + + left_val_offset += left_jentry.length as usize; + right_val_offset += right_jentry.length as usize; + } + + Some(left_length.cmp(&right_length)) +} + +// `Object` values compares each key-value in turn, +// first compare the key, and then compare the value if the key is equal. +// The larger the key/value, the larger the `Object`. +fn compare_object( + left_header: u32, + left: &[u8], + right_header: u32, + right: &[u8], +) -> Option { + let left_length = (left_header & CONTAINER_HEADER_LEN_MASK) as usize; + let right_length = (right_header & CONTAINER_HEADER_LEN_MASK) as usize; + + let mut left_jentry_offset = 0; + let mut left_val_offset = 8 * left_length; + let mut right_jentry_offset = 0; + let mut right_val_offset = 8 * right_length; + + // read all left key jentries and right key jentries first. + // Note: since the values are stored after the keys, + // we must first read all the key jentries to get the correct value offset. + let mut left_key_jentries: VecDeque = VecDeque::with_capacity(left_length); + let mut right_key_jentries: VecDeque = VecDeque::with_capacity(right_length); + for _ in 0..left_length { + let left_encoded = read_u32(left, left_jentry_offset).ok()?; + let left_key_jentry = JEntry::decode_jentry(left_encoded); + + left_jentry_offset += 4; + left_val_offset += left_key_jentry.length as usize; + left_key_jentries.push_back(left_key_jentry); + } + for _ in 0..right_length { + let right_encoded = read_u32(right, right_jentry_offset).ok()?; + let right_key_jentry = JEntry::decode_jentry(right_encoded); + + right_jentry_offset += 4; + right_val_offset += right_key_jentry.length as usize; + right_key_jentries.push_back(right_key_jentry); + } + + let length = if left_length <= right_length { + left_length + } else { + right_length + }; + + let mut left_key_offset = 8 * left_length; + let mut right_key_offset = 8 * right_length; + for _ in 0..length { + // first compare key, if keys are equal, then compare the value + let left_key_jentry = left_key_jentries.pop_front().unwrap(); + let right_key_jentry = right_key_jentries.pop_front().unwrap(); + + let key_order = compare_scalar( + &left_key_jentry, + &left[left_key_offset..], + &right_key_jentry, + &right[right_key_offset..], + )?; + if key_order != Ordering::Equal { + return Some(key_order); + } + + let left_encoded = read_u32(left, left_jentry_offset).ok()?; + let left_val_jentry = JEntry::decode_jentry(left_encoded); + let right_encoded = read_u32(right, right_jentry_offset).ok()?; + let right_val_jentry = JEntry::decode_jentry(right_encoded); + + let val_order = compare_scalar( + &left_val_jentry, + &left[left_val_offset..], + &right_val_jentry, + &right[right_val_offset..], + )?; + if val_order != Ordering::Equal { + return Some(val_order); + } + left_jentry_offset += 4; + right_jentry_offset += 4; + + left_key_offset += left_key_jentry.length as usize; + right_key_offset += right_key_jentry.length as usize; + left_val_offset += left_val_jentry.length as usize; + right_val_offset += right_val_jentry.length as usize; + } + + Some(left_length.cmp(&right_length)) +} + +struct PrettyOpts { + enabled: bool, + indent: usize, +} + +impl PrettyOpts { + fn new(enabled: bool) -> Self { + Self { enabled, indent: 0 } + } + + fn inc_indent(&self) -> Self { + Self { + enabled: self.enabled, + indent: self.indent + 2, + } + } + + fn generate_indent(&self) -> String { + String::from_utf8(vec![0x20; self.indent]).unwrap() + } +} + +pub(crate) fn read_u32(buf: &[u8], idx: usize) -> Result { + let bytes: [u8; 4] = buf + .get(idx..idx + 4) + .ok_or(Error::InvalidEOF)? + .try_into() + .unwrap(); + Ok(u32::from_be_bytes(bytes)) +} diff --git a/src/functions/mod.rs b/src/functions/mod.rs new file mode 100644 index 0000000..5b92886 --- /dev/null +++ b/src/functions/mod.rs @@ -0,0 +1,20 @@ +// Copyright 2023 Datafuse Labs. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod array; +mod core; +mod object; +mod other; +mod path; +mod scalar; diff --git a/src/functions/object.rs b/src/functions/object.rs new file mode 100644 index 0000000..451af66 --- /dev/null +++ b/src/functions/object.rs @@ -0,0 +1,723 @@ +// Copyright 2023 Datafuse Labs. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains functions that specifically operate on JSONB object values. + +use core::convert::TryInto; +use std::collections::BTreeSet; +use std::collections::VecDeque; +use std::str::from_utf8; +use std::str::from_utf8_unchecked; + +use crate::builder::ObjectBuilder; +use crate::constants::*; +use crate::error::*; +use crate::functions::core::extract_by_jentry; +use crate::functions::core::read_u32; +use crate::iterator::iteate_object_keys; +use crate::iterator::iterate_array; +use crate::iterator::iterate_object_entries; +use crate::jentry::JEntry; + +use crate::OwnedJsonb; +use crate::RawJsonb; + +impl OwnedJsonb { + /// Builds a JSONB object from a collection of key-value pairs. + /// + /// This function constructs a new JSONB object from an iterator of key-value pairs. The keys are strings, and the values are `RawJsonb` values. The resulting `OwnedJsonb` represents the binary encoding of the object. The input iterator must be an `ExactSizeIterator` to allow for efficient pre-allocation of the output buffer. + /// + /// # Arguments + /// + /// * `items` - An iterator of `(K, &'a RawJsonb<'a>)` tuples, where `K` is a type that can be converted into a string slice (`AsRef`) representing the key, and the second element is a `RawJsonb` representing the value. Must be an `ExactSizeIterator`. + /// + /// # Returns + /// + /// * `Ok(OwnedJsonb)` - The newly created JSONB object. + /// * `Err(Error)` - If any of the input `RawJsonb` values are invalid, if any key is not valid UTF-8, or if an error occurs during object construction. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::{OwnedJsonb, RawJsonb}; + /// + /// // Create some RawJsonb values + /// let owned_num = "1".parse::().unwrap(); + /// let owned_str = r#""hello""#.parse::().unwrap(); + /// let owned_arr = "[1,2,3]".parse::().unwrap(); + /// + /// // Build the object + /// let raw_jsonbs = vec![("a", owned_num.as_raw()), ("b", owned_str.as_raw()), ("c", owned_arr.as_raw())]; + /// let object_result = OwnedJsonb::build_object(raw_jsonbs.into_iter()); + /// assert!(object_result.is_ok()); + /// let object = object_result.unwrap(); + /// + /// // Convert to string for easy verification + /// assert_eq!(object.to_string(), r#"{"a":1,"b":"hello","c":[1,2,3]}"#); + /// + /// // Example with an empty iterator + /// let empty_object = OwnedJsonb::build_object(<[(&str, RawJsonb<'_>); 0] as IntoIterator>::into_iter([])).unwrap(); + /// assert_eq!(empty_object.to_string(), "{}"); + /// + /// // Example with invalid value + /// let invalid_data = OwnedJsonb::new(vec![1,2,3,4]); + /// let result = OwnedJsonb::build_object([("a", invalid_data.as_raw())].into_iter()); + /// assert!(result.is_err()); + /// ``` + pub fn build_object<'a, K: AsRef>( + items: impl IntoIterator)>, + ) -> Result { + let mut key_jentries = Vec::new(); + let mut val_jentries = Vec::new(); + let mut key_data = Vec::new(); + let mut val_data = Vec::new(); + + for (key, value) in items.into_iter() { + let key = key.as_ref(); + // write key jentry and key data + let encoded_key_jentry = (STRING_TAG | key.len() as u32).to_be_bytes(); + key_jentries.push(encoded_key_jentry); + key_data.extend_from_slice(key.as_bytes()); + + // build value jentry and write value data + let header = read_u32(value.data, 0)?; + let encoded_val_jentry = match header & CONTAINER_HEADER_TYPE_MASK { + SCALAR_CONTAINER_TAG => { + let jentry = &value.data[4..8]; + val_data.extend_from_slice(&value.data[8..]); + jentry.try_into().unwrap() + } + ARRAY_CONTAINER_TAG | OBJECT_CONTAINER_TAG => { + val_data.extend_from_slice(value.data); + (CONTAINER_TAG | value.data.len() as u32).to_be_bytes() + } + _ => return Err(Error::InvalidJsonbHeader), + }; + val_jentries.push(encoded_val_jentry); + } + let len = key_jentries.len(); + // reserve space for header, jentries and value data + let mut buf = Vec::with_capacity(key_data.len() + val_data.len() + len * 8 + 4); + // write header + let header = OBJECT_CONTAINER_TAG | (len as u32); + buf.extend_from_slice(&header.to_be_bytes()); + // write key jentries + for jentry in key_jentries.into_iter() { + buf.extend_from_slice(&jentry); + } + // write value jentries + for jentry in val_jentries.into_iter() { + buf.extend_from_slice(&jentry); + } + // write key data and value data + buf.extend_from_slice(&key_data); + buf.extend_from_slice(&val_data); + Ok(OwnedJsonb::new(buf)) + } +} + +impl RawJsonb<'_> { + /// Returns an `OwnedJsonb` array containing the keys of the JSONB object. + /// + /// If the JSONB value is an object, this function returns a new `OwnedJsonb` array containing the keys of the object as string values. + /// The order of the keys in the returned array is the same as their order in the original object. + /// If the JSONB value is not an object (e.g., it's an array or a scalar), this function returns `Ok(None)`. + /// + /// # Returns + /// + /// * `Ok(Some(OwnedJsonb))` - An `OwnedJsonb` representing the array of keys if the input is an object. + /// * `Ok(None)` - If the input is not an object. + /// * `Err(Error)` - If an error occurred during decoding (e.g., invalid JSONB data). + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // Object keys + /// let obj_jsonb = r#"{"a": 1, "b": 2, "c": 3}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// let keys_result = raw_jsonb.object_keys(); + /// assert!(keys_result.is_ok()); + /// + /// let keys_jsonb = keys_result.unwrap(); + /// assert_eq!(keys_jsonb.as_ref().map(|k| k.to_string()), Some(r#"["a","b","c"]"#.to_string())); + /// + /// // Array - returns None + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_jsonb = arr_jsonb.as_raw(); + /// let keys_result = raw_jsonb.object_keys(); + /// assert!(keys_result.is_ok()); + /// assert!(keys_result.unwrap().is_none()); + /// + /// // Scalar - returns None + /// let scalar_jsonb = "1".parse::().unwrap(); + /// let raw_jsonb = scalar_jsonb.as_raw(); + /// let keys_result = raw_jsonb.object_keys(); + /// assert!(keys_result.is_ok()); + /// assert!(keys_result.unwrap().is_none()); + /// ``` + pub fn object_keys(&self) -> Result, Error> { + let header = read_u32(self.data, 0)?; + match header & CONTAINER_HEADER_TYPE_MASK { + OBJECT_CONTAINER_TAG => { + let mut buf: Vec = Vec::new(); + let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; + let key_header = ARRAY_CONTAINER_TAG | length as u32; + buf.extend_from_slice(&key_header.to_be_bytes()); + + let mut jentry_offset = 4; + let mut key_offset = 8 * length + 4; + let mut key_offsets = Vec::with_capacity(length); + for _ in 0..length { + let key_encoded = read_u32(self.data, jentry_offset)?; + let key_jentry = JEntry::decode_jentry(key_encoded); + buf.extend_from_slice(&key_encoded.to_be_bytes()); + + jentry_offset += 4; + key_offset += key_jentry.length as usize; + key_offsets.push(key_offset); + } + let mut prev_key_offset = 8 * length + 4; + for key_offset in key_offsets { + if key_offset > prev_key_offset { + buf.extend_from_slice(&self.data[prev_key_offset..key_offset]); + } + prev_key_offset = key_offset; + } + Ok(Some(OwnedJsonb::new(buf))) + } + ARRAY_CONTAINER_TAG | SCALAR_CONTAINER_TAG => Ok(None), + _ => Err(Error::InvalidJsonb), + } + } + + /// Iterates over the key-value pairs of a JSONB object. + /// + /// If the JSONB value is an object, this function returns a vector of tuples, where each tuple contains + /// the key (as a `String`) and the value (as an `OwnedJsonb`) of a key-value pair. + /// The order of the key-value pairs in the returned vector is the same as their order in the original object. + /// If the JSONB value is not an object (e.g., it's an array or a scalar), this function returns `Ok(None)`. + /// + /// # Returns + /// + /// * `Ok(Some(Vec<(String, OwnedJsonb)>))` - A vector of tuples representing the key-value pairs if the input is an object. + /// * `Ok(None)` - If the input is not an object. + /// * `Err(Error)` - If an error occurred during decoding (e.g., invalid JSONB data, invalid UTF-8 key). + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // Object iteration + /// let obj_jsonb = r#"{"a": 1, "b": "hello", "c": [1, 2]}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// let items_result = raw_jsonb.object_each(); + /// assert!(items_result.is_ok()); + /// + /// let items = items_result.unwrap().unwrap(); + /// assert_eq!(items.len(), 3); + /// + /// assert_eq!(items[0].0, "a"); + /// assert_eq!(items[0].1.to_string(), "1"); + /// assert_eq!(items[1].0, "b"); + /// assert_eq!(items[1].1.to_string(), r#""hello""#); + /// assert_eq!(items[2].0, "c"); + /// assert_eq!(items[2].1.to_string(), r#"[1,2]"#); + /// + /// // Array - returns None + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_jsonb = arr_jsonb.as_raw(); + /// let items_result = raw_jsonb.object_each(); + /// assert!(items_result.is_ok()); + /// assert!(items_result.unwrap().is_none()); + /// + /// // Scalar - returns None + /// let scalar_jsonb = "1".parse::().unwrap(); + /// let raw_jsonb = scalar_jsonb.as_raw(); + /// let items_result = raw_jsonb.object_each(); + /// assert!(items_result.is_ok()); + /// assert!(items_result.unwrap().is_none()); + /// ``` + pub fn object_each(&self) -> Result>, Error> { + let header = read_u32(self.data, 0)?; + + match header & CONTAINER_HEADER_TYPE_MASK { + OBJECT_CONTAINER_TAG => { + let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; + let mut items: Vec<(String, OwnedJsonb)> = Vec::with_capacity(length); + let mut jentries: VecDeque<(JEntry, u32)> = VecDeque::with_capacity(length * 2); + let mut offset = 4; + + for _ in 0..length * 2 { + let encoded = read_u32(self.data, offset)?; + offset += 4; + jentries.push_back((JEntry::decode_jentry(encoded), encoded)); + } + + let mut keys: VecDeque = VecDeque::with_capacity(length); + for _ in 0..length { + let (jentry, _) = jentries.pop_front().unwrap(); + let key_len = jentry.length as usize; + let key_data = self.data[offset..offset + key_len].to_vec(); + let key = String::from_utf8(key_data).map_err(|_| Error::InvalidJsonb)?; + keys.push_back(key); + offset += key_len; + } + + for _ in 0..length { + let (jentry, encoded) = jentries.pop_front().unwrap(); + let key = keys.pop_front().unwrap(); + let val_length = jentry.length as usize; + let val_data = extract_by_jentry(&jentry, encoded, offset, self.data); + let val = OwnedJsonb::new(val_data); + offset += val_length; + items.push((key, val)); + } + Ok(Some(items)) + } + ARRAY_CONTAINER_TAG | SCALAR_CONTAINER_TAG => Ok(None), + _ => Err(Error::InvalidJsonb), + } + } + + /// Inserts or updates a key-value pair in a JSONB object. + /// + /// This function inserts a new key-value pair into a JSONB object or updates an existing key-value pair if the key already exists. The behavior is controlled by the `update_flag`: + /// + /// * `update_flag = true`: If the key already exists, its value is updated with `new_val`. If the key does not exist, it is inserted. + /// * `update_flag = false`: If the key already exists, an error (`Error::ObjectDuplicateKey`) is returned. If the key does not exist, it is inserted. + /// + /// The input JSONB value must be an object; otherwise, an error (`Error::InvalidObject`) is returned. Invalid JSONB data results in an `Error::InvalidJsonb`. + /// + /// # Arguments + /// + /// * `self` - The JSONB object. + /// * `new_key` - The key to insert or update. + /// * `new_val` - The new value. + /// * `update_flag` - A boolean indicating whether to update an existing key (true) or fail if a duplicate key is found (false). + /// + /// # Returns + /// + /// * `Ok(OwnedJsonb)` - The modified JSONB object. + /// * `Err(Error)` - If the input is not a JSONB object, if `update_flag` is false and the key already exists, or if the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // Inserting a new key-value pair + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// let new_jsonb = "2".parse::().unwrap(); + /// let new_raw_jsonb = new_jsonb.as_raw(); + /// let inserted = raw_jsonb.object_insert("b", new_raw_jsonb, false).unwrap(); + /// assert_eq!(inserted.to_string(), r#"{"a":1,"b":2}"#); + /// + /// // Updating an existing key-value pair + /// let updated = inserted.as_raw().object_insert("b", r#"3"#.parse::().unwrap().as_raw(), true).unwrap(); + /// assert_eq!(updated.to_string(), r#"{"a":1,"b":3}"#); + /// + /// // Attempting to insert a duplicate key without update + /// let result = raw_jsonb.object_insert("a", r#"4"#.parse::().unwrap().as_raw(), false); + /// assert!(result.is_err()); // Returns an error because key "a" already exists + /// + /// // Invalid JSONB input + /// let invalid_jsonb = OwnedJsonb::new(vec![1,2,3,4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let new_raw_jsonb = new_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.object_insert("a", new_raw_jsonb, false); + /// assert!(result.is_err()); // Returns an error due to invalid JSONB data + /// + /// // Inserting into a non-object + /// let arr_jsonb = "[1,2,3]".parse::().unwrap(); + /// let arr_raw_jsonb = invalid_jsonb.as_raw(); + /// let new_raw_jsonb = new_jsonb.as_raw(); + /// let result = arr_raw_jsonb.object_insert("a", new_raw_jsonb, false); + /// assert!(result.is_err()); // Returns an error because input is not a JSONB object + /// ``` + pub fn object_insert( + &self, + new_key: &str, + new_val: RawJsonb, + update_flag: bool, + ) -> Result { + let mut buf = Vec::new(); + let value = self.data; + let new_value = new_val.data; + + let header = read_u32(value, 0)?; + match header & CONTAINER_HEADER_TYPE_MASK { + OBJECT_CONTAINER_TAG => {} + ARRAY_CONTAINER_TAG | SCALAR_CONTAINER_TAG => { + return Err(Error::InvalidObject); + } + _ => { + return Err(Error::InvalidJsonb); + } + } + + let mut idx = 0; + let mut duplicate_key = false; + for (i, obj_key) in iteate_object_keys(value, header).enumerate() { + if new_key.eq(obj_key) { + if !update_flag { + return Err(Error::ObjectDuplicateKey); + } + idx = i; + duplicate_key = true; + break; + } else if new_key > obj_key { + idx = i + 1; + } else { + break; + } + } + + let mut builder = ObjectBuilder::new(); + let mut obj_iter = iterate_object_entries(value, header); + for _ in 0..idx { + if let Some((key, jentry, item)) = obj_iter.next() { + builder.push_raw(key, jentry, item); + } + } + // insert new key and value + let new_header = read_u32(new_value, 0)?; + match new_header & CONTAINER_HEADER_TYPE_MASK { + ARRAY_CONTAINER_TAG | OBJECT_CONTAINER_TAG => { + let new_jentry = JEntry::make_container_jentry(new_value.len()); + builder.push_raw(new_key, new_jentry, new_value); + } + SCALAR_CONTAINER_TAG => { + let encoded = read_u32(new_value, 4)?; + let new_jentry = JEntry::decode_jentry(encoded); + builder.push_raw(new_key, new_jentry, &new_value[8..]); + } + _ => { + return Err(Error::InvalidJsonb); + } + } + // if the key is duplicated, ignore the original key and value. + if duplicate_key { + let _ = obj_iter.next(); + } + for (key, jentry, item) in obj_iter { + builder.push_raw(key, jentry, item); + } + builder.build_into(&mut buf); + + Ok(OwnedJsonb::new(buf)) + } + + /// Deletes key-value pairs from a JSONB object based on a set of keys. + /// + /// This function removes key-value pairs from a JSONB object where the keys are present in the provided `keys` set. The key comparison is case-sensitive. + /// + /// If the input JSONB value is not an object, an error (`Error::InvalidObject`) is returned. Other invalid JSONB data results in an `Error::InvalidJsonb`. + /// + /// # Arguments + /// + /// * `self` - The JSONB object. + /// * `keys` - A set of keys to delete. + /// + /// # Returns + /// + /// * `Ok(OwnedJsonb)` - The modified JSONB object with the specified keys removed. + /// * `Err(Error)` - If the input JSONB value is not an object, or if the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// use std::collections::BTreeSet; + /// + /// let obj_jsonb = r#"{"a": 1, "b": "hello", "c": 3}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// + /// // Delete keys "a" and "c" + /// let keys_to_delete: BTreeSet<&str> = ["a", "c"].into_iter().collect(); + /// let deleted = raw_jsonb.object_delete(&keys_to_delete).unwrap(); + /// assert_eq!(deleted.to_string(), r#"{"b":"hello"}"#); + /// + /// // Delete a non-existent key + /// let keys_to_delete: BTreeSet<&str> = ["x"].into_iter().collect(); + /// let deleted = raw_jsonb.object_delete(&keys_to_delete).unwrap(); + /// assert_eq!(deleted.to_string(), r#"{"a":1,"b":"hello","c":3}"#); // Original object returned + /// + /// // Attempting to delete from a non-object + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let result = arr_jsonb.as_raw().object_delete(&keys_to_delete); + /// assert!(result.is_err()); // Returns an error + /// + /// // Invalid JSONB data + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.object_delete(&keys_to_delete); + /// assert!(result.is_err()); // Returns an error + /// ``` + pub fn object_delete(&self, keys: &BTreeSet<&str>) -> Result { + let mut buf = Vec::new(); + let value = self.data; + let header = read_u32(value, 0)?; + match header & CONTAINER_HEADER_TYPE_MASK { + OBJECT_CONTAINER_TAG => {} + ARRAY_CONTAINER_TAG | SCALAR_CONTAINER_TAG => { + return Err(Error::InvalidObject); + } + _ => { + return Err(Error::InvalidJsonb); + } + } + + let mut builder = ObjectBuilder::new(); + for (key, jentry, item) in iterate_object_entries(value, header) { + if keys.contains(key) { + continue; + } + builder.push_raw(key, jentry, item); + } + builder.build_into(&mut buf); + + Ok(OwnedJsonb::new(buf)) + } + + /// Creates a new JSONB object containing only the specified keys from the original object. + /// + /// This function selects a subset of key-value pairs from a JSONB object based on the provided `keys` set. Only key-value pairs where the key is present in the `keys` set are included in the resulting object. The key comparison is case-sensitive. + /// + /// If the input JSONB value is not an object, an error (`Error::InvalidObject`) is returned. Invalid JSONB data results in an `Error::InvalidJsonb`. + /// + /// # Arguments + /// + /// * `self` - The JSONB object. + /// * `keys` - A set of keys to select. + /// + /// # Returns + /// + /// * `Ok(OwnedJsonb)` - A new JSONB object containing only the key-value pairs specified by the `keys` set. Returns an empty object `{}` if none of the keys are found in the input. + /// * `Err(Error)` - If the input JSONB value is not an object, or if the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// use std::collections::BTreeSet; + /// + /// let obj_jsonb = r#"{"a": 1, "b": "hello", "c": 3}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// + /// // Pick keys "a" and "c" + /// let keys_to_pick: BTreeSet<&str> = ["a", "c"].into_iter().collect(); + /// let picked = raw_jsonb.object_pick(&keys_to_pick).unwrap(); + /// assert_eq!(picked.to_string(), r#"{"a":1,"c":3}"#); + /// + /// // Pick a non-existent key + /// let keys_to_pick: BTreeSet<&str> = ["x"].into_iter().collect(); + /// let picked = raw_jsonb.object_pick(&keys_to_pick).unwrap(); + /// assert_eq!(picked.to_string(), "{}"); // Empty object returned + /// + /// // Attempting to pick from a non-object + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let result = arr_jsonb.as_raw().object_pick(&keys_to_pick); + /// assert!(result.is_err()); // Returns an error + /// + /// // Invalid JSONB data + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.object_pick(&keys_to_pick); + /// assert!(result.is_err()); // Returns an error + /// ``` + pub fn object_pick(&self, keys: &BTreeSet<&str>) -> Result { + let mut buf = Vec::new(); + let value = self.data; + + let header = read_u32(value, 0)?; + match header & CONTAINER_HEADER_TYPE_MASK { + OBJECT_CONTAINER_TAG => {} + ARRAY_CONTAINER_TAG | SCALAR_CONTAINER_TAG => { + return Err(Error::InvalidObject); + } + _ => { + return Err(Error::InvalidJsonb); + } + } + + let mut builder = ObjectBuilder::new(); + for (key, jentry, item) in iterate_object_entries(value, header) { + if !keys.contains(key) { + continue; + } + builder.push_raw(key, jentry, item); + } + builder.build_into(&mut buf); + + Ok(OwnedJsonb::new(buf)) + } + + /// Checks if all specified keys exist in a JSONB object. + /// + /// This function checks if a JSONB object contains *all* of the keys provided in the `keys` iterator. + /// The keys are expected to be UTF-8 encoded byte slices. If the JSONB value is not an object, + /// the function will return `Ok(false)`. + /// + /// # Arguments + /// + /// * `keys` - An iterator of byte slices representing the keys to check for. + /// + /// # Returns + /// + /// * `Ok(true)` - If all keys exist in the JSONB object. + /// * `Ok(false)` - If any of the keys do not exist in the object, if any key is not valid UTF-8, or if the JSONB value is not an object. + /// * `Err(Error)` - If an error occurred during decoding (e.g., invalid JSONB data other than the value not being an object). + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// let obj_jsonb = r#"{"a": 1, "b": 2, "c": 3}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// + /// let keys = ["a".as_bytes(), "b".as_bytes(), "c".as_bytes()]; + /// assert!(raw_jsonb.exists_all_keys(keys.into_iter()).unwrap()); + /// + /// let keys = ["b".as_bytes(), "b".as_bytes(), "d".as_bytes()]; + /// assert!(!raw_jsonb.exists_all_keys(keys.into_iter()).unwrap()); // "d" does not exist + /// + /// let keys = ["a".as_bytes(), "b".as_bytes(), &[0xff_u8]]; // Invalid UTF-8 + /// assert!(!raw_jsonb.exists_all_keys(keys.into_iter()).unwrap()); + /// + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_jsonb = arr_jsonb.as_raw(); + /// let keys = ["a".as_bytes()]; + /// assert!(!raw_jsonb.exists_all_keys(keys.into_iter()).unwrap()); // Not an object + /// + /// let obj_jsonb = r#"{"a b": 1, "c": 2}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// let keys = ["a b".as_bytes(), "c".as_bytes()]; + /// assert!(raw_jsonb.exists_all_keys(keys.into_iter()).unwrap()); + /// ``` + pub fn exists_all_keys<'a, I: Iterator>( + &self, + keys: I, + ) -> Result { + let value = self.data; + let header = read_u32(value, 0)?; + + for key in keys { + match from_utf8(key) { + Ok(key) => { + if !Self::exists_key(value, header, key)? { + return Ok(false); + } + } + Err(_) => return Ok(false), + } + } + Ok(true) + } + + /// Checks if any of the specified keys exist in a JSONB object. + /// + /// This function checks if a JSONB object contains *any* of the keys provided in the `keys` iterator. + /// The keys are expected to be UTF-8 encoded byte slices. + /// If the JSONB value is not an object, the function will return `Ok(false)`. + /// + /// # Arguments + /// + /// * `keys` - An iterator of byte slices representing the keys to check for. + /// + /// # Returns + /// + /// * `Ok(true)` - If any of the keys exist in the JSONB object. + /// * `Ok(false)` - If none of the keys exist in the object, if any key is not valid UTF-8, or if the JSONB value is not an object. + /// * `Err(Error)` - If an error occurred during decoding (e.g., invalid JSONB data other than the value not being an object). + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// let obj_jsonb = r#"{"a": 1, "b": 2, "c": 3}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// + /// let keys = ["a".as_bytes(), "d".as_bytes(), "e".as_bytes()]; + /// assert!(raw_jsonb.exists_any_keys(keys.into_iter()).unwrap()); // "a" exists + /// + /// let keys = ["d".as_bytes(), "e".as_bytes(), "f".as_bytes()]; + /// assert!(!raw_jsonb.exists_any_keys(keys.into_iter()).unwrap()); // None of the keys exist + /// + /// let keys = ["d".as_bytes(), &[0xff_u8], "f".as_bytes()]; // Invalid UTF-8 for the second key + /// assert!(!raw_jsonb.exists_any_keys(keys.into_iter()).unwrap()); // Stops at invalid UTF-8 and returns false + /// + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_jsonb = arr_jsonb.as_raw(); + /// let keys = ["a".as_bytes()]; + /// assert!(!raw_jsonb.exists_any_keys(keys.into_iter()).unwrap()); // Not an object + /// + /// let obj_jsonb = r#"{"a b": 1, "c": 2}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// let keys = ["a b".as_bytes()]; + /// assert!(raw_jsonb.exists_any_keys(keys.into_iter()).unwrap()); + /// ``` + pub fn exists_any_keys<'a, I: Iterator>( + &self, + keys: I, + ) -> Result { + let value = self.data; + let header = read_u32(value, 0)?; + + for key in keys { + if let Ok(key) = from_utf8(key) { + if Self::exists_key(value, header, key)? { + return Ok(true); + } + } + } + Ok(false) + } + + fn exists_key(value: &[u8], header: u32, key: &str) -> Result { + match header & CONTAINER_HEADER_TYPE_MASK { + OBJECT_CONTAINER_TAG => { + let mut matches = false; + for obj_key in iteate_object_keys(value, header) { + if obj_key.eq(key) { + matches = true; + break; + } + } + Ok(matches) + } + ARRAY_CONTAINER_TAG => { + let mut matches = false; + for (jentry, val) in iterate_array(value, header) { + if jentry.type_code != STRING_TAG { + continue; + } + let val = unsafe { from_utf8_unchecked(val) }; + if val.eq(key) { + matches = true; + break; + } + } + Ok(matches) + } + SCALAR_CONTAINER_TAG => Ok(false), + _ => Err(Error::InvalidJsonb), + } + } +} diff --git a/src/functions/other.rs b/src/functions/other.rs new file mode 100644 index 0000000..968fce8 --- /dev/null +++ b/src/functions/other.rs @@ -0,0 +1,792 @@ +// Copyright 2023 Datafuse Labs. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains functions that don't neatly fit into other categories. + +use std::collections::VecDeque; + +use crate::builder::ArrayBuilder; +use crate::builder::ObjectBuilder; +use crate::constants::*; +use crate::error::*; +use crate::functions::core::jentry_compare_level; +use crate::functions::core::read_u32; +use crate::functions::path::get_jentry_by_name; +use crate::iterator::iterate_array; +use crate::iterator::iterate_object_entries; +use crate::jentry::JEntry; +use crate::number::Number; + +use crate::OwnedJsonb; +use crate::RawJsonb; + +impl RawJsonb<'_> { + /// Determines the type of a JSONB value. + /// + /// This function returns a string representation of the JSONB value's type. + /// The possible return values are: + /// * `"null"` + /// * `"boolean"` + /// * `"number"` + /// * `"string"` + /// * `"array"` + /// * `"object"` + /// + /// # Returns + /// + /// * `Ok(&'static str)` - A string slice representing the type of the JSONB value. + /// * `Err(Error)` - If an error occurred during decoding (e.g., invalid JSONB data). + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // Type checking + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_jsonb = arr_jsonb.as_raw(); + /// assert_eq!(raw_jsonb.type_of().unwrap(), "array"); + /// + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// assert_eq!(raw_jsonb.type_of().unwrap(), "object"); + /// + /// let num_jsonb = "1".parse::().unwrap(); + /// let raw_jsonb = num_jsonb.as_raw(); + /// assert_eq!(raw_jsonb.type_of().unwrap(), "number"); + /// + /// let string_jsonb = r#""hello""#.parse::().unwrap(); + /// let raw_jsonb = string_jsonb.as_raw(); + /// assert_eq!(raw_jsonb.type_of().unwrap(), "string"); + /// + /// let bool_jsonb = "true".parse::().unwrap(); + /// let raw_jsonb = bool_jsonb.as_raw(); + /// assert_eq!(raw_jsonb.type_of().unwrap(), "boolean"); + /// + /// let null_jsonb = "null".parse::().unwrap(); + /// let raw_jsonb = null_jsonb.as_raw(); + /// assert_eq!(raw_jsonb.type_of().unwrap(), "null"); + /// ``` + pub fn type_of(&self) -> Result<&'static str, Error> { + let value = self.data; + let header = read_u32(value, 0)?; + + match header & CONTAINER_HEADER_TYPE_MASK { + SCALAR_CONTAINER_TAG => { + let encoded = read_u32(value, 4)?; + let jentry = JEntry::decode_jentry(encoded); + match jentry.type_code { + NULL_TAG => Ok(TYPE_NULL), + TRUE_TAG | FALSE_TAG => Ok(TYPE_BOOLEAN), + NUMBER_TAG => Ok(TYPE_NUMBER), + STRING_TAG => Ok(TYPE_STRING), + _ => Err(Error::InvalidJsonbJEntry), + } + } + ARRAY_CONTAINER_TAG => Ok(TYPE_ARRAY), + OBJECT_CONTAINER_TAG => Ok(TYPE_OBJECT), + _ => Err(Error::InvalidJsonbHeader), + } + } + + /// Checks if the JSONB value contains other JSONB value. + /// + /// Containment is defined as follows: + /// + /// * **Scalar values:** Two scalar values are considered equal if their byte representations are identical. + /// * **Objects:** The self object contains the other object if all key-value pairs in the other object + /// exist in the self object with the same values. The self object may have additional key-value pairs. + /// * **Arrays:** The self array contains the other array if, for every element in the other array, there exists + /// an identical element in the self array. The self array may have additional elements. Note that order does + /// **not** matter for containment, and duplicate elements are handled correctly. Nested arrays are also supported. + /// + /// # Arguments + /// + /// * `self` - The self JSONB value. + /// * `other` - The other JSONB value. + /// + /// # Returns + /// + /// * `Ok(true)` if the self JSONB value contains the other JSONB value. + /// * `Ok(false)` if the self JSONB value does not contain the other JSONB value. + /// * `Err(Error)` if an error occurred during decoding. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // Example 1: Array containment + /// let left_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let left_raw = left_jsonb.as_raw(); + /// let right_jsonb = "[3, 1]".parse::().unwrap(); + /// let right_raw = right_jsonb.as_raw(); + /// assert!(left_raw.contains(&right_raw).unwrap()); + /// + /// // Example 2: Object containment with nested structures + /// let left_jsonb = r#"{"a": 1, "b": {"c": 2, "d": 3}}"#.parse::().unwrap(); + /// let left_raw = left_jsonb.as_raw(); + /// let right_jsonb = r#"{"b": {"c": 2}}"#.parse::().unwrap(); + /// let right_raw = right_jsonb.as_raw(); + /// assert!(left_raw.contains(&right_raw).unwrap()); + /// ``` + pub fn contains(&self, other: &RawJsonb) -> Result { + let left = self.data; + let right = other.data; + Self::containter_contains(left, right) + } + + fn containter_contains(left: &[u8], right: &[u8]) -> Result { + let l_header = read_u32(left, 0)?; + let r_header = read_u32(right, 0)?; + + let l_type = l_header & CONTAINER_HEADER_TYPE_MASK; + let r_type = r_header & CONTAINER_HEADER_TYPE_MASK; + + // special case for the left array and the right scalar + if l_type == ARRAY_CONTAINER_TAG && r_type == SCALAR_CONTAINER_TAG { + let r_jentry = JEntry::decode_jentry(read_u32(right, 4)?); + return Ok(Self::array_contains(left, l_header, &right[8..], r_jentry)); + } + + if l_type != r_type { + return Ok(false); + } + + match r_type { + OBJECT_CONTAINER_TAG => { + let l_size = l_header & CONTAINER_HEADER_LEN_MASK; + let r_size = r_header & CONTAINER_HEADER_LEN_MASK; + if l_size < r_size { + return Ok(false); + } + for (r_key, r_jentry, r_val) in iterate_object_entries(right, r_header) { + match get_jentry_by_name(left, 0, l_header, r_key, false) { + Some((l_jentry, _, l_val_offset)) => { + if l_jentry.type_code != r_jentry.type_code { + return Ok(false); + } + let l_val = + &left[l_val_offset..l_val_offset + l_jentry.length as usize]; + if r_jentry.type_code != CONTAINER_TAG { + if !l_val.eq(r_val) { + return Ok(false); + } + } else if !Self::containter_contains(l_val, r_val)? { + return Ok(false); + } + } + None => return Ok(false), + } + } + Ok(true) + } + ARRAY_CONTAINER_TAG => { + for (r_jentry, r_val) in iterate_array(right, r_header) { + if r_jentry.type_code != CONTAINER_TAG { + if !Self::array_contains(left, l_header, r_val, r_jentry) { + return Ok(false); + } + } else { + let l_nested: Vec<_> = iterate_array(left, l_header) + .filter(|(l_jentry, _)| l_jentry.type_code == CONTAINER_TAG) + .map(|(_, l_val)| l_val) + .collect(); + + let mut contains_nested = false; + + for l_nested_val in l_nested { + if Self::containter_contains(l_nested_val, r_val)? { + contains_nested = true; + break; + } + } + if !contains_nested { + return Ok(false); + } + } + } + Ok(true) + } + _ => Ok(left.eq(right)), + } + } + + fn array_contains(arr: &[u8], arr_header: u32, val: &[u8], val_jentry: JEntry) -> bool { + for (jentry, arr_val) in iterate_array(arr, arr_header) { + if jentry.type_code != val_jentry.type_code { + continue; + } + if val.eq(arr_val) { + return true; + } + } + false + } + + /// Traverses the JSONB value and checks string values against a provided function. + /// + /// This function recursively traverses the JSONB value, visiting all string elements. + /// For each string element, it calls the provided `func`. If `func` returns `true` for any string element, + /// the traversal stops, and the function returns `Ok(true)`. If `func` returns `false` for all string elements, + /// the traversal completes, and the function returns `Ok(false)`. + /// + /// This function is useful for efficiently searching for a specific string within a potentially complex JSONB structure + /// without having to manually traverse the entire structure. + /// + /// # Arguments + /// + /// * `func` - A function that takes a byte slice (`&[u8]`) representing a string value and returns a boolean indicating whether the string satisfies a certain condition. + /// + /// # Returns + /// + /// * `Ok(true)` - If `func` returns `true` for any string element encountered during traversal. + /// * `Ok(false)` - If `func` returns `false` for all string elements. + /// * `Err(Error)` - If an error occurred during decoding (e.g., invalid JSONB data). + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// let jsonb_value = r#"{"a": "hello", "b": [1, "world", {"c": "rust"}]}"#.parse::().unwrap(); + /// let raw_jsonb = jsonb_value.as_raw(); + /// + /// // Check if any string contains "rust" + /// let contains_rust = raw_jsonb.traverse_check_string(|s| s.eq("rust".as_bytes())).unwrap(); + /// assert!(contains_rust); + /// + /// // Check if any string contains "xyz" + /// let contains_xyz = raw_jsonb.traverse_check_string(|s| s.eq("xyz".as_bytes())).unwrap(); + /// assert!(!contains_xyz); + /// + /// // Check if any string is longer than 5 characters + /// let long_string = raw_jsonb.traverse_check_string(|s| s.len() > 5).unwrap(); + /// assert!(!long_string); + /// + /// // Example with an array of strings + /// let jsonb_value = r#"["hello", "world", "rust"]"#.parse::().unwrap(); + /// let raw_jsonb = jsonb_value.as_raw(); + /// let contains_rust = raw_jsonb.traverse_check_string(|s| s.eq("rust".as_bytes())).unwrap(); + /// assert!(contains_rust); + /// ``` + pub fn traverse_check_string(&self, func: impl Fn(&[u8]) -> bool) -> Result { + let value = self.data; + let mut offsets = VecDeque::new(); + offsets.push_back(0); + + while let Some(offset) = offsets.pop_front() { + let header = read_u32(value, offset)?; + let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; + + let size = match header & CONTAINER_HEADER_TYPE_MASK { + SCALAR_CONTAINER_TAG => 1, + ARRAY_CONTAINER_TAG => length, + OBJECT_CONTAINER_TAG => length * 2, + _ => { + return Err(Error::InvalidJsonb); + } + }; + + let mut jentry_offset = offset + 4; + let mut val_offset = offset + 4 + 4 * size; + for _ in 0..size { + let encoded = read_u32(value, jentry_offset)?; + let jentry = JEntry::decode_jentry(encoded); + match jentry.type_code { + CONTAINER_TAG => { + offsets.push_back(val_offset); + } + STRING_TAG => { + let val_length = jentry.length as usize; + if func(&value[val_offset..val_offset + val_length]) { + return Ok(true); + } + } + _ => {} + } + jentry_offset += 4; + val_offset += jentry.length as usize; + } + } + Ok(false) + } + + /// Concatenates two JSONB values. + /// + /// This function concatenates the `self` JSONB value with the `other` JSONB value. + /// The concatenation behavior depends on the types of the input values: + /// + /// * **Object + Object:** Merges the two objects. If there are duplicate keys, the keys from the `other` object will overwrite the keys from the `self` object. + /// * **Array + Array:** Appends the elements of the `other` array to the `self` array. + /// * **Object/Scalar + Array:** Creates a new array containing the `self` value (as a single element) followed by the elements of the `other` array. + /// * **Array + Object/Scalar:** Creates a new array containing the elements of the `self` array followed by the `other` value (as a single element). + /// * **Object + Scalar:** Creates a new array containing the `self` object (as a single element) and the `other` scalar (as a single element). + /// * **Scalar + Object:** Creates a new array containing the `self` scalar (as a single element) and the `other` object (as a single element). + /// * **Scalar + Scalar:** Creates a new array containing both scalar values as elements. + /// + /// If the input JSONB values are invalid or if an unsupported concatenation is attempted (e.g. trying to concatenate a number with an object directly, without creating an array), an error is returned. + /// + /// # Arguments + /// + /// * `self` - The first JSONB value. + /// * `other` - The second JSONB value. + /// + /// # Returns + /// + /// * `Ok(OwnedJsonb)` - The concatenated JSONB value. + /// * `Err(Error)` - If the input JSONB values are invalid, or if an unsupported concatenation is attempted. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // Object + Object + /// let obj1 = r#"{"a": 1, "b": 2}"#.parse::().unwrap(); + /// let obj2 = r#"{"b": 3, "c": 4}"#.parse::().unwrap(); + /// let concatenated = obj1.as_raw().concat(&obj2.as_raw()).unwrap(); + /// assert_eq!(concatenated.to_string(), r#"{"a":1,"b":3,"c":4}"#); // "b" is overwritten + /// + /// // Array + Array + /// let arr1 = "[1, 2]".parse::().unwrap(); + /// let arr2 = "[3, 4]".parse::().unwrap(); + /// let concatenated = arr1.as_raw().concat(&arr2.as_raw()).unwrap(); + /// assert_eq!(concatenated.to_string(), "[1,2,3,4]"); + /// + /// // Object + Array + /// let obj = r#"{"a": 1}"#.parse::().unwrap(); + /// let arr = "[2, 3]".parse::().unwrap(); + /// let concatenated = obj.as_raw().concat(&arr.as_raw()).unwrap(); + /// assert_eq!(concatenated.to_string(), r#"[{"a":1},2,3]"#); + /// + /// // Scalar + Array + /// let scalar = "1".parse::().unwrap(); + /// let arr = "[2, 3]".parse::().unwrap(); + /// let concatenated = scalar.as_raw().concat(&arr.as_raw()).unwrap(); + /// assert_eq!(concatenated.to_string(), "[1,2,3]"); + /// + /// // Scalar + Scalar + /// let scalar1 = "1".parse::().unwrap(); + /// let scalar2 = "2".parse::().unwrap(); + /// let concatenated = scalar1.as_raw().concat(&scalar2.as_raw()).unwrap(); + /// assert_eq!(concatenated.to_string(), "[1,2]"); + /// ``` + pub fn concat(&self, other: &RawJsonb) -> Result { + let mut buf = Vec::new(); + let left = self.data; + let right = other.data; + + let left_header = read_u32(left, 0)?; + let right_header = read_u32(right, 0)?; + + let left_len = (left_header & CONTAINER_HEADER_LEN_MASK) as usize; + let right_len = (right_header & CONTAINER_HEADER_LEN_MASK) as usize; + + let left_type = left_header & CONTAINER_HEADER_TYPE_MASK; + let right_type = right_header & CONTAINER_HEADER_TYPE_MASK; + + match (left_type, right_type) { + (OBJECT_CONTAINER_TAG, OBJECT_CONTAINER_TAG) => { + let mut builder = ObjectBuilder::new(); + for (key, jentry, item) in iterate_object_entries(left, left_header) { + builder.push_raw(key, jentry, item); + } + for (key, jentry, item) in iterate_object_entries(right, right_header) { + builder.push_raw(key, jentry, item); + } + builder.build_into(&mut buf); + } + (ARRAY_CONTAINER_TAG, ARRAY_CONTAINER_TAG) => { + let mut builder = ArrayBuilder::new(left_len + right_len); + for (jentry, item) in iterate_array(left, left_header) { + builder.push_raw(jentry, item); + } + for (jentry, item) in iterate_array(right, right_header) { + builder.push_raw(jentry, item); + } + builder.build_into(&mut buf); + } + (OBJECT_CONTAINER_TAG | SCALAR_CONTAINER_TAG, ARRAY_CONTAINER_TAG) => { + let mut builder = ArrayBuilder::new(right_len + 1); + match left_type { + OBJECT_CONTAINER_TAG => { + let jentry = JEntry::make_container_jentry(left.len()); + builder.push_raw(jentry, left); + } + _ => { + let jentry = JEntry::decode_jentry(read_u32(left, 4)?); + builder.push_raw(jentry, &left[8..]); + } + }; + for (jentry, item) in iterate_array(right, right_header) { + builder.push_raw(jentry, item); + } + builder.build_into(&mut buf); + } + (ARRAY_CONTAINER_TAG, OBJECT_CONTAINER_TAG | SCALAR_CONTAINER_TAG) => { + let mut builder = ArrayBuilder::new(left_len + 1); + for (jentry, item) in iterate_array(left, left_header) { + builder.push_raw(jentry, item); + } + match right_type { + OBJECT_CONTAINER_TAG => { + let jentry = JEntry::make_container_jentry(right.len()); + builder.push_raw(jentry, right); + } + _ => { + let jentry = JEntry::decode_jentry(read_u32(right, 4)?); + builder.push_raw(jentry, &right[8..]); + } + }; + builder.build_into(&mut buf); + } + (OBJECT_CONTAINER_TAG, SCALAR_CONTAINER_TAG) => { + let mut builder = ArrayBuilder::new(2); + let jentry = JEntry::make_container_jentry(left.len()); + builder.push_raw(jentry, left); + let jentry = JEntry::decode_jentry(read_u32(right, 4)?); + builder.push_raw(jentry, &right[8..]); + builder.build_into(&mut buf); + } + (SCALAR_CONTAINER_TAG, OBJECT_CONTAINER_TAG) => { + let mut builder = ArrayBuilder::new(2); + let jentry = JEntry::decode_jentry(read_u32(left, 4)?); + builder.push_raw(jentry, &left[8..]); + let jentry = JEntry::make_container_jentry(right.len()); + builder.push_raw(jentry, right); + builder.build_into(&mut buf); + } + (SCALAR_CONTAINER_TAG, SCALAR_CONTAINER_TAG) => { + let mut builder = ArrayBuilder::new(2); + let jentry = JEntry::decode_jentry(read_u32(left, 4)?); + builder.push_raw(jentry, &left[8..]); + let jentry = JEntry::decode_jentry(read_u32(right, 4)?); + builder.push_raw(jentry, &right[8..]); + builder.build_into(&mut buf); + } + (_, _) => { + return Err(Error::InvalidJsonb); + } + } + Ok(OwnedJsonb::new(buf)) + } + + /// Recursively reomves all object key-value pairs that have null values from a JSONB object. + /// + /// Note: null values in the JSONB array are not reomved. + /// + /// # Returns + /// + /// * `Ok(OwnedJsonb)` - The JSONB value with nulls removed. + /// * `Err(Error)` - If the input JSONB value is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // Object with nulls + /// let obj_with_nulls = r#"{"a": 1, "b": null, "c": 3}"#.parse::().unwrap(); + /// let stripped = obj_with_nulls.as_raw().strip_nulls().unwrap(); + /// assert_eq!(stripped.to_string(), r#"{"a":1,"c":3}"#); + /// + /// // Nested structures + /// let nested = r#"{"a": [1, null, {"b": null, "c": 2}]}"#.parse::().unwrap(); + /// let stripped = nested.as_raw().strip_nulls().unwrap(); + /// assert_eq!(stripped.to_string(), r#"{"a":[1,null,{"c":2}]}"#); + /// + /// // Array with nulls + /// let arr_with_nulls = r#"[1, null, 3]"#.parse::().unwrap(); + /// let stripped = arr_with_nulls.as_raw().strip_nulls().unwrap(); + /// assert_eq!(stripped.to_string(), r#"[1,null,3]"#); // Remains unchanged + /// + /// // Scalar null + /// let null_scalar = "null".parse::().unwrap(); + /// let stripped = null_scalar.as_raw().strip_nulls().unwrap(); + /// assert_eq!(stripped.to_string(), "null"); // Remains unchanged + /// ``` + pub fn strip_nulls(&self) -> Result { + let mut buf = Vec::new(); + let value = self.data; + let header = read_u32(value, 0)?; + + match header & CONTAINER_HEADER_TYPE_MASK { + OBJECT_CONTAINER_TAG => { + let builder = Self::strip_nulls_object(header, value)?; + builder.build_into(&mut buf); + } + ARRAY_CONTAINER_TAG => { + let builder = Self::strip_nulls_array(header, value)?; + builder.build_into(&mut buf); + } + SCALAR_CONTAINER_TAG => buf.extend_from_slice(value), + _ => { + return Err(Error::InvalidJsonb); + } + } + Ok(OwnedJsonb::new(buf)) + } + + fn strip_nulls_array(header: u32, value: &[u8]) -> Result, Error> { + let len = (header & CONTAINER_HEADER_LEN_MASK) as usize; + let mut builder = ArrayBuilder::new(len); + + for (jentry, item) in iterate_array(value, header) { + match jentry.type_code { + CONTAINER_TAG => { + let item_header = read_u32(item, 0)?; + match item_header & CONTAINER_HEADER_TYPE_MASK { + OBJECT_CONTAINER_TAG => { + builder.push_object(Self::strip_nulls_object(item_header, item)?); + } + ARRAY_CONTAINER_TAG => { + builder.push_array(Self::strip_nulls_array(item_header, item)?); + } + _ => { + return Err(Error::InvalidJsonb); + } + } + } + NULL_TAG | STRING_TAG | NUMBER_TAG | FALSE_TAG | TRUE_TAG => { + builder.push_raw(jentry, item) + } + _ => { + return Err(Error::InvalidJsonb); + } + } + } + Ok(builder) + } + + fn strip_nulls_object(header: u32, value: &[u8]) -> Result, Error> { + let mut builder = ObjectBuilder::new(); + for (key, jentry, item) in iterate_object_entries(value, header) { + match jentry.type_code { + CONTAINER_TAG => { + let item_header = read_u32(item, 0)?; + match item_header & CONTAINER_HEADER_TYPE_MASK { + OBJECT_CONTAINER_TAG => { + builder.push_object(key, Self::strip_nulls_object(item_header, item)?); + } + ARRAY_CONTAINER_TAG => { + builder.push_array(key, Self::strip_nulls_array(item_header, item)?); + } + _ => { + return Err(Error::InvalidJsonb); + } + } + } + NULL_TAG => continue, + STRING_TAG | NUMBER_TAG | FALSE_TAG | TRUE_TAG => { + builder.push_raw(key, jentry, item) + } + _ => { + return Err(Error::InvalidJsonb); + } + } + } + Ok(builder) + } + + /// Converts a RawJsonb value into a comparable binary representation. + /// + /// This function transforms the JSONB value into a new binary format designed for efficient comparison. + /// The resulting binary data can be directly compared using byte-wise operations to determine the relative order of two JSONB values. + /// The original JSONB data structure is flattened into a linear representation, and different data types are encoded in a way that enables direct comparison. + /// The compare rules are the same as the `PartialOrd` trait. + /// Scalar Null > Array > Object > Other Scalars(String > Number > Boolean). + /// + /// **Important:** The resulting byte array is *not* a valid JSONB format; it's specifically designed for comparison purposes and should not be interpreted as standard JSONB data. + /// + /// # Arguments + /// + /// * `self` - The RawJsonb value to convert. + /// + /// # Returns + /// + /// A `Vec` representing the comparable binary form of the JSONB data. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// let json1 = r#"{"a":1,"b":"hello"}"#.parse::().unwrap(); + /// let json2 = r#"{"a":1,"b":"world"}"#.parse::().unwrap(); + /// let json3 = r#"{"a":2,"b":"hello"}"#.parse::().unwrap(); + /// let json4 = r#"{"a":1,"b":[1,2,3]}"#.parse::().unwrap(); + /// let json5 = r#"{"a":1,"b":[1,2]}"#.parse::().unwrap(); + /// let json6 = "[1,2,3]".parse::().unwrap(); + /// let json7 = "[1,2]".parse::().unwrap(); + /// let json8 = "1".parse::().unwrap(); + /// let json9 = "2".parse::().unwrap(); + /// + /// let comparable1 = json1.as_raw().convert_to_comparable(); + /// let comparable2 = json2.as_raw().convert_to_comparable(); + /// let comparable3 = json3.as_raw().convert_to_comparable(); + /// let comparable4 = json4.as_raw().convert_to_comparable(); + /// let comparable5 = json5.as_raw().convert_to_comparable(); + /// let comparable6 = json6.as_raw().convert_to_comparable(); + /// let comparable7 = json7.as_raw().convert_to_comparable(); + /// let comparable8 = json8.as_raw().convert_to_comparable(); + /// let comparable9 = json9.as_raw().convert_to_comparable(); + /// + /// assert!(comparable1 < comparable2); + /// assert!(comparable1 < comparable3); + /// assert!(comparable1 < comparable4); + /// assert!(comparable4 > comparable5); + /// assert!(comparable6 > comparable7); + /// assert!(comparable8 < comparable9); + /// ``` + pub fn convert_to_comparable(&self) -> Vec { + let value = self.data; + let mut buf = Vec::new(); + let depth = 0; + let header = read_u32(value, 0).unwrap_or_default(); + match header & CONTAINER_HEADER_TYPE_MASK { + SCALAR_CONTAINER_TAG => { + let encoded = match read_u32(value, 4) { + Ok(encoded) => encoded, + Err(_) => { + return buf; + } + }; + let jentry = JEntry::decode_jentry(encoded); + Self::scalar_convert_to_comparable(depth, &jentry, &value[8..], &mut buf); + } + ARRAY_CONTAINER_TAG => { + buf.push(depth); + buf.push(ARRAY_LEVEL); + let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; + Self::array_convert_to_comparable(depth + 1, length, &value[4..], &mut buf); + } + OBJECT_CONTAINER_TAG => { + buf.push(depth); + buf.push(OBJECT_LEVEL); + let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; + Self::object_convert_to_comparable(depth + 1, length, &value[4..], &mut buf); + } + _ => {} + } + buf + } + + fn scalar_convert_to_comparable(depth: u8, jentry: &JEntry, value: &[u8], buf: &mut Vec) { + buf.push(depth); + let level = jentry_compare_level(jentry); + match jentry.type_code { + CONTAINER_TAG => { + let header = match read_u32(value, 0) { + Ok(header) => header, + Err(_) => { + return; + } + }; + let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; + match header & CONTAINER_HEADER_TYPE_MASK { + ARRAY_CONTAINER_TAG => { + buf.push(ARRAY_LEVEL); + Self::array_convert_to_comparable(depth + 1, length, &value[4..], buf); + } + OBJECT_CONTAINER_TAG => { + buf.push(OBJECT_LEVEL); + Self::object_convert_to_comparable(depth + 1, length, &value[4..], buf); + } + _ => {} + } + } + _ => { + buf.push(level); + match jentry.type_code { + STRING_TAG => { + let length = jentry.length as usize; + buf.extend_from_slice(&value[..length]); + } + NUMBER_TAG => { + let length = jentry.length as usize; + if let Ok(num) = Number::decode(&value[..length]) { + let n = num.as_f64().unwrap(); + // https://github.com/rust-lang/rust/blob/9c20b2a8cc7588decb6de25ac6a7912dcef24d65/library/core/src/num/f32.rs#L1176-L1260 + let s = n.to_bits() as i64; + let v = s ^ (((s >> 63) as u64) >> 1) as i64; + let mut b = v.to_be_bytes(); + // Toggle top "sign" bit to ensure consistent sort order + b[0] ^= 0x80; + buf.extend_from_slice(&b); + } + } + _ => {} + } + } + } + } + + fn array_convert_to_comparable(depth: u8, length: usize, value: &[u8], buf: &mut Vec) { + let mut jentry_offset = 0; + let mut val_offset = 4 * length; + for _ in 0..length { + let encoded = match read_u32(value, jentry_offset) { + Ok(encoded) => encoded, + Err(_) => { + return; + } + }; + let jentry = JEntry::decode_jentry(encoded); + Self::scalar_convert_to_comparable(depth, &jentry, &value[val_offset..], buf); + jentry_offset += 4; + val_offset += jentry.length as usize; + } + } + + fn object_convert_to_comparable(depth: u8, length: usize, value: &[u8], buf: &mut Vec) { + let mut jentry_offset = 0; + let mut val_offset = 8 * length; + + // read all key jentries first + let mut key_jentries: VecDeque = VecDeque::with_capacity(length); + for _ in 0..length { + let encoded = match read_u32(value, jentry_offset) { + Ok(encoded) => encoded, + Err(_) => { + return; + } + }; + let key_jentry = JEntry::decode_jentry(encoded); + + jentry_offset += 4; + val_offset += key_jentry.length as usize; + key_jentries.push_back(key_jentry); + } + + let mut key_offset = 8 * length; + for _ in 0..length { + let key_jentry = key_jentries.pop_front().unwrap(); + Self::scalar_convert_to_comparable(depth, &key_jentry, &value[key_offset..], buf); + + let encoded = match read_u32(value, jentry_offset) { + Ok(encoded) => encoded, + Err(_) => { + return; + } + }; + let val_jentry = JEntry::decode_jentry(encoded); + Self::scalar_convert_to_comparable(depth, &val_jentry, &value[val_offset..], buf); + + jentry_offset += 4; + key_offset += key_jentry.length as usize; + val_offset += val_jentry.length as usize; + } + } +} diff --git a/src/functions/path.rs b/src/functions/path.rs new file mode 100644 index 0000000..55b7558 --- /dev/null +++ b/src/functions/path.rs @@ -0,0 +1,941 @@ +// Copyright 2023 Datafuse Labs. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains functions that dealing with path-based access to JSONB data. + +use std::collections::VecDeque; +use std::str::from_utf8_unchecked; + +use crate::builder::ArrayBuilder; +use crate::builder::ObjectBuilder; +use crate::constants::*; +use crate::error::*; +use crate::functions::core::extract_by_jentry; +use crate::functions::core::read_u32; +use crate::iterator::iterate_array; +use crate::iterator::iterate_object_entries; +use crate::jentry::JEntry; +use crate::jsonpath::JsonPath; +use crate::jsonpath::Mode; +use crate::jsonpath::Selector; +use crate::keypath::KeyPath; + +use crate::OwnedJsonb; +use crate::RawJsonb; + +impl RawJsonb<'_> { + /// Gets the element at the specified index in a JSONB array. + /// + /// If the JSONB value is an array, this function returns the element at the given `index` as an `OwnedJsonb`. + /// If the `index` is out of bounds, it returns `Ok(None)`. + /// If the JSONB value is not an array (e.g., it's an object or a scalar), this function also returns `Ok(None)`. + /// + /// # Arguments + /// + /// * `index` - The index of the desired element. + /// + /// # Returns + /// + /// * `Ok(Some(OwnedJsonb))` - The element at the specified index as an `OwnedJsonb` if the input is an array and the index is valid. + /// * `Ok(None)` - If the input is not an array, or if the index is out of bounds. + /// * `Err(Error)` - If an error occurred during decoding (e.g., invalid JSONB data). + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// let arr_jsonb = r#"[1, "hello", {"a": 1}]"#.parse::().unwrap(); + /// let raw_jsonb = arr_jsonb.as_raw(); + /// + /// let element0 = raw_jsonb.get_by_index(0).unwrap(); + /// assert_eq!(element0.unwrap().to_string(), "1"); + /// + /// let element1 = raw_jsonb.get_by_index(1).unwrap(); + /// assert_eq!(element1.unwrap().to_string(), r#""hello""#); + /// + /// let element2 = raw_jsonb.get_by_index(2).unwrap(); + /// assert_eq!(element2.unwrap().to_string(), r#"{"a":1}"#); + /// + /// let element3 = raw_jsonb.get_by_index(3).unwrap(); + /// assert!(element3.is_none()); // Index out of bounds + /// + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// let element = raw_jsonb.get_by_index(0).unwrap(); + /// assert!(element.is_none()); // Not an array + /// ``` + pub fn get_by_index(&self, index: usize) -> Result, Error> { + let value = self.data; + let header = read_u32(value, 0)?; + match header & CONTAINER_HEADER_TYPE_MASK { + ARRAY_CONTAINER_TAG => { + let val = get_jentry_by_index(value, 0, header, index) + .map(|(jentry, encoded, val_offset)| { + extract_by_jentry(&jentry, encoded, val_offset, value) + }) + .map(OwnedJsonb::new); + Ok(val) + } + SCALAR_CONTAINER_TAG | OBJECT_CONTAINER_TAG => Ok(None), + _ => Err(Error::InvalidJsonb), + } + } + + /// Gets the value associated with a given key in a JSONB object. + /// + /// If the JSONB value is an object, this function searches for a key matching the provided `name` + /// and returns the associated value as an `OwnedJsonb`. + /// The `ignore_case` parameter controls whether the key search is case-sensitive. + /// If the key is not found, it returns `Ok(None)`. + /// If the JSONB value is not an object (e.g., it's an array or a scalar), this function also returns `Ok(None)`. + /// + /// # Arguments + /// + /// * `name` - The key to search for. + /// * `ignore_case` - Whether the key search should be case-insensitive. + /// + /// # Returns + /// + /// * `Ok(Some(OwnedJsonb))` - The value associated with the key as an `OwnedJsonb`, if the input is an object and the key is found. + /// * `Ok(None)` - If the input is not an object, or if the key is not found. + /// * `Err(Error)` - If an error occurred during decoding (e.g., invalid JSONB data). + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// let obj_jsonb = r#"{"a": 1, "b": "hello", "c": [1, 2]}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// + /// let value_a = raw_jsonb.get_by_name("a", false).unwrap(); + /// assert_eq!(value_a.unwrap().to_string(), "1"); + /// + /// let value_b = raw_jsonb.get_by_name("b", false).unwrap(); + /// assert_eq!(value_b.unwrap().to_string(), r#""hello""#); + /// + /// let value_c = raw_jsonb.get_by_name("c", false).unwrap(); + /// assert_eq!(value_c.unwrap().to_string(), "[1,2]"); + /// + /// let value_d = raw_jsonb.get_by_name("d", false).unwrap(); + /// assert!(value_d.is_none()); // Key not found + /// + /// // Case-insensitive search + /// let value_a_case_insensitive = raw_jsonb.get_by_name("A", true).unwrap(); + /// assert_eq!(value_a_case_insensitive.unwrap().to_string(), "1"); + /// + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_jsonb = arr_jsonb.as_raw(); + /// let value = raw_jsonb.get_by_name("a", false).unwrap(); + /// assert!(value.is_none()); // Not an object + /// ``` + pub fn get_by_name(&self, name: &str, ignore_case: bool) -> Result, Error> { + let value = self.data; + let header = read_u32(value, 0)?; + match header & CONTAINER_HEADER_TYPE_MASK { + OBJECT_CONTAINER_TAG => { + let val = get_jentry_by_name(value, 0, header, name, ignore_case) + .map(|(jentry, encoded, val_offset)| { + extract_by_jentry(&jentry, encoded, val_offset, value) + }) + .map(OwnedJsonb::new); + Ok(val) + } + SCALAR_CONTAINER_TAG | ARRAY_CONTAINER_TAG => Ok(None), + _ => Err(Error::InvalidJsonb), + } + } + + /// Gets the value at the specified key path in a JSONB value. + /// + /// This function traverses the JSONB value according to the provided key path + /// and returns the value at the final path element as an `OwnedJsonb`. + /// The key path is an iterator of `KeyPath` elements, which can be + /// either named keys (for objects) or array indices. + /// + /// If any element in the key path does not exist or if the type of the current value + /// does not match the key path element (e.g., trying to access a named key in an array), + /// the function returns `Ok(None)`. + /// If the key path is empty, the function returns the original `RawJsonb` value wrapped in `Some`. + /// + /// # Arguments + /// + /// * `keypaths` - An iterator of `KeyPath` elements representing the path to traverse. + /// + /// # Returns + /// + /// * `Ok(Some(OwnedJsonb))` - The value at the specified key path as an `OwnedJsonb`, if found. + /// * `Ok(None)` - If the key path is invalid or leads to a non-existent value. + /// * `Err(Error)` - If an error occurred during decoding (e.g., invalid JSONB data). + /// + /// # Examples + /// + /// ```rust + /// use std::borrow::Cow; + /// use jsonb::{keypath::KeyPath, OwnedJsonb, RawJsonb}; + /// + /// let jsonb_value = r#"{"a": {"b": [1, 2, 3], "c": "hello"}, "d": [4, 5]}"#.parse::().unwrap(); + /// let raw_jsonb = jsonb_value.as_raw(); + /// + /// // Accessing nested values + /// let path = [KeyPath::Name(Cow::Borrowed("a")), KeyPath::Name(Cow::Borrowed("b")), KeyPath::Index(1)]; + /// let value = raw_jsonb.get_by_keypath(path.iter()).unwrap(); + /// assert_eq!(value.unwrap().to_string(), "2"); + /// + /// let path = [KeyPath::Name(Cow::Borrowed("a")), KeyPath::Name(Cow::Borrowed("c"))]; + /// let value = raw_jsonb.get_by_keypath(path.iter()).unwrap(); + /// assert_eq!(value.unwrap().to_string(), r#""hello""#); + /// + /// let path = [KeyPath::Name(Cow::Borrowed("d")), KeyPath::Index(0)]; + /// let value = raw_jsonb.get_by_keypath(path.iter()).unwrap(); + /// assert_eq!(value.unwrap().to_string(), "4"); + /// + /// // Invalid key path + /// let path = [KeyPath::Name(Cow::Borrowed("a")), KeyPath::Name(Cow::Borrowed("x"))]; // "x" doesn't exist + /// let value = raw_jsonb.get_by_keypath(path.iter()).unwrap(); + /// assert!(value.is_none()); + /// + /// let path = [KeyPath::Name(Cow::Borrowed("a")), KeyPath::Index(0)]; // "a" is an object, not an array + /// let value = raw_jsonb.get_by_keypath(path.iter()).unwrap(); + /// assert!(value.is_none()); + /// + /// // Empty key path - returns the original value + /// let value = raw_jsonb.get_by_keypath([].iter()).unwrap(); + /// assert_eq!(value.unwrap().to_string(), r#"{"a":{"b":[1,2,3],"c":"hello"},"d":[4,5]}"#); + /// + /// // KeyPath with quoted name + /// let jsonb_value = r#"{"a b": 1}"#.parse::().unwrap(); + /// let raw_jsonb = jsonb_value.as_raw(); + /// let path = [KeyPath::QuotedName(Cow::Borrowed("a b"))]; + /// let value = raw_jsonb.get_by_keypath(path.iter()).unwrap(); + /// assert_eq!(value.unwrap().to_string(), r#"1"#); + /// ``` + pub fn get_by_keypath<'a, I: Iterator>>( + &self, + keypaths: I, + ) -> Result, Error> { + let value = self.data; + + let mut curr_val_offset = 0; + let mut curr_jentry_encoded = 0; + let mut curr_jentry: Option = None; + + for path in keypaths { + if let Some(ref jentry) = curr_jentry { + if jentry.type_code != CONTAINER_TAG { + return Ok(None); + } + } + let header = read_u32(value, curr_val_offset)?; + let length = (header & CONTAINER_HEADER_LEN_MASK) as i32; + match (path, header & CONTAINER_HEADER_TYPE_MASK) { + (KeyPath::QuotedName(name) | KeyPath::Name(name), OBJECT_CONTAINER_TAG) => { + match get_jentry_by_name(value, curr_val_offset, header, name, false) { + Some((jentry, encoded, value_offset)) => { + curr_jentry_encoded = encoded; + curr_jentry = Some(jentry); + curr_val_offset = value_offset; + } + None => return Ok(None), + }; + } + (KeyPath::Index(idx), ARRAY_CONTAINER_TAG) => { + if *idx > length || length + *idx < 0 { + return Ok(None); + } else { + let idx = if *idx >= 0 { + *idx as usize + } else { + (length + *idx) as usize + }; + match get_jentry_by_index(value, curr_val_offset, header, idx) { + Some((jentry, encoded, value_offset)) => { + curr_jentry_encoded = encoded; + curr_jentry = Some(jentry); + curr_val_offset = value_offset; + } + None => return Ok(None), + } + } + } + (_, _) => return Ok(None), + } + } + // If the key paths is empty, return original value. + if curr_val_offset == 0 { + return Ok(Some(OwnedJsonb::new(value.to_vec()))); + } + let val = curr_jentry.map(|jentry| { + let val_data = extract_by_jentry(&jentry, curr_jentry_encoded, curr_val_offset, value); + OwnedJsonb::new(val_data) + }); + Ok(val) + } + + /// Extracts values from a JSONB structure using a JSONPath expression. Handles multiple matches as a Vec. + /// + /// This function uses the `jsonpath` crate to select values from the JSONB data based on the provided JSONPath expression. + /// This function always returns a `Vec`, even for a single match. This allows for consistent handling of multiple matches. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// * `json_path` - The JSONPath expression (from the `jsonpath` crate). + /// * `mode` - The selection mode (`Mode::First`, `Mode::All`, `Mode::Mixed`, or `Mode::Array`) from the `jsonpath` crate. + /// + /// # Returns + /// + /// * `Ok(Vec)` - A vector containing the selected values. If the path does not match any values, an empty vector `Vec::new()` is returned. + /// * `Err(Error)` - If the JSONB data is invalid or if an error occurs during path evaluation (e.g., an invalid JSONPath expression). + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// use jsonb::jsonpath::{parse_json_path, Mode}; + /// + /// let jsonb_value = r#"{"a": {"b": [1, 2, 3]}, "c": 4}"#.parse::().unwrap(); + /// let raw_jsonb = jsonb_value.as_raw(); + /// + /// // Mode::All (return all matches as a vector) + /// let path = parse_json_path("$.a.b[*]".as_bytes()).unwrap(); + /// let result = raw_jsonb.get_by_path(&path, Mode::All).unwrap(); + /// assert_eq!(result.len(), 3); + /// assert_eq!(result[0].to_string(), "1"); + /// assert_eq!(result[1].to_string(), "2"); + /// assert_eq!(result[2].to_string(), "3"); + /// + /// // Mode::First (return first value as a vector) + /// let result = raw_jsonb.get_by_path(&path, Mode::First).unwrap(); + /// assert_eq!(result.len(), 1); + /// assert_eq!(result[0].to_string(), "1"); + /// + /// // Mode::Array (return a JSONB array as a vector) + /// let result = raw_jsonb.get_by_path(&path, Mode::Array).unwrap(); + /// assert_eq!(result.len(), 1); + /// assert_eq!(result[0].to_string(), "[1,2,3]"); + /// + /// // Mode::Mixed (return a JSONB value as a vector) + /// let result = raw_jsonb.get_by_path(&path, Mode::Mixed).unwrap(); + /// assert_eq!(result.len(), 1); + /// assert_eq!(result[0].to_string(), "[1,2,3]"); + /// + /// // Path that does not exist + /// let path = parse_json_path("$.x".as_bytes()).unwrap(); + /// let result = raw_jsonb.get_by_path(&path, Mode::All).unwrap(); + /// assert!(result.is_empty()); + /// ``` + pub fn get_by_path<'a>( + &self, + json_path: &'a JsonPath<'a>, + mode: Mode, + ) -> Result, Error> { + let selector = Selector::new(json_path, mode); + selector.select(*self) + } + + /// Extracts a value from a JSONB structure using a JSONPath expression. Handles single or first match. + /// + /// This function uses the `jsonpath` crate to select a value from the JSONB data based on the provided JSONPath expression. + /// This function returns an `Option`, suitable for cases where you expect a single or first match. + /// Note: if the mode is `Mode::All`, behavior is same as `Mode::Mixed`. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// * `json_path` - The JSONPath expression (from the `jsonpath` crate). + /// * `mode` - The selection mode (`Mode::First`, `Mode::Mixed`) from the `jsonpath` crate. `Mode::All` and `Mode::Array` are not supported by this function. + /// + /// # Returns + /// + /// * `Ok(Some(OwnedJsonb))` - If the path matches at least one value, the first matched value. + /// * `Ok(None)` - If the path does not match any values. + /// * `Err(Error)` - If the JSONB data is invalid or if an error occurs during path evaluation (e.g., an invalid JSONPath expression). + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// use jsonb::jsonpath::{parse_json_path, Mode}; + /// + /// let jsonb_value = r#"{"a": {"b": [1, 2, 3]}, "c": 4}"#.parse::().unwrap(); + /// let raw_jsonb = jsonb_value.as_raw(); + /// let path = parse_json_path("$.a.b[*]".as_bytes()).unwrap(); + /// + /// // Mode::First (return the first value) + /// let result = raw_jsonb.get_by_path_opt(&path, Mode::First).unwrap(); + /// assert!(result.is_some()); + /// assert_eq!(result.unwrap().to_string(), "1"); + /// + /// // Mode::Array (return a JSONB array) + /// let result = raw_jsonb.get_by_path_opt(&path, Mode::Array).unwrap(); + /// assert!(result.is_some()); + /// assert_eq!(result.unwrap().to_string(), "[1,2,3]"); + /// + /// // Mode::Mixed (return a JSONB value) + /// let result = raw_jsonb.get_by_path_opt(&path, Mode::Mixed).unwrap(); + /// assert!(result.is_some()); + /// assert_eq!(result.unwrap().to_string(), "[1,2,3]"); + /// + /// // Path that does not exist + /// let path = parse_json_path("$.x".as_bytes()).unwrap(); + /// let result = raw_jsonb.get_by_path_opt(&path, Mode::First).unwrap(); + /// assert!(result.is_none()); + /// ``` + pub fn get_by_path_opt<'a>( + &self, + json_path: &'a JsonPath<'a>, + mode: Mode, + ) -> Result, Error> { + let selector = if mode == Mode::All { + Selector::new(json_path, Mode::Mixed) + } else { + Selector::new(json_path, mode) + }; + let mut owned_jsonbs = selector.select(*self)?; + if owned_jsonbs.is_empty() { + Ok(None) + } else { + Ok(Some(owned_jsonbs.remove(0))) + } + } + + /// Checks if a JSON path exists within the JSONB value. + /// + /// This function uses the `jsonpath` crate to check if a given JSON path exists within the JSONB value. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// * `json_path` - The JSON path to check (from the `jsonpath` crate). + /// + /// # Returns + /// + /// * `Ok(true)` - If the JSON path exists. + /// * `Ok(false)` - If the JSON path does not exist. + /// * `Err(Error)` - If the JSONB data is invalid or if an error occurs during path evaluation. This could also indicate issues with the `json_path` itself. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// use jsonb::jsonpath::{parse_json_path, Mode}; + /// + /// let jsonb_value = r#"{"a": {"b": [1, 2, 3]}, "c": 4}"#.parse::().unwrap(); + /// let raw_jsonb = jsonb_value.as_raw(); + /// + /// // Valid paths + /// let path1 = parse_json_path("$.a.b[1]".as_bytes()).unwrap(); + /// assert!(raw_jsonb.path_exists(&path1).unwrap()); + /// + /// let path2 = parse_json_path("$.c".as_bytes()).unwrap(); + /// assert!(raw_jsonb.path_exists(&path2).unwrap()); + /// + /// // Invalid paths + /// let path3 = parse_json_path("$.a.x".as_bytes()).unwrap(); // "x" does not exist + /// assert!(!raw_jsonb.path_exists(&path3).unwrap()); + /// ``` + pub fn path_exists<'a>(&self, json_path: &'a JsonPath<'a>) -> Result { + let selector = Selector::new(json_path, Mode::Mixed); + selector.exists(*self) + } + + /// Checks if a JSON path matches the JSONB value using a predicate. + /// + /// This function uses the `jsonpath` crate to check if a given JSON path, along with an associated predicate, matches the JSONB value. + /// The predicate determines the conditions that the selected value(s) must satisfy for the match to be considered successful. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// * `json_path` - The JSON path with a predicate (from the `jsonpath` crate). The predicate is specified within the `json_path` using the standard JSONPath syntax. For example, `$.store.book[?(@.price < 10)]` selects books with a price less than 10. + /// + /// # Returns + /// + /// * `Ok(true)` - If the JSON path with its predicate matches at least one value in the JSONB data. + /// * `Ok(false)` - If the JSON path with its predicate does not match any values. + /// * `Err(Error)` - If the JSONB data is invalid or if an error occurs during path evaluation or predicate checking. This could also indicate issues with the `json_path` itself (invalid syntax, etc.). + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// use jsonb::jsonpath::{parse_json_path, Mode}; + /// + /// let jsonb_value = r#"[ + /// {"price": 12, "title": "Book A"}, + /// {"price": 8, "title": "Book B"}, + /// {"price": 5, "title": "Book C"} + /// ]"#.parse::().unwrap(); + /// let raw_jsonb = jsonb_value.as_raw(); + /// + /// // Path with predicate (select books with price < 10) + /// let path = parse_json_path("$[*].price < 10".as_bytes()).unwrap(); + /// assert!(raw_jsonb.path_match(&path).unwrap()); // True because Book B and Book C match. + /// + /// // Path with predicate (select books with title "Book D") + /// let path = parse_json_path("$[*].title == \"Book D\"".as_bytes()).unwrap(); + /// assert!(!raw_jsonb.path_match(&path).unwrap()); // False because no book has this title. + /// ``` + pub fn path_match<'a>(&self, json_path: &'a JsonPath<'a>) -> Result { + let selector = Selector::new(json_path, Mode::First); + selector.predicate_match(*self) + } + + /// Deletes the element at the specified index from a JSONB array. + /// + /// This function removes the element at the given `index` from a JSONB array. + /// The `index` can be positive or negative: + /// + /// * **Positive index:** 0-based index from the beginning of the array. + /// * **Negative index:** 1-based index from the end of the array (e.g., -1 refers to the last element). + /// + /// If the `index` is out of bounds, the original JSONB array is returned unchanged. + /// If the input JSONB value is not an array (e.g., it's an object or a scalar), an `Error::InvalidJsonType` is returned. + /// Other invalid JSONB data results in an `Error::InvalidJsonb`. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// * `index` - The index of the element to delete. + /// + /// # Returns + /// + /// * `Ok(OwnedJsonb)` - The JSONB array with the element at the specified index removed, or the original array if the index is out of bounds. + /// * `Err(Error)` - If the input JSONB value is not an array, or if the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// let arr_jsonb = r#"[1, "hello", 3, 4]"#.parse::().unwrap(); + /// let raw_jsonb = arr_jsonb.as_raw(); + /// + /// // Delete element at index 1 + /// let deleted = raw_jsonb.delete_by_index(1).unwrap(); + /// assert_eq!(deleted.to_string(), "[1,3,4]"); + /// + /// // Delete last element using negative index + /// let deleted = raw_jsonb.delete_by_index(-1).unwrap(); + /// assert_eq!(deleted.to_string(), "[1,\"hello\",3]"); + /// + /// // Out of bounds index (positive) + /// let deleted = raw_jsonb.delete_by_index(4).unwrap(); + /// assert_eq!(deleted.to_string(), "[1,\"hello\",3,4]"); // Original array returned + /// + /// // Out of bounds index (negative) + /// let deleted = raw_jsonb.delete_by_index(-5).unwrap(); + /// assert_eq!(deleted.to_string(), "[1,\"hello\",3,4]"); // Original array returned + /// + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// let result = raw_jsonb.delete_by_index(0); + /// assert!(result.is_err()); // Error because input is not an array + pub fn delete_by_index(&self, index: i32) -> Result { + let mut buf = Vec::new(); + let value = self.data; + let header = read_u32(value, 0)?; + + match header & CONTAINER_HEADER_TYPE_MASK { + ARRAY_CONTAINER_TAG => { + let len = (header & CONTAINER_HEADER_LEN_MASK) as i32; + let index = if index < 0 { len - index.abs() } else { index }; + if index < 0 || index >= len { + buf.extend_from_slice(value); + } else { + let mut builder = + ArrayBuilder::new((header & CONTAINER_HEADER_LEN_MASK) as usize); + let index = index as usize; + for (i, entry) in iterate_array(value, header).enumerate() { + if i != index { + builder.push_raw(entry.0, entry.1); + } + } + builder.build_into(&mut buf); + } + } + SCALAR_CONTAINER_TAG | OBJECT_CONTAINER_TAG => return Err(Error::InvalidJsonType), + _ => return Err(Error::InvalidJsonb), + } + Ok(OwnedJsonb::new(buf)) + } + + /// Deletes a key-value pair from a JSONB object or an element from a JSONB array. + /// + /// This function removes a key-value pair from a JSONB object if the key matches the given `name` + /// or removes an element from a JSONB array if the element is a string that matches the given `name`. + /// + /// * **Object:** If the input is an object, the key-value pair with the matching key is removed. The key comparison is case-sensitive. + /// * **Array:** If the input is an array, elements that are strings and match `name` (case-sensitive) are removed. Other array elements remain unchanged. + /// * **Invalid input:** If the input JSONB value is a scalar value, an `Error::InvalidJsonType` is returned. Other invalid JSONB data results in an `Error::InvalidJsonb`. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// * `name` - The key (for objects) or string value (for arrays) to match. + /// + /// # Returns + /// + /// * `Ok(OwnedJsonb)` - The modified JSONB value with the matching key-value pair or element removed. + /// * `Err(Error)` - If the input JSONB value is a scalar, or if the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // Deleting from an object + /// let obj_jsonb = r#"{"a": 1, "b": "hello", "c": 3}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// let deleted = raw_jsonb.delete_by_name("b").unwrap(); + /// assert_eq!(deleted.to_string(), r#"{"a":1,"c":3}"#); + /// + /// // Deleting from an array (string elements only) + /// let arr_jsonb = r#"[1, "hello", 3, "world"]"#.parse::().unwrap(); + /// let raw_jsonb = arr_jsonb.as_raw(); + /// let deleted = raw_jsonb.delete_by_name("hello").unwrap(); + /// assert_eq!(deleted.to_string(), "[1,3,\"world\"]"); + /// + /// // Non-matching key in object + /// let deleted = raw_jsonb.delete_by_name("x").unwrap(); // "x" doesn't exist + /// assert_eq!(deleted.to_string(), r#"[1,"hello",3,"world"]"#); // Original array returned + /// + /// // Non-matching value in array + /// let deleted = arr_jsonb.as_raw().delete_by_name("xyz").unwrap(); // "xyz" doesn't exist + /// assert_eq!(deleted.to_string(), r#"[1,"hello",3,"world"]"#); // Original array returned + /// + /// // Attempting to delete from a scalar + /// let scalar_jsonb = "1".parse::().unwrap(); + /// let raw_jsonb = scalar_jsonb.as_raw(); + /// let result = raw_jsonb.delete_by_name("a"); + /// assert!(result.is_err()); // Returns an error + /// ``` + pub fn delete_by_name(&self, name: &str) -> Result { + let mut buf = Vec::new(); + let value = self.data; + let header = read_u32(value, 0)?; + + match header & CONTAINER_HEADER_TYPE_MASK { + OBJECT_CONTAINER_TAG => { + let mut builder = ObjectBuilder::new(); + for (key, jentry, item) in iterate_object_entries(value, header) { + if !key.eq(name) { + builder.push_raw(key, jentry, item); + } + } + builder.build_into(&mut buf); + } + ARRAY_CONTAINER_TAG => { + let mut builder = ArrayBuilder::new((header & CONTAINER_HEADER_LEN_MASK) as usize); + for (jentry, item) in iterate_array(value, header) { + let matches = match jentry.type_code { + STRING_TAG => { + let v = unsafe { from_utf8_unchecked(item) }; + v.eq(name) + } + _ => false, + }; + if !matches { + builder.push_raw(jentry, item); + } + } + builder.build_into(&mut buf); + } + SCALAR_CONTAINER_TAG => return Err(Error::InvalidJsonType), + _ => return Err(Error::InvalidJsonb), + } + Ok(OwnedJsonb::new(buf)) + } + + /// Deletes a value from a JSONB array or object based on a key path. + /// + /// This function removes a value from a JSONB array or object using a key path. The key path is an iterator of `KeyPath` elements specifying the path to the element to delete. + /// + /// * **Array:** If the JSONB value is an array, the key path must consist of array indices (`KeyPath::Index`). A negative index counts from the end of the array (e.g., -1 is the last element). If the index is out of bounds, the original JSONB value is returned unchanged. + /// * **Object:** If the JSONB value is an object, the key path can be a mix of object keys (`KeyPath::Name` or `KeyPath::QuotedName`) and array indices. If any part of the path is invalid (e.g., trying to access an index in a non-array or a key in a non-object), the original JSONB value is returned unchanged. + /// * **Invalid input:** If the input is neither an array nor an object, or if the JSONB data is otherwise invalid, an error (`Error::InvalidJsonType` or `Error::InvalidJsonb`) is returned. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// * `keypath` - An iterator of `KeyPath` elements specifying the path to the element to delete. + /// + /// # Returns + /// + /// * `Ok(OwnedJsonb)` - The JSONB value with the specified element deleted. Returns the original value if the keypath is invalid or leads to a non-existent value. + /// * `Err(Error)` - If the input JSONB value is neither an array nor an object, or if the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::keypath::KeyPath; + /// use jsonb::OwnedJsonb; + /// use std::borrow::Cow; + /// + /// // Deleting from an array + /// let arr_jsonb = r#"[1, 2, 3]"#.parse::().unwrap(); + /// let raw_jsonb = arr_jsonb.as_raw(); + /// let keypath = [KeyPath::Index(1)]; // Delete element at index 1 + /// let deleted = raw_jsonb.delete_by_keypath(keypath.iter()).unwrap(); + /// assert_eq!(deleted.to_string(), "[1,3]"); + /// + /// let keypath = [KeyPath::Index(-1)]; // Delete last element + /// let deleted = raw_jsonb.delete_by_keypath(keypath.iter()).unwrap(); + /// assert_eq!(deleted.to_string(), "[1,2]"); + /// + /// // Deleting from an object + /// let obj_jsonb = r#"{"a": {"b": [1, 2, 3]}, "c": 4}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// let keypath = [KeyPath::Name(Cow::Borrowed("a")), KeyPath::Name(Cow::Borrowed("b")), KeyPath::Index(1)]; + /// let deleted = raw_jsonb.delete_by_keypath(keypath.iter()).unwrap(); + /// assert_eq!(deleted.to_string(), r#"{"a":{"b":[1,3]},"c":4}"#); + /// + /// // Invalid keypath (index out of bounds) + /// let keypath = [KeyPath::Index(3)]; + /// let deleted = raw_jsonb.delete_by_keypath(keypath.iter()).unwrap(); + /// assert_eq!(deleted.to_string(), r#"{"a":{"b":[1,2,3]},"c":4}"#); // Original value returned + /// + /// // Invalid keypath (wrong type) + /// let keypath = [KeyPath::Name(Cow::Borrowed("a")), KeyPath::Name(Cow::Borrowed("x"))]; // "x" doesn't exist under "a" + /// let deleted = raw_jsonb.delete_by_keypath(keypath.iter()).unwrap(); + /// assert_eq!(deleted.to_string(), r#"{"a":{"b":[1,2,3]},"c":4}"#); // Original value returned + /// + /// // Attempting to delete from a scalar + /// let scalar_jsonb = "1".parse::().unwrap(); + /// let raw_jsonb = scalar_jsonb.as_raw(); + /// let result = raw_jsonb.delete_by_keypath([].iter()); + /// assert!(result.is_err()); // Returns an error + /// ``` + pub fn delete_by_keypath<'a, I: Iterator>>( + &self, + keypath: I, + ) -> Result { + let mut buf = Vec::new(); + let mut keypath: VecDeque<_> = keypath.collect(); + let value = self.data; + let header = read_u32(value, 0)?; + match header & CONTAINER_HEADER_TYPE_MASK { + ARRAY_CONTAINER_TAG => { + match self.delete_array_by_keypath(value, header, &mut keypath)? { + Some(builder) => { + builder.build_into(&mut buf); + } + None => { + buf.extend_from_slice(value); + } + }; + } + OBJECT_CONTAINER_TAG => { + match self.delete_object_by_keypath(value, header, &mut keypath)? { + Some(builder) => { + builder.build_into(&mut buf); + } + None => { + buf.extend_from_slice(value); + } + } + } + _ => return Err(Error::InvalidJsonType), + } + Ok(OwnedJsonb::new(buf)) + } + + fn delete_array_by_keypath<'a, 'b>( + &self, + value: &'b [u8], + header: u32, + keypath: &mut VecDeque<&'a KeyPath<'a>>, + ) -> Result>, Error> { + let len = (header & CONTAINER_HEADER_LEN_MASK) as i32; + match keypath.pop_front() { + Some(KeyPath::Index(idx)) => { + let idx = if *idx < 0 { len - idx.abs() } else { *idx }; + if idx < 0 || idx >= len { + return Ok(None); + } + let mut builder = ArrayBuilder::new(len as usize); + let idx = idx as usize; + for (i, entry) in iterate_array(value, header).enumerate() { + if i != idx { + builder.push_raw(entry.0, entry.1); + } else if !keypath.is_empty() { + let item_value = entry.1; + match entry.0.type_code { + CONTAINER_TAG => { + let item_header = read_u32(item_value, 0)?; + match item_header & CONTAINER_HEADER_TYPE_MASK { + ARRAY_CONTAINER_TAG => { + match self.delete_array_by_keypath( + item_value, + item_header, + keypath, + )? { + Some(item_builder) => builder.push_array(item_builder), + None => return Ok(None), + } + } + OBJECT_CONTAINER_TAG => { + match self.delete_object_by_keypath( + item_value, + item_header, + keypath, + )? { + Some(item_builder) => builder.push_object(item_builder), + None => return Ok(None), + } + } + _ => unreachable!(), + } + } + _ => return Ok(None), + } + } + } + Ok(Some(builder)) + } + _ => Ok(None), + } + } + + fn delete_object_by_keypath<'a, 'b>( + &self, + value: &'b [u8], + header: u32, + keypath: &mut VecDeque<&'a KeyPath<'a>>, + ) -> Result>, Error> { + match keypath.pop_front() { + Some(KeyPath::QuotedName(name) | KeyPath::Name(name)) => { + let mut builder = ObjectBuilder::new(); + for (key, jentry, item) in iterate_object_entries(value, header) { + if !key.eq(name) { + builder.push_raw(key, jentry, item); + } else if !keypath.is_empty() { + match jentry.type_code { + CONTAINER_TAG => { + let item_header = read_u32(item, 0)?; + match item_header & CONTAINER_HEADER_TYPE_MASK { + ARRAY_CONTAINER_TAG => { + match self.delete_array_by_keypath( + item, + item_header, + keypath, + )? { + Some(item_builder) => { + builder.push_array(key, item_builder) + } + None => return Ok(None), + } + } + OBJECT_CONTAINER_TAG => { + match self.delete_object_by_keypath( + item, + item_header, + keypath, + )? { + Some(item_builder) => { + builder.push_object(key, item_builder) + } + None => return Ok(None), + } + } + _ => unreachable!(), + } + } + _ => return Ok(None), + } + } + } + Ok(Some(builder)) + } + _ => Ok(None), + } + } +} + +pub(crate) fn get_jentry_by_name( + value: &[u8], + offset: usize, + header: u32, + name: &str, + ignore_case: bool, +) -> Option<(JEntry, u32, usize)> { + let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; + let mut jentry_offset = offset + 4; + let mut val_offset = offset + 8 * length + 4; + + let mut key_jentries: VecDeque = VecDeque::with_capacity(length); + for _ in 0..length { + let encoded = read_u32(value, jentry_offset).ok()?; + let key_jentry = JEntry::decode_jentry(encoded); + + jentry_offset += 4; + val_offset += key_jentry.length as usize; + key_jentries.push_back(key_jentry); + } + + let mut result = None; + let mut key_offset = offset + 8 * length + 4; + + while let Some(key_jentry) = key_jentries.pop_front() { + let prev_key_offset = key_offset; + key_offset += key_jentry.length as usize; + let key = unsafe { std::str::from_utf8_unchecked(&value[prev_key_offset..key_offset]) }; + + let val_encoded = read_u32(value, jentry_offset).ok()?; + let val_jentry = JEntry::decode_jentry(val_encoded); + let val_length = val_jentry.length as usize; + + // first match the value with the same name, if not found, + // then match the value with the ignoring case name. + if name.eq(key) { + result = Some((val_jentry, val_encoded, val_offset)); + break; + } else if ignore_case && name.eq_ignore_ascii_case(key) && result.is_none() { + result = Some((val_jentry, val_encoded, val_offset)); + } + + jentry_offset += 4; + val_offset += val_length; + } + result +} + +pub(crate) fn get_jentry_by_index( + value: &[u8], + offset: usize, + header: u32, + index: usize, +) -> Option<(JEntry, u32, usize)> { + let length = (header & CONTAINER_HEADER_LEN_MASK) as usize; + if index >= length { + return None; + } + let mut jentry_offset = offset + 4; + let mut val_offset = offset + 4 * length + 4; + + for i in 0..length { + let encoded = read_u32(value, jentry_offset).ok()?; + let jentry = JEntry::decode_jentry(encoded); + let val_length = jentry.length as usize; + if i < index { + jentry_offset += 4; + val_offset += val_length; + continue; + } + return Some((jentry, encoded, val_offset)); + } + None +} diff --git a/src/functions/scalar.rs b/src/functions/scalar.rs new file mode 100644 index 0000000..e649092 --- /dev/null +++ b/src/functions/scalar.rs @@ -0,0 +1,1395 @@ +// Copyright 2023 Datafuse Labs. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains functions that specifically operate on JSONB scalar values. + +use std::borrow::Cow; + +use crate::constants::*; +use crate::error::*; +use crate::functions::core::read_u32; +use crate::jentry::JEntry; +use crate::number::Number; + +use crate::RawJsonb; + +impl RawJsonb<'_> { + /// Checks if the JSONB value is null. + /// + /// This function determines whether the JSONB value represents a JSON `null`. + /// + /// # Returns + /// + /// * `Ok(true)` if the value is null. + /// * `Ok(false)` if the value is not null. + /// * `Err(Error)` if an error occurred during decoding (e.g., invalid JSONB data). + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// let null_jsonb = "null".parse::().unwrap(); + /// let raw_jsonb = null_jsonb.as_raw(); + /// assert!(raw_jsonb.is_null().unwrap()); + /// + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// assert!(!raw_jsonb.is_null().unwrap()); + /// ``` + pub fn is_null(&self) -> Result { + self.as_null().map(|v| v.is_some()) + } + + /// Checks if the JSONB value is null. + /// + /// This function checks if the JSONB value represents a JSON `null` value. + /// It returns `Some(())` if the value is null and `None` otherwise. + /// Note that this function only checks for the specific JSON `null` type; it doesn't check for empty objects or arrays. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(Some(()))` - If the value is JSON `null`. + /// * `Ok(None)` - If the value is not JSON `null`. + /// * `Err(Error)` - If the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // JSON null + /// let null_jsonb = "null".parse::().unwrap(); + /// let raw_jsonb = null_jsonb.as_raw(); + /// assert!(raw_jsonb.as_null().unwrap().is_some()); + /// + /// // Non-null values + /// let num_jsonb = "1".parse::().unwrap(); + /// let raw_jsonb = num_jsonb.as_raw(); + /// assert!(raw_jsonb.as_null().unwrap().is_none()); + /// + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_jsonb = arr_jsonb.as_raw(); + /// assert!(raw_jsonb.as_null().unwrap().is_none()); + /// + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let raw_jsonb = obj_jsonb.as_raw(); + /// assert!(raw_jsonb.as_null().unwrap().is_none()); + /// + /// let empty_array_jsonb = "[]".parse::().unwrap(); + /// let raw_jsonb = empty_array_jsonb.as_raw(); + /// assert!(raw_jsonb.as_null().unwrap().is_none()); + /// + /// let empty_object_jsonb = "{}".parse::().unwrap(); + /// let raw_jsonb = empty_object_jsonb.as_raw(); + /// assert!(raw_jsonb.as_null().unwrap().is_none()); + /// + /// // Invalid JSONB + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.as_null(); + /// assert!(result.is_err()); + /// ``` + pub fn as_null(&self) -> Result, Error> { + let header = read_u32(self.data, 0)?; + match header & CONTAINER_HEADER_TYPE_MASK { + SCALAR_CONTAINER_TAG => { + let jentry_encoded = read_u32(self.data, 4)?; + let jentry = JEntry::decode_jentry(jentry_encoded); + match jentry.type_code { + NULL_TAG => Ok(Some(())), + STRING_TAG | NUMBER_TAG | FALSE_TAG | TRUE_TAG | CONTAINER_TAG => Ok(None), + _ => Err(Error::InvalidJsonb), + } + } + OBJECT_CONTAINER_TAG | ARRAY_CONTAINER_TAG => Ok(None), + _ => Err(Error::InvalidJsonb), + } + } + + /// Checks if the JSONB value is a boolean. + /// + /// This function checks if the JSONB value represents a JSON boolean (`true` or `false`). + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(true)` - If the value is a boolean (`true` or `false`). + /// * `Ok(false)` - If the value is not a boolean. + /// * `Err(Error)` - If the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // Boolean values + /// let true_jsonb = "true".parse::().unwrap(); + /// let raw_true = true_jsonb.as_raw(); + /// assert!(raw_true.is_boolean().unwrap()); + /// + /// let false_jsonb = "false".parse::().unwrap(); + /// let raw_false = false_jsonb.as_raw(); + /// assert!(raw_false.is_boolean().unwrap()); + /// + /// // Non-boolean values + /// let num_jsonb = "1".parse::().unwrap(); + /// let raw_num = num_jsonb.as_raw(); + /// assert!(!raw_num.is_boolean().unwrap()); + /// + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_arr = arr_jsonb.as_raw(); + /// assert!(!raw_arr.is_boolean().unwrap()); + /// + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let raw_obj = obj_jsonb.as_raw(); + /// assert!(!raw_obj.is_boolean().unwrap()); + /// + /// let null_jsonb = "null".parse::().unwrap(); + /// let raw_null = null_jsonb.as_raw(); + /// assert!(!raw_null.is_boolean().unwrap()); + /// + /// // Invalid JSONB + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.is_boolean(); + /// assert!(result.is_err()); + /// ``` + pub fn is_boolean(&self) -> Result { + self.as_bool().map(|v| v.is_some()) + } + + /// Extracts a boolean value from a JSONB value. + /// + /// This function attempts to extract a boolean value (`true` or `false`) from the JSONB value. If the JSONB value is a boolean, the corresponding boolean value is returned. If the JSONB value is not a boolean (e.g., a number, string, null, array, or object), `None` is returned. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(Some(true))` - If the value is JSON `true`. + /// * `Ok(Some(false))` - If the value is JSON `false`. + /// * `Ok(None)` - If the value is not a boolean (number, string, null, array, object). + /// * `Err(Error)` - If the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // Boolean values + /// let true_jsonb = "true".parse::().unwrap(); + /// let raw_true = true_jsonb.as_raw(); + /// assert_eq!(raw_true.as_bool().unwrap(), Some(true)); + /// + /// let false_jsonb = "false".parse::().unwrap(); + /// let raw_false = false_jsonb.as_raw(); + /// assert_eq!(raw_false.as_bool().unwrap(), Some(false)); + /// + /// // Non-boolean values + /// let num_jsonb = "1".parse::().unwrap(); + /// let raw_num = num_jsonb.as_raw(); + /// assert_eq!(raw_num.as_bool().unwrap(), None); + /// + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_arr = arr_jsonb.as_raw(); + /// assert_eq!(raw_arr.as_bool().unwrap(), None); + /// + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let raw_obj = obj_jsonb.as_raw(); + /// assert_eq!(raw_obj.as_bool().unwrap(), None); + /// + /// let null_jsonb = "null".parse::().unwrap(); + /// let raw_null = null_jsonb.as_raw(); + /// assert_eq!(raw_null.as_bool().unwrap(), None); + /// + /// // Invalid JSONB + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.as_bool(); + /// assert!(result.is_err()); + /// ``` + pub fn as_bool(&self) -> Result, Error> { + let header = read_u32(self.data, 0)?; + match header & CONTAINER_HEADER_TYPE_MASK { + SCALAR_CONTAINER_TAG => { + let jentry_encoded = read_u32(self.data, 4)?; + let jentry = JEntry::decode_jentry(jentry_encoded); + match jentry.type_code { + FALSE_TAG => Ok(Some(false)), + TRUE_TAG => Ok(Some(true)), + NULL_TAG | STRING_TAG | NUMBER_TAG | CONTAINER_TAG => Ok(None), + _ => Err(Error::InvalidJsonb), + } + } + OBJECT_CONTAINER_TAG | ARRAY_CONTAINER_TAG => Ok(None), + _ => Err(Error::InvalidJsonb), + } + } + + /// Converts a JSONB value to a boolean. + /// + /// This function attempts to convert a JSONB value to a boolean. It prioritizes extracting a boolean value directly if possible. If the value is a string, it converts the string to lowercase and checks if it's "true" or "false". Otherwise, it returns an error. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(true)` - If the value is JSON `true` or a string that is "true" (case-insensitive). + /// * `Ok(false)` - If the value is JSON `false` or a string that is "false" (case-insensitive). + /// * `Err(Error::InvalidCast)` - If the value cannot be converted to a boolean (e.g., it's a number, null, array, or object, or a string that's not "true" or "false"). + /// * `Err(Error)` - If the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // Boolean values + /// let true_jsonb = "true".parse::().unwrap(); + /// assert!(true_jsonb.as_raw().to_bool().unwrap()); + /// + /// let false_jsonb = "false".parse::().unwrap(); + /// assert!(!false_jsonb.as_raw().to_bool().unwrap()); + /// + /// // String representations of booleans + /// let true_str = r#""true""#.parse::().unwrap(); + /// assert!(true_str.as_raw().to_bool().unwrap()); + /// + /// let false_str = r#""false""#.parse::().unwrap(); + /// assert!(!false_str.as_raw().to_bool().unwrap()); + /// + /// let true_str_lowercase = r#""TRUE""#.parse::().unwrap(); + /// assert!(true_str_lowercase.as_raw().to_bool().unwrap()); + /// + /// // Invalid conversions + /// let num_jsonb = "1".parse::().unwrap(); + /// let result = num_jsonb.as_raw().to_bool(); + /// assert!(result.is_err()); + /// + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let result = arr_jsonb.as_raw().to_bool(); + /// assert!(result.is_err()); + /// + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let result = obj_jsonb.as_raw().to_bool(); + /// assert!(result.is_err()); + /// + /// let null_jsonb = "null".parse::().unwrap(); + /// let result = null_jsonb.as_raw().to_bool(); + /// assert!(result.is_err()); + /// + /// let invalid_str = r#""maybe""#.parse::().unwrap(); + /// let result = invalid_str.as_raw().to_bool(); + /// assert!(result.is_err()); + /// + /// // Invalid JSONB + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.to_bool(); + /// assert!(result.is_err()); + /// ``` + pub fn to_bool(&self) -> Result { + if let Some(v) = self.as_bool()? { + return Ok(v); + } else if let Some(v) = self.as_str()? { + if &v.to_lowercase() == "true" { + return Ok(true); + } else if &v.to_lowercase() == "false" { + return Ok(false); + } + } + Err(Error::InvalidCast) + } + + /// Checks if the JSONB value is a number. + /// + /// This function checks if the JSONB value represents a JSON number. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(true)` - If the value is a number. + /// * `Ok(false)` - If the value is not a number. + /// * `Err(Error)` - If the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // Number values + /// let num_jsonb = "123.45".parse::().unwrap(); + /// let raw_num = num_jsonb.as_raw(); + /// assert!(raw_num.is_number().unwrap()); + /// + /// let num_jsonb = "123".parse::().unwrap(); + /// let raw_num = num_jsonb.as_raw(); + /// assert!(raw_num.is_number().unwrap()); + /// + /// let num_jsonb = "-123.45".parse::().unwrap(); + /// let raw_num = num_jsonb.as_raw(); + /// assert!(raw_num.is_number().unwrap()); + /// + /// // Non-number values + /// let bool_jsonb = "true".parse::().unwrap(); + /// let raw_bool = bool_jsonb.as_raw(); + /// assert!(!raw_bool.is_number().unwrap()); + /// + /// let str_jsonb = r#""hello""#.parse::().unwrap(); + /// let raw_str = str_jsonb.as_raw(); + /// assert!(!raw_str.is_number().unwrap()); + /// + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_arr = arr_jsonb.as_raw(); + /// assert!(!raw_arr.is_number().unwrap()); + /// + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let raw_obj = obj_jsonb.as_raw(); + /// assert!(!raw_obj.is_number().unwrap()); + /// + /// let null_jsonb = "null".parse::().unwrap(); + /// let raw_null = null_jsonb.as_raw(); + /// assert!(!raw_null.is_number().unwrap()); + /// + /// // Invalid JSONB + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.is_number(); + /// assert!(result.is_err()); + /// ``` + pub fn is_number(&self) -> Result { + self.as_number().map(|v| v.is_some()) + } + + /// Extracts a number from a JSONB value. + /// + /// This function attempts to extract a number from the JSONB value. If the JSONB value is a number, it returns the number; otherwise, it returns `None`. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(Some(Number))` - If the value is a number, the extracted number. + /// * `Ok(None)` - If the value is not a number (boolean, string, null, array, object). + /// * `Err(Error)` - If the JSONB data is invalid or if the number cannot be decoded. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::{Number, OwnedJsonb, RawJsonb}; + /// + /// // Number value + /// let num_jsonb = "123.45".parse::().unwrap(); + /// let raw_num = num_jsonb.as_raw(); + /// assert_eq!(raw_num.as_number().unwrap(), Some(Number::Float64(123.45))); + /// + /// let num_jsonb = "-123".parse::().unwrap(); + /// let raw_num = num_jsonb.as_raw(); + /// assert_eq!(raw_num.as_number().unwrap(), Some(Number::Int64(-123))); + /// + /// // Non-number values + /// let bool_jsonb = "true".parse::().unwrap(); + /// let raw_bool = bool_jsonb.as_raw(); + /// assert_eq!(raw_bool.as_number().unwrap(), None); + /// + /// let str_jsonb = r#""hello""#.parse::().unwrap(); + /// let raw_str = str_jsonb.as_raw(); + /// assert_eq!(raw_str.as_number().unwrap(), None); + /// + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_arr = arr_jsonb.as_raw(); + /// assert_eq!(raw_arr.as_number().unwrap(), None); + /// + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let raw_obj = obj_jsonb.as_raw(); + /// assert_eq!(raw_obj.as_number().unwrap(), None); + /// + /// let null_jsonb = "null".parse::().unwrap(); + /// let raw_null = null_jsonb.as_raw(); + /// assert_eq!(raw_null.as_number().unwrap(), None); + /// + /// // Invalid JSONB + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.as_number(); + /// assert!(result.is_err()); + /// + /// // Invalid Number (corrupted data) + /// let corrupted_num_jsonb = OwnedJsonb::new(vec![10, 0, 0, 0, 16, 0, 0, 0, 0, 1]); + /// let corrupted_raw_num_jsonb = corrupted_num_jsonb.as_raw(); + /// let result = corrupted_raw_num_jsonb.as_number(); + /// assert!(result.is_err()); //Decodes should return Err + /// ``` + pub fn as_number(&self) -> Result, Error> { + let header = read_u32(self.data, 0)?; + match header & CONTAINER_HEADER_TYPE_MASK { + SCALAR_CONTAINER_TAG => { + let jentry_encoded = read_u32(self.data, 4)?; + let jentry = JEntry::decode_jentry(jentry_encoded); + match jentry.type_code { + NUMBER_TAG => { + let length = jentry.length as usize; + let num = Number::decode(&self.data[8..8 + length])?; + Ok(Some(num)) + } + NULL_TAG | STRING_TAG | FALSE_TAG | TRUE_TAG | CONTAINER_TAG => Ok(None), + _ => Err(Error::InvalidJsonb), + } + } + OBJECT_CONTAINER_TAG | ARRAY_CONTAINER_TAG => Ok(None), + _ => Err(Error::InvalidJsonb), + } + } + + /// Checks if the JSONB value is an integer that can be represented as an i64. + /// + /// This function checks if the JSONB value is a number and can be converted to an `i64` without loss of information. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(true)` - If the value is an integer representable as an `i64`. + /// * `Ok(false)` - If the value is not an integer or cannot be represented as an `i64`. + /// * `Err(Error)` - If the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // i64 values + /// let i64_jsonb = "123456789012345678".parse::().unwrap(); + /// let raw_i64 = i64_jsonb.as_raw(); + /// assert!(raw_i64.is_i64().unwrap()); + /// + /// let i64_jsonb = "-123456789012345678".parse::().unwrap(); + /// let raw_i64 = i64_jsonb.as_raw(); + /// assert!(raw_i64.is_i64().unwrap()); + /// + /// // Non-i64 values + /// let float_jsonb = "123.45".parse::().unwrap(); + /// let raw_float = float_jsonb.as_raw(); + /// assert!(!raw_float.is_i64().unwrap()); + /// + /// let str_jsonb = r#""hello""#.parse::().unwrap(); + /// let raw_str = str_jsonb.as_raw(); + /// assert!(!raw_str.is_i64().unwrap()); + /// + /// // Invalid JSONB + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.is_i64(); + /// assert!(result.is_err()); + /// ``` + pub fn is_i64(&self) -> Result { + self.as_i64().map(|v| v.is_some()) + } + + /// Extracts an i64 integer from a JSONB value. + /// + /// This function attempts to extract an `i64` integer from the JSONB value. If the JSONB value is a number and can be represented as an `i64` without loss of information, the integer value is returned. Otherwise, `None` is returned. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(Some(i64))` - If the value is an integer that can be represented as an `i64`. + /// * `Ok(None)` - If the value is not an integer or cannot be represented as an `i64` (e.g., it's a floating-point number, a boolean, string, null, array, or object). + /// * `Err(Error)` - If the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // i64 value + /// let i64_jsonb = "123456789012345678".parse::().unwrap(); + /// let raw_i64 = i64_jsonb.as_raw(); + /// assert_eq!(raw_i64.as_i64().unwrap(), Some(123456789012345678)); + /// + /// // Non-i64 values + /// let float_jsonb = "123.45".parse::().unwrap(); + /// let raw_float = float_jsonb.as_raw(); + /// assert_eq!(raw_float.as_i64().unwrap(), None); + /// + /// let str_jsonb = r#""hello""#.parse::().unwrap(); + /// let raw_str = str_jsonb.as_raw(); + /// assert_eq!(raw_str.as_i64().unwrap(), None); + /// + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_arr = arr_jsonb.as_raw(); + /// assert_eq!(raw_arr.as_i64().unwrap(), None); + /// + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let raw_obj = obj_jsonb.as_raw(); + /// assert_eq!(raw_obj.as_i64().unwrap(), None); + /// + /// // Invalid JSONB + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.as_i64(); + /// assert!(result.is_err()); + /// ``` + pub fn as_i64(&self) -> Result, Error> { + match self.as_number()? { + Some(num) => Ok(num.as_i64()), + None => Ok(None), + } + } + + /// Converts a JSONB value to an i64 integer. + /// + /// This function attempts to convert a JSONB value to an `i64` integer. It prioritizes direct conversion from a number if possible. If the value is a boolean, it's converted to 1 (for `true`) or 0 (for `false`). If the value is a string that can be parsed as an `i64`, that parsed value is returned. Otherwise, an error is returned. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(i64)` - The `i64` representation of the JSONB value. + /// * `Err(Error::InvalidCast)` - If the value cannot be converted to an `i64` (e.g., it's a floating-point number, an array, an object, or a string that is not a valid integer). + /// * `Err(Error)` - If the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // Integer values + /// let i64_jsonb = "123".parse::().unwrap(); + /// assert_eq!(i64_jsonb.as_raw().to_i64().unwrap(), 123); + /// + /// let i64_jsonb = "-42".parse::().unwrap(); + /// assert_eq!(i64_jsonb.as_raw().to_i64().unwrap(), -42); + /// + /// // Boolean values + /// let true_jsonb = "true".parse::().unwrap(); + /// assert_eq!(true_jsonb.as_raw().to_i64().unwrap(), 1); + /// + /// let false_jsonb = "false".parse::().unwrap(); + /// assert_eq!(false_jsonb.as_raw().to_i64().unwrap(), 0); + /// + /// // String representation of an integer + /// let str_jsonb = r#""123""#.parse::().unwrap(); + /// assert_eq!(str_jsonb.as_raw().to_i64().unwrap(), 123); + /// + /// // Invalid conversions + /// let float_jsonb = "123.45".parse::().unwrap(); + /// let result = float_jsonb.as_raw().to_i64(); + /// assert!(result.is_err()); + /// + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let result = arr_jsonb.as_raw().to_i64(); + /// assert!(result.is_err()); + /// + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let result = obj_jsonb.as_raw().to_i64(); + /// assert!(result.is_err()); + /// + /// let null_jsonb = "null".parse::().unwrap(); + /// let result = null_jsonb.as_raw().to_i64(); + /// assert!(result.is_err()); + /// + /// let invalid_str_jsonb = r#""abc""#.parse::().unwrap(); + /// let result = invalid_str_jsonb.as_raw().to_i64(); + /// assert!(result.is_err()); + /// + /// // Invalid JSONB + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.to_i64(); + /// assert!(result.is_err()); + /// ``` + pub fn to_i64(&self) -> Result { + if let Some(v) = self.as_i64()? { + return Ok(v); + } else if let Some(v) = self.as_bool()? { + if v { + return Ok(1_i64); + } else { + return Ok(0_i64); + } + } else if let Some(v) = self.as_str()? { + if let Ok(v) = v.parse::() { + return Ok(v); + } + } + Err(Error::InvalidCast) + } + + /// Checks if the JSONB value is an unsigned integer that can be represented as a u64. + /// + /// This function checks if the JSONB value is a number and can be converted to a `u64` without loss of information. Negative numbers will always return `false`. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(true)` - If the value is an unsigned integer representable as a `u64`. + /// * `Ok(false)` - If the value is not an unsigned integer or cannot be represented as a `u64`. + /// * `Err(Error)` - If the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // u64 values + /// let u64_jsonb = "1234567890123456789".parse::().unwrap(); + /// let raw_u64 = u64_jsonb.as_raw(); + /// assert!(raw_u64.is_u64().unwrap()); + /// + /// let u64_jsonb = "0".parse::().unwrap(); + /// let raw_u64 = u64_jsonb.as_raw(); + /// assert!(raw_u64.is_u64().unwrap()); + /// + /// // Non-u64 values + /// let float_jsonb = "123.45".parse::().unwrap(); + /// let raw_float = float_jsonb.as_raw(); + /// assert!(!raw_float.is_u64().unwrap()); + /// + /// let negative_num_jsonb = "-123".parse::().unwrap(); + /// let raw_neg = negative_num_jsonb.as_raw(); + /// assert!(!raw_neg.is_u64().unwrap()); // Negative numbers are not u64 + /// + /// let bool_jsonb = "true".parse::().unwrap(); + /// let raw_bool = bool_jsonb.as_raw(); + /// assert!(!raw_bool.is_u64().unwrap()); + /// + /// let str_jsonb = r#""hello""#.parse::().unwrap(); + /// let raw_str = str_jsonb.as_raw(); + /// assert!(!raw_str.is_u64().unwrap()); + /// + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_arr = arr_jsonb.as_raw(); + /// assert!(!raw_arr.is_u64().unwrap()); + /// + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let raw_obj = obj_jsonb.as_raw(); + /// assert!(!raw_obj.is_u64().unwrap()); + /// + /// let null_jsonb = "null".parse::().unwrap(); + /// let raw_null = null_jsonb.as_raw(); + /// assert!(!raw_null.is_u64().unwrap()); + /// + /// // Invalid JSONB + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.is_u64(); + /// assert!(result.is_err()); + /// ``` + pub fn is_u64(&self) -> Result { + self.as_u64().map(|v| v.is_some()) + } + + /// Extracts a u64 unsigned integer from a JSONB value. + /// + /// This function attempts to extract a `u64` unsigned integer from the JSONB value. If the JSONB value is a number and can be represented as a `u64` without loss of information (i.e., it's a non-negative integer within the `u64` range), the unsigned integer value is returned. Otherwise, `None` is returned. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(Some(u64))` - If the value is an unsigned integer that can be represented as a `u64`. + /// * `Ok(None)` - If the value is not an unsigned integer or cannot be represented as a `u64` (e.g., it's a floating-point number, a negative number, a boolean, string, null, array, or object). + /// * `Err(Error)` - If the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // u64 value + /// let u64_jsonb = "1234567890123456789".parse::().unwrap(); + /// let raw_u64 = u64_jsonb.as_raw(); + /// assert_eq!(raw_u64.as_u64().unwrap(), Some(1234567890123456789)); + /// + /// // Non-u64 values + /// let float_jsonb = "123.45".parse::().unwrap(); + /// let raw_float = float_jsonb.as_raw(); + /// assert_eq!(raw_float.as_u64().unwrap(), None); + /// + /// let negative_num_jsonb = "-123".parse::().unwrap(); + /// let raw_neg = negative_num_jsonb.as_raw(); + /// assert_eq!(raw_neg.as_u64().unwrap(), None); // Negative numbers are not u64 + /// + /// let bool_jsonb = "true".parse::().unwrap(); + /// let raw_bool = bool_jsonb.as_raw(); + /// assert_eq!(raw_bool.as_u64().unwrap(), None); + /// + /// let str_jsonb = r#""hello""#.parse::().unwrap(); + /// let raw_str = str_jsonb.as_raw(); + /// assert_eq!(raw_str.as_u64().unwrap(), None); + /// + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_arr = arr_jsonb.as_raw(); + /// assert_eq!(raw_arr.as_u64().unwrap(), None); + /// + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let raw_obj = obj_jsonb.as_raw(); + /// assert_eq!(raw_obj.as_u64().unwrap(), None); + /// + /// let null_jsonb = "null".parse::().unwrap(); + /// let raw_null = null_jsonb.as_raw(); + /// assert_eq!(raw_null.as_u64().unwrap(), None); + /// + /// // Invalid JSONB + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.as_u64(); + /// assert!(result.is_err()); + /// ``` + pub fn as_u64(&self) -> Result, Error> { + match self.as_number()? { + Some(num) => Ok(num.as_u64()), + None => Ok(None), + } + } + + /// Converts a JSONB value to a u64 unsigned integer. + /// + /// This function attempts to convert a JSONB value to a `u64` unsigned integer. It prioritizes direct conversion from a number if possible. If the value is a boolean, it's converted to 1 (for `true`) or 0 (for `false`). If the value is a string that can be parsed as a `u64`, that parsed value is returned. Otherwise, an error is returned. Note that negative numbers cannot be converted to `u64`. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(u64)` - The `u64` representation of the JSONB value. + /// * `Err(Error::InvalidCast)` - If the value cannot be converted to a `u64` (e.g., it's a floating-point number, a negative number, an array, an object, or a string that is not a valid unsigned integer). + /// * `Err(Error)` - If the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // u64 values + /// let u64_jsonb = "1234567890123456789".parse::().unwrap(); + /// assert_eq!(u64_jsonb.as_raw().to_u64().unwrap(), 1234567890123456789); + /// + /// let u64_jsonb = "0".parse::().unwrap(); + /// assert_eq!(u64_jsonb.as_raw().to_u64().unwrap(), 0); + /// + /// // Boolean values + /// let true_jsonb = "true".parse::().unwrap(); + /// assert_eq!(true_jsonb.as_raw().to_u64().unwrap(), 1); + /// + /// let false_jsonb = "false".parse::().unwrap(); + /// assert_eq!(false_jsonb.as_raw().to_u64().unwrap(), 0); + /// + /// // String representation of an unsigned integer + /// let str_jsonb = r#""123""#.parse::().unwrap(); + /// assert_eq!(str_jsonb.as_raw().to_u64().unwrap(), 123); + /// + /// // Invalid conversions + /// let float_jsonb = "123.45".parse::().unwrap(); + /// let result = float_jsonb.as_raw().to_u64(); + /// assert!(result.is_err()); + /// + /// let negative_num_jsonb = "-123".parse::().unwrap(); + /// let result = negative_num_jsonb.as_raw().to_u64(); + /// assert!(result.is_err()); // Negative numbers are not u64 + /// + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let result = arr_jsonb.as_raw().to_u64(); + /// assert!(result.is_err()); + /// + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let result = obj_jsonb.as_raw().to_u64(); + /// assert!(result.is_err()); + /// + /// let null_jsonb = "null".parse::().unwrap(); + /// let result = null_jsonb.as_raw().to_u64(); + /// assert!(result.is_err()); + /// + /// let invalid_str_jsonb = r#""abc""#.parse::().unwrap(); + /// let result = invalid_str_jsonb.as_raw().to_u64(); + /// assert!(result.is_err()); + /// + /// // Invalid JSONB + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.to_u64(); + /// assert!(result.is_err()); + /// ``` + pub fn to_u64(&self) -> Result { + if let Some(v) = self.as_u64()? { + return Ok(v); + } else if let Some(v) = self.as_bool()? { + if v { + return Ok(1_u64); + } else { + return Ok(0_u64); + } + } else if let Some(v) = self.as_str()? { + if let Ok(v) = v.parse::() { + return Ok(v); + } + } + Err(Error::InvalidCast) + } + + /// Checks if the JSONB value is a floating-point number that can be represented as an f64. + /// + /// This function checks if the JSONB value is a number and can be converted to an `f64` without loss of information (this is generally always true for numbers in JSONB). + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(true)` - If the value is a number. + /// * `Ok(false)` - If the value is not a number (boolean, string, null, array, object). + /// * `Err(Error)` - If the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // f64 values + /// let f64_jsonb = "123.45".parse::().unwrap(); + /// let raw_f64 = f64_jsonb.as_raw(); + /// assert!(raw_f64.is_f64().unwrap()); + /// + /// let f64_jsonb = "123".parse::().unwrap(); + /// let raw_f64 = f64_jsonb.as_raw(); + /// assert!(raw_f64.is_f64().unwrap()); + /// + /// let f64_jsonb = "-123.45".parse::().unwrap(); + /// let raw_f64 = f64_jsonb.as_raw(); + /// assert!(raw_f64.is_f64().unwrap()); + /// + /// // Non-f64 values + /// let bool_jsonb = "true".parse::().unwrap(); + /// let raw_bool = bool_jsonb.as_raw(); + /// assert!(!raw_bool.is_f64().unwrap()); + /// + /// let str_jsonb = r#""hello""#.parse::().unwrap(); + /// let raw_str = str_jsonb.as_raw(); + /// assert!(!raw_str.is_f64().unwrap()); + /// + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_arr = arr_jsonb.as_raw(); + /// assert!(!raw_arr.is_f64().unwrap()); + /// + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let raw_obj = obj_jsonb.as_raw(); + /// assert!(!raw_obj.is_f64().unwrap()); + /// + /// let null_jsonb = "null".parse::().unwrap(); + /// let raw_null = null_jsonb.as_raw(); + /// assert!(!raw_null.is_f64().unwrap()); + /// + /// // Invalid JSONB + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.is_f64(); + /// assert!(result.is_err()); + /// ``` + pub fn is_f64(&self) -> Result { + self.as_f64().map(|v| v.is_some()) + } + + /// Extracts an f64 floating-point number from a JSONB value. + /// + /// This function attempts to extract an `f64` floating-point number from the JSONB value. If the JSONB value is a number, it's converted to an `f64` and returned. Otherwise, `None` is returned. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(Some(f64))` - If the value is a number, the extracted `f64` value. + /// * `Ok(None)` - If the value is not a number (boolean, string, null, array, object). + /// * `Err(Error)` - If the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // f64 values + /// let f64_jsonb = "123.45".parse::().unwrap(); + /// let raw_f64 = f64_jsonb.as_raw(); + /// assert_eq!(raw_f64.as_f64().unwrap(), Some(123.45)); + /// + /// let int_jsonb = "123".parse::().unwrap(); + /// let raw_int = int_jsonb.as_raw(); + /// assert_eq!(raw_int.as_f64().unwrap(), Some(123.0)); + /// + /// // Non-f64 values + /// let bool_jsonb = "true".parse::().unwrap(); + /// let raw_bool = bool_jsonb.as_raw(); + /// assert_eq!(raw_bool.as_f64().unwrap(), None); + /// + /// let str_jsonb = r#""hello""#.parse::().unwrap(); + /// let raw_str = str_jsonb.as_raw(); + /// assert_eq!(raw_str.as_f64().unwrap(), None); + /// + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_arr = arr_jsonb.as_raw(); + /// assert_eq!(raw_arr.as_f64().unwrap(), None); + /// + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let raw_obj = obj_jsonb.as_raw(); + /// assert_eq!(raw_obj.as_f64().unwrap(), None); + /// + /// let null_jsonb = "null".parse::().unwrap(); + /// let raw_null = null_jsonb.as_raw(); + /// assert_eq!(raw_null.as_f64().unwrap(), None); + /// + /// // Invalid JSONB + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.as_f64(); + /// assert!(result.is_err()); + /// ``` + pub fn as_f64(&self) -> Result, Error> { + match self.as_number()? { + Some(num) => Ok(num.as_f64()), + None => Ok(None), + } + } + + /// Converts a JSONB value to an f64 floating-point number. + /// + /// This function attempts to convert a JSONB value to an `f64` floating-point number. It prioritizes direct conversion from a number if possible. If the value is a boolean, it's converted to 1.0 (for `true`) or 0.0 (for `false`). If the value is a string that can be parsed as an `f64`, that parsed value is returned. Otherwise, an error is returned. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(f64)` - The `f64` representation of the JSONB value. + /// * `Err(Error::InvalidCast)` - If the value cannot be converted to an `f64` (e.g., it's an array, an object, a string that is not a valid number, or a null value). + /// * `Err(Error)` - If the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // f64 values + /// let f64_jsonb = "123.45".parse::().unwrap(); + /// assert_eq!(f64_jsonb.as_raw().to_f64().unwrap(), 123.45); + /// + /// let int_jsonb = "123".parse::().unwrap(); + /// assert_eq!(int_jsonb.as_raw().to_f64().unwrap(), 123.0); + /// + /// // Boolean values + /// let true_jsonb = "true".parse::().unwrap(); + /// assert_eq!(true_jsonb.as_raw().to_f64().unwrap(), 1.0); + /// + /// let false_jsonb = "false".parse::().unwrap(); + /// assert_eq!(false_jsonb.as_raw().to_f64().unwrap(), 0.0); + /// + /// // String representation of a number + /// let str_jsonb = r#""123.45""#.parse::().unwrap(); + /// assert_eq!(str_jsonb.as_raw().to_f64().unwrap(), 123.45); + /// + /// // Invalid conversions + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let result = arr_jsonb.as_raw().to_f64(); + /// assert!(result.is_err()); + /// + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let result = obj_jsonb.as_raw().to_f64(); + /// assert!(result.is_err()); + /// + /// let null_jsonb = "null".parse::().unwrap(); + /// let result = null_jsonb.as_raw().to_f64(); + /// assert!(result.is_err()); + /// + /// let invalid_str_jsonb = r#""abc""#.parse::().unwrap(); + /// let result = invalid_str_jsonb.as_raw().to_f64(); + /// assert!(result.is_err()); + /// + /// // Invalid JSONB + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.to_f64(); + /// assert!(result.is_err()); + /// ``` + pub fn to_f64(&self) -> Result { + if let Some(v) = self.as_f64()? { + return Ok(v); + } else if let Some(v) = self.as_bool()? { + if v { + return Ok(1_f64); + } else { + return Ok(0_f64); + } + } else if let Some(v) = self.as_str()? { + if let Ok(v) = v.parse::() { + return Ok(v); + } + } + Err(Error::InvalidCast) + } + + /// Checks if the JSONB value is a string. + /// + /// This function checks if the JSONB value represents a JSON string. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(true)` - If the value is a string. + /// * `Ok(false)` - If the value is not a string. + /// * `Err(Error)` - If the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // String value + /// let str_jsonb = r#""hello""#.parse::().unwrap(); + /// let raw_str = str_jsonb.as_raw(); + /// assert!(raw_str.is_string().unwrap()); + /// + /// // Non-string values + /// let num_jsonb = "123".parse::().unwrap(); + /// let raw_num = num_jsonb.as_raw(); + /// assert!(!raw_num.is_string().unwrap()); + /// + /// let bool_jsonb = "true".parse::().unwrap(); + /// let raw_bool = bool_jsonb.as_raw(); + /// assert!(!raw_bool.is_string().unwrap()); + /// + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_arr = arr_jsonb.as_raw(); + /// assert!(!raw_arr.is_string().unwrap()); + /// + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let raw_obj = obj_jsonb.as_raw(); + /// assert!(!raw_obj.is_string().unwrap()); + /// + /// let null_jsonb = "null".parse::().unwrap(); + /// let raw_null = null_jsonb.as_raw(); + /// assert!(!raw_null.is_string().unwrap()); + /// + /// // Invalid JSONB + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.is_string(); + /// assert!(result.is_err()); + /// ``` + pub fn is_string(&self) -> Result { + self.as_str().map(|v| v.is_some()) + } + + /// Extracts a string from a JSONB value. + /// + /// This function attempts to extract a string from the JSONB value. If the JSONB value is a string, it returns the string as a `Cow<'_, str>`. Otherwise, it returns `None`. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(Some(Cow<'_, str>))` - If the value is a string, the extracted string. + /// * `Ok(None)` - If the value is not a string (number, boolean, null, array, object). + /// * `Err(Error)` - If the JSONB data is invalid or if the string is not valid UTF-8. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// use std::borrow::Cow; + /// + /// // String value + /// let str_jsonb = r#""hello""#.parse::().unwrap(); + /// let raw_str = str_jsonb.as_raw(); + /// assert_eq!(raw_str.as_str().unwrap(), Some(Cow::Borrowed("hello"))); + /// + /// // Non-string values + /// let num_jsonb = "123".parse::().unwrap(); + /// let raw_num = num_jsonb.as_raw(); + /// assert_eq!(raw_num.as_str().unwrap(), None); + /// + /// let bool_jsonb = "true".parse::().unwrap(); + /// let raw_bool = bool_jsonb.as_raw(); + /// assert_eq!(raw_bool.as_str().unwrap(), None); + /// + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_arr = arr_jsonb.as_raw(); + /// assert_eq!(raw_arr.as_str().unwrap(), None); + /// + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let raw_obj = obj_jsonb.as_raw(); + /// assert_eq!(raw_obj.as_str().unwrap(), None); + /// + /// let null_jsonb = "null".parse::().unwrap(); + /// let raw_null = null_jsonb.as_raw(); + /// assert_eq!(raw_null.as_str().unwrap(), None); + /// + /// // Invalid JSONB + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.as_str(); + /// assert!(result.is_err()); + /// + /// // Invalid UTF-8 (this will panic in the unsafe block of the original code!) + /// let invalid_utf8_jsonb = OwnedJsonb::new(vec![10, 0, 0, 0, 16, 0, 0, 0, 0, 150, 151]); // Invalid UTF-8 bytes + /// let invalid_raw_utf8_jsonb = invalid_utf8_jsonb.as_raw(); + /// let result = invalid_raw_utf8_jsonb.as_str(); + /// assert!(result.is_err()); + /// ``` + pub fn as_str(&self) -> Result>, Error> { + let header = read_u32(self.data, 0)?; + match header & CONTAINER_HEADER_TYPE_MASK { + SCALAR_CONTAINER_TAG => { + let jentry_encoded = read_u32(self.data, 4)?; + let jentry = JEntry::decode_jentry(jentry_encoded); + match jentry.type_code { + STRING_TAG => { + let length = jentry.length as usize; + let s = unsafe { std::str::from_utf8_unchecked(&self.data[8..8 + length]) }; + Ok(Some(Cow::Borrowed(s))) + } + NULL_TAG | NUMBER_TAG | FALSE_TAG | TRUE_TAG | CONTAINER_TAG => Ok(None), + _ => Err(Error::InvalidJsonb), + } + } + OBJECT_CONTAINER_TAG | ARRAY_CONTAINER_TAG => Ok(None), + _ => Err(Error::InvalidJsonb), + } + } + + /// Converts a JSONB value to a String. + /// + /// This function attempts to convert a JSONB value to a string representation. It prioritizes direct conversion from strings. Booleans are converted to "true" or "false", and numbers are converted to their string representations. Other types (arrays, objects, null) will result in an error. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(String)` - The string representation of the JSONB value. + /// * `Err(Error::InvalidCast)` - If the JSONB value cannot be converted to a string (e.g., it's an array, an object, or a null value). + /// * `Err(Error)` - If the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // String value + /// let str_jsonb = r#""hello""#.parse::().unwrap(); + /// assert_eq!(str_jsonb.as_raw().to_str().unwrap(), "hello"); + /// + /// // Number value + /// let num_jsonb = "123.45".parse::().unwrap(); + /// assert_eq!(num_jsonb.as_raw().to_str().unwrap(), "123.45"); + /// + /// // Boolean values + /// let true_jsonb = "true".parse::().unwrap(); + /// assert_eq!(true_jsonb.as_raw().to_str().unwrap(), "true"); + /// + /// let false_jsonb = "false".parse::().unwrap(); + /// assert_eq!(false_jsonb.as_raw().to_str().unwrap(), "false"); + /// + /// // Invalid conversions + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let result = arr_jsonb.as_raw().to_str(); + /// assert!(result.is_err()); + /// + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let result = obj_jsonb.as_raw().to_str(); + /// assert!(result.is_err()); + /// + /// let null_jsonb = "null".parse::().unwrap(); + /// let result = null_jsonb.as_raw().to_str(); + /// assert!(result.is_err()); + /// + /// // Invalid JSONB + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.to_str(); + /// assert!(result.is_err()); + /// ``` + pub fn to_str(&self) -> Result { + if let Some(v) = self.as_str()? { + return Ok(v.to_string()); + } else if let Some(v) = self.as_bool()? { + if v { + return Ok("true".to_string()); + } else { + return Ok("false".to_string()); + } + } else if let Some(v) = self.as_number()? { + return Ok(format!("{}", v)); + } + Err(Error::InvalidCast) + } + + /// Checks if the JSONB value is an array. + /// + /// This function checks if the JSONB value represents a JSON array. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(true)` - If the value is an array. + /// * `Ok(false)` - If the value is not an array (number, string, boolean, null, object). + /// * `Err(Error)` - If the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // Array value + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_arr = arr_jsonb.as_raw(); + /// assert!(raw_arr.is_array().unwrap()); + /// + /// // Non-array values + /// let num_jsonb = "123".parse::().unwrap(); + /// let raw_num = num_jsonb.as_raw(); + /// assert!(!raw_num.is_array().unwrap()); + /// + /// let bool_jsonb = "true".parse::().unwrap(); + /// let raw_bool = bool_jsonb.as_raw(); + /// assert!(!raw_bool.is_array().unwrap()); + /// + /// let str_jsonb = r#""hello""#.parse::().unwrap(); + /// let raw_str = str_jsonb.as_raw(); + /// assert!(!raw_str.is_array().unwrap()); + /// + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let raw_obj = obj_jsonb.as_raw(); + /// assert!(!raw_obj.is_array().unwrap()); + /// + /// let null_jsonb = "null".parse::().unwrap(); + /// let raw_null = null_jsonb.as_raw(); + /// assert!(!raw_null.is_array().unwrap()); + /// + /// // Invalid JSONB + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.is_array(); + /// assert!(result.is_err()); + /// ``` + pub fn is_array(&self) -> Result { + let header = read_u32(self.data, 0)?; + match header & CONTAINER_HEADER_TYPE_MASK { + ARRAY_CONTAINER_TAG => Ok(true), + SCALAR_CONTAINER_TAG | OBJECT_CONTAINER_TAG => Ok(false), + _ => Err(Error::InvalidJsonb), + } + } + + /// Checks if the JSONB value is an object. + /// + /// This function checks if the JSONB value represents a JSON object. + /// + /// # Arguments + /// + /// * `self` - The JSONB value. + /// + /// # Returns + /// + /// * `Ok(true)` - If the value is an object. + /// * `Ok(false)` - If the value is not an object (number, string, boolean, null, array). + /// * `Err(Error)` - If the JSONB data is invalid. + /// + /// # Examples + /// + /// ```rust + /// use jsonb::OwnedJsonb; + /// + /// // Object value + /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); + /// let raw_obj = obj_jsonb.as_raw(); + /// assert!(raw_obj.is_object().unwrap()); + /// + /// // Non-object values + /// let num_jsonb = "123".parse::().unwrap(); + /// let raw_num = num_jsonb.as_raw(); + /// assert!(!raw_num.is_object().unwrap()); + /// + /// let bool_jsonb = "true".parse::().unwrap(); + /// let raw_bool = bool_jsonb.as_raw(); + /// assert!(!raw_bool.is_object().unwrap()); + /// + /// let str_jsonb = r#""hello""#.parse::().unwrap(); + /// let raw_str = str_jsonb.as_raw(); + /// assert!(!raw_str.is_object().unwrap()); + /// + /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); + /// let raw_arr = arr_jsonb.as_raw(); + /// assert!(!raw_arr.is_object().unwrap()); + /// + /// let null_jsonb = "null".parse::().unwrap(); + /// let raw_null = null_jsonb.as_raw(); + /// assert!(!raw_null.is_object().unwrap()); + /// + /// // Invalid JSONB + /// let invalid_jsonb = OwnedJsonb::new(vec![1, 2, 3, 4]); + /// let invalid_raw_jsonb = invalid_jsonb.as_raw(); + /// let result = invalid_raw_jsonb.is_object(); + /// assert!(result.is_err()); + /// ``` + pub fn is_object(&self) -> Result { + let header = read_u32(self.data, 0)?; + match header & CONTAINER_HEADER_TYPE_MASK { + OBJECT_CONTAINER_TAG => Ok(true), + SCALAR_CONTAINER_TAG | ARRAY_CONTAINER_TAG => Ok(false), + _ => Err(Error::InvalidJsonb), + } + } +} diff --git a/src/iterator.rs b/src/iterator.rs index f904cfa..2b3189e 100644 --- a/src/iterator.rs +++ b/src/iterator.rs @@ -157,7 +157,7 @@ impl<'a> Iterator for ObjectEntryIterator<'a> { } } -impl<'a> ObjectEntryIterator<'a> { +impl ObjectEntryIterator<'_> { fn fill_keys(&mut self) { let mut keys: VecDeque = VecDeque::with_capacity(self.length); for _ in 0..self.length { diff --git a/src/jsonpath/path.rs b/src/jsonpath/path.rs index b5a33f9..3bb7ab4 100644 --- a/src/jsonpath/path.rs +++ b/src/jsonpath/path.rs @@ -25,7 +25,7 @@ pub struct JsonPath<'a> { pub paths: Vec>, } -impl<'a> JsonPath<'a> { +impl JsonPath<'_> { pub fn is_predicate(&self) -> bool { self.paths.len() == 1 && matches!(self.paths[0], Path::Predicate(_)) } @@ -183,7 +183,7 @@ pub enum FilterFunc<'a> { StartsWith(Cow<'a, str>), } -impl<'a> Display for JsonPath<'a> { +impl Display for JsonPath<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { for path in &self.paths { write!(f, "{path}")?; @@ -229,7 +229,7 @@ impl Display for ArrayIndex { } } -impl<'a> Display for Path<'a> { +impl Display for Path<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Path::Root => { @@ -277,7 +277,7 @@ impl<'a> Display for Path<'a> { } } -impl<'a> Display for PathValue<'a> { +impl Display for PathValue<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { PathValue::Null => { @@ -354,7 +354,7 @@ impl Display for BinaryArithmeticOperator { } } -impl<'a> Display for Expr<'a> { +impl Display for Expr<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Expr::Paths(paths) => { diff --git a/src/jsonpath/selector.rs b/src/jsonpath/selector.rs index 39f09e9..802bff1 100644 --- a/src/jsonpath/selector.rs +++ b/src/jsonpath/selector.rs @@ -30,6 +30,8 @@ use crate::jsonpath::Path; use crate::jsonpath::PathValue; use crate::number::Number; use crate::Error; +use crate::OwnedJsonb; +use crate::RawJsonb; use nom::{ bytes::complete::take, combinator::map, multi::count, number::complete::be_u32, IResult, @@ -65,47 +67,42 @@ pub enum Mode { } pub struct Selector<'a> { - json_path: JsonPath<'a>, + json_path: &'a JsonPath<'a>, mode: Mode, } impl<'a> Selector<'a> { - pub fn new(json_path: JsonPath<'a>, mode: Mode) -> Self { + pub fn new(json_path: &'a JsonPath<'a>, mode: Mode) -> Self { Self { json_path, mode } } - pub fn select( - &'a self, - root: &'a [u8], - data: &mut Vec, - offsets: &mut Vec, - ) -> Result<(), Error> { + pub fn select(&'a self, root: RawJsonb) -> Result, Error> { let mut poses = self.find_positions(root, None, &self.json_path.paths)?; if self.json_path.is_predicate() { - Self::build_predicate_result(&mut poses, data)?; - return Ok(()); + let owned_jsonbs = Self::build_predicate_result(&mut poses)?; + return Ok(owned_jsonbs); } - match self.mode { - Mode::All => Self::build_values(root, &mut poses, data, offsets)?, + let owned_jsonbs = match self.mode { + Mode::All => Self::build_values(root, &mut poses)?, Mode::First => { poses.truncate(1); - Self::build_values(root, &mut poses, data, offsets)? + Self::build_values(root, &mut poses)? } - Mode::Array => Self::build_scalar_array(root, &mut poses, data, offsets)?, + Mode::Array => Self::build_scalar_array(root, &mut poses)?, Mode::Mixed => { if poses.len() > 1 { - Self::build_scalar_array(root, &mut poses, data, offsets)? + Self::build_scalar_array(root, &mut poses)? } else { - Self::build_values(root, &mut poses, data, offsets)? + Self::build_values(root, &mut poses)? } } - } - Ok(()) + }; + Ok(owned_jsonbs) } - pub fn exists(&'a self, root: &'a [u8]) -> Result { + pub fn exists(&'a self, root: RawJsonb) -> Result { if self.json_path.is_predicate() { return Ok(true); } @@ -113,7 +110,7 @@ impl<'a> Selector<'a> { Ok(!poses.is_empty()) } - pub fn predicate_match(&'a self, root: &'a [u8]) -> Result { + pub fn predicate_match(&'a self, root: RawJsonb) -> Result { if !self.json_path.is_predicate() { return Err(Error::InvalidJsonPathPredicate); } @@ -123,7 +120,7 @@ impl<'a> Selector<'a> { fn find_positions( &'a self, - root: &'a [u8], + root: RawJsonb, current: Option<&Position>, paths: &[Path<'a>], ) -> Result, Error> { @@ -175,7 +172,7 @@ impl<'a> Selector<'a> { fn select_path( &'a self, - root: &'a [u8], + root: RawJsonb, offset: usize, length: usize, path: &Path<'a>, @@ -202,11 +199,11 @@ impl<'a> Selector<'a> { // select all values in an Object. fn select_object_values( &'a self, - root: &'a [u8], + root: RawJsonb, root_offset: usize, poses: &mut VecDeque, ) -> Result<(), Error> { - let (rest, (ty, length)) = decode_header(&root[root_offset..])?; + let (rest, (ty, length)) = decode_header(&root.data[root_offset..])?; if ty != OBJECT_CONTAINER_TAG || length == 0 { return Ok(()); } @@ -231,12 +228,12 @@ impl<'a> Selector<'a> { // select all values in an Array. fn select_array_values( &'a self, - root: &'a [u8], + root: RawJsonb, root_offset: usize, root_length: usize, poses: &mut VecDeque, ) -> Result<(), Error> { - let (rest, (ty, length)) = decode_header(&root[root_offset..])?; + let (rest, (ty, length)) = decode_header(&root.data[root_offset..])?; if ty != ARRAY_CONTAINER_TAG { // In lax mode, bracket wildcard allow Scalar value. poses.push_back(Position::Container((root_offset, root_length))); @@ -259,12 +256,12 @@ impl<'a> Selector<'a> { // select value in an Object by key name. fn select_by_name( &'a self, - root: &'a [u8], + root: RawJsonb, root_offset: usize, name: &str, poses: &mut VecDeque, ) -> Result<(), Error> { - let (rest, (ty, length)) = decode_header(&root[root_offset..])?; + let (rest, (ty, length)) = decode_header(&root.data[root_offset..])?; if ty != OBJECT_CONTAINER_TAG || length == 0 { return Ok(()); } @@ -278,7 +275,7 @@ impl<'a> Selector<'a> { offset += jlength; continue; } - let (_, key) = decode_string(&root[offset..], *jlength)?; + let (_, key) = decode_string(&root.data[offset..], *jlength)?; if name == unsafe { std::str::from_utf8_unchecked(key) } { found = true; idx = i; @@ -307,12 +304,12 @@ impl<'a> Selector<'a> { // select values in an Array by indices. fn select_by_indices( &'a self, - root: &'a [u8], + root: RawJsonb, root_offset: usize, indices: &Vec, poses: &mut VecDeque, ) -> Result<(), Error> { - let (rest, (ty, length)) = decode_header(&root[root_offset..])?; + let (rest, (ty, length)) = decode_header(&root.data[root_offset..])?; if ty != ARRAY_CONTAINER_TAG || length == 0 { return Ok(()); } @@ -354,50 +351,47 @@ impl<'a> Selector<'a> { Ok(()) } - fn build_predicate_result( - poses: &mut VecDeque, - data: &mut Vec, - ) -> Result<(), Error> { + fn build_predicate_result(poses: &mut VecDeque) -> Result, Error> { let jentry = match poses.pop_front() { Some(_) => TRUE_TAG, None => FALSE_TAG, }; + let mut data = Vec::with_capacity(8); data.write_u32::(SCALAR_CONTAINER_TAG)?; data.write_u32::(jentry)?; - Ok(()) + Ok(vec![OwnedJsonb::new(data)]) } fn build_values( - root: &'a [u8], + root: RawJsonb, poses: &mut VecDeque, - data: &mut Vec, - offsets: &mut Vec, - ) -> Result<(), Error> { + ) -> Result, Error> { + let mut owned_jsonbs = Vec::with_capacity(poses.len()); while let Some(pos) = poses.pop_front() { + let mut data = Vec::new(); match pos { Position::Container((offset, length)) => { - data.extend_from_slice(&root[offset..offset + length]); + data.extend_from_slice(&root.data[offset..offset + length]); } Position::Scalar((ty, offset, length)) => { data.write_u32::(SCALAR_CONTAINER_TAG)?; let jentry = ty | length as u32; data.write_u32::(jentry)?; if length > 0 { - data.extend_from_slice(&root[offset..offset + length]); + data.extend_from_slice(&root.data[offset..offset + length]); } } } - offsets.push(data.len() as u64); + owned_jsonbs.push(OwnedJsonb::new(data)); } - Ok(()) + Ok(owned_jsonbs) } fn build_scalar_array( - root: &'a [u8], + root: RawJsonb, poses: &mut VecDeque, - data: &mut Vec, - offsets: &mut Vec, - ) -> Result<(), Error> { + ) -> Result, Error> { + let mut data = Vec::new(); let len = poses.len(); let header = ARRAY_CONTAINER_TAG | len as u32; // write header. @@ -408,12 +402,12 @@ impl<'a> Selector<'a> { while let Some(pos) = poses.pop_front() { let jentry = match pos { Position::Container((offset, length)) => { - data.extend_from_slice(&root[offset..offset + length]); + data.extend_from_slice(&root.data[offset..offset + length]); CONTAINER_TAG | length as u32 } Position::Scalar((ty, offset, length)) => { if length > 0 { - data.extend_from_slice(&root[offset..offset + length]); + data.extend_from_slice(&root.data[offset..offset + length]); } ty | length as u32 } @@ -423,8 +417,7 @@ impl<'a> Selector<'a> { } jentry_offset += 4; } - offsets.push(data.len() as u64); - Ok(()) + Ok(vec![OwnedJsonb::new(data)]) } // check and convert index to Array index. @@ -465,7 +458,7 @@ impl<'a> Selector<'a> { fn filter_expr( &'a self, - root: &'a [u8], + root: RawJsonb, pos: &Position, expr: &Expr<'a>, ) -> Result { @@ -498,7 +491,7 @@ impl<'a> Selector<'a> { fn eval_exists( &'a self, - root: &'a [u8], + root: RawJsonb, pos: &Position, paths: &[Path<'a>], ) -> Result { @@ -509,7 +502,7 @@ impl<'a> Selector<'a> { fn eval_starts_with( &'a self, - _root: &'a [u8], + _root: RawJsonb, _pos: &Position, _prefix: &str, ) -> Result { @@ -519,7 +512,7 @@ impl<'a> Selector<'a> { fn convert_expr_val( &'a self, - root: &'a [u8], + root: RawJsonb, pos: &Position, expr: Expr<'a>, ) -> Result, Error> { @@ -567,11 +560,11 @@ impl<'a> Selector<'a> { TRUE_TAG => PathValue::Boolean(true), FALSE_TAG => PathValue::Boolean(false), NUMBER_TAG => { - let n = Number::decode(&root[offset..offset + length])?; + let n = Number::decode(&root.data[offset..offset + length])?; PathValue::Number(n) } STRING_TAG => { - let v = &root[offset..offset + length]; + let v = &root.data[offset..offset + length]; PathValue::String(Cow::Owned(unsafe { String::from_utf8_unchecked(v.to_vec()) })) diff --git a/src/keypath.rs b/src/keypath.rs index 05e468c..28d6993 100644 --- a/src/keypath.rs +++ b/src/keypath.rs @@ -47,7 +47,7 @@ pub enum KeyPath<'a> { Name(Cow<'a, str>), } -impl<'a> Display for KeyPaths<'a> { +impl Display for KeyPaths<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{{")?; for (i, path) in self.paths.iter().enumerate() { @@ -61,7 +61,7 @@ impl<'a> Display for KeyPaths<'a> { } } -impl<'a> Display for KeyPath<'a> { +impl Display for KeyPath<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { KeyPath::Index(idx) => { diff --git a/src/lazy_value.rs b/src/lazy_value.rs index a7f6148..f6504e6 100644 --- a/src/lazy_value.rs +++ b/src/lazy_value.rs @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::RawJsonb; use std::borrow::Cow; use std::fmt::Debug; -use crate::array_length; use crate::ser::Encoder; use crate::Value; @@ -54,12 +54,15 @@ impl<'a> LazyValue<'a> { pub fn array_length(&self) -> Option { match self { LazyValue::Value(Value::Array(arr)) => Some(arr.len()), - LazyValue::Raw(cow) => array_length(cow.as_ref()), + LazyValue::Raw(cow) => { + let raw_jsonb = RawJsonb::new(cow.as_ref()); + raw_jsonb.array_length().ok()? + } _ => None, } } - pub fn to_value(&'a self) -> Cow> { + pub fn to_value(&'a self) -> Cow<'a, Value<'a>> { match self { LazyValue::Value(v) => Cow::Borrowed(v), LazyValue::Raw(v) => Cow::Owned(crate::from_slice(v.as_ref()).unwrap()), diff --git a/src/lib.rs b/src/lib.rs index 603625e..5025aff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,7 +75,9 @@ pub mod jsonpath; pub mod keypath; mod lazy_value; mod number; +mod owned; mod parser; +mod raw; mod ser; mod util; mod value; @@ -84,9 +86,10 @@ pub use de::{from_slice, parse_jsonb}; pub use error::Error; #[allow(unused_imports)] pub use from::*; -pub use functions::*; pub use lazy_value::*; pub use number::Number; +pub use owned::OwnedJsonb; pub use parser::parse_lazy_value; pub use parser::parse_value; +pub use raw::RawJsonb; pub use value::*; diff --git a/src/owned.rs b/src/owned.rs new file mode 100644 index 0000000..f05e0ab --- /dev/null +++ b/src/owned.rs @@ -0,0 +1,129 @@ +// Copyright 2023 Datafuse Labs. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::error::Error; +use crate::parse_value; +use crate::RawJsonb; +use std::fmt::Display; +use std::str::FromStr; + +/// Represents a JSONB data that owns its underlying data. +/// +/// This struct provides ownership over the binary JSONB representation. +/// `OwnedJsonb` is primarily used to create JSONB data from other data types (such as JSON String). +/// However, for most operations, it's necessary to convert an `OwnedJsonb` to a `RawJsonb` using the `as_raw()` method +/// to avoid unnecessary copying and to take advantage of the performance benefits of the read-only access of the `RawJsonb`. +#[derive(Debug, Clone, PartialEq)] +pub struct OwnedJsonb { + /// The underlying `Vec` containing the binary JSONB data. + pub(crate) data: Vec, +} + +impl OwnedJsonb { + /// Creates a new OwnedJsonb from a Vec. + /// + /// # Arguments + /// + /// * `data` - The `Vec` containing the JSONB data. + /// + /// # Returns + /// + /// A new `OwnedJsonb` instance. + pub fn new(data: Vec) -> OwnedJsonb { + Self { data } + } + + /// Creates a `RawJsonb` view of the owned data. + /// This is useful for passing the data to functions that expect a `RawJsonb`. + /// This does *not* transfer ownership. + /// + /// # Returns + /// + /// A `RawJsonb` instance referencing the owned data. + pub fn as_raw(&self) -> RawJsonb<'_> { + RawJsonb::new(self.data.as_slice()) + } + + /// Consumes the OwnedJsonb and returns the underlying Vec. + /// + /// # Returns + /// + /// The underlying `Vec` containing the JSONB data. + pub fn to_vec(self) -> Vec { + self.data + } + + /// Checks if the JSONB data is empty. + /// + /// # Returns + /// + /// `true` if the data is empty, `false` otherwise. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Returns the length of the JSONB data in bytes. + /// + /// # Returns + /// + /// The length of the data in bytes. + pub fn len(&self) -> usize { + self.data.len() + } +} + +/// Creates an `OwnedJsonb` from a borrowed byte slice. The byte slice is copied into a new `Vec`. +impl From<&[u8]> for OwnedJsonb { + fn from(data: &[u8]) -> Self { + Self { + data: data.to_vec(), + } + } +} + +/// Creates an `OwnedJsonb` from a `Vec`. This is a simple ownership transfer. +impl From> for OwnedJsonb { + fn from(data: Vec) -> Self { + Self { data } + } +} + +/// Parses a string into an `OwnedJsonb`. +/// The string is parsed into a JSON value, then encoded into the binary JSONB format. +impl FromStr for OwnedJsonb { + type Err = Error; + + fn from_str(s: &str) -> Result { + let value = parse_value(s.as_bytes())?; + let mut data = Vec::new(); + value.write_to_vec(&mut data); + Ok(Self { data }) + } +} + +/// Allows accessing the underlying byte slice as a reference. +/// This enables easy integration with functions that expect a `&[u8]`. +impl AsRef<[u8]> for OwnedJsonb { + fn as_ref(&self) -> &[u8] { + self.data.as_ref() + } +} + +/// Implements the Display trait, allowing OwnedJsonb to be formatted as a string using the `{}` format specifier. +impl Display for OwnedJsonb { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let raw_jsonb = self.as_raw(); + write!(f, "{}", raw_jsonb.to_string()) + } +} diff --git a/src/parser.rs b/src/parser.rs index b1edbfd..71a5610 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -14,7 +14,6 @@ use std::borrow::Cow; -use crate::is_jsonb; use crate::lazy_value::LazyValue; use super::constants::*; @@ -387,3 +386,14 @@ impl<'a> Parser<'a> { Ok(Value::Object(obj)) } } + +// Check whether the value is `JSONB` format, +// for compatibility with previous `JSON` string. +fn is_jsonb(value: &[u8]) -> bool { + if let Some(v) = value.first() { + if matches!(*v, ARRAY_PREFIX | OBJECT_PREFIX | SCALAR_PREFIX) { + return true; + } + } + false +} diff --git a/src/raw.rs b/src/raw.rs new file mode 100644 index 0000000..6c6f50b --- /dev/null +++ b/src/raw.rs @@ -0,0 +1,73 @@ +// Copyright 2023 Datafuse Labs. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// Represents JSONB data wrapped around a raw, immutable slice of bytes. +/// +/// It does not own the underlying data, allowing various operations to be performed on the JSONB data *without copying*. +/// This is critical for performance when dealing with large JSONB values. +/// `RawJsonb` provides various methods to inspect and manipulate the JSONB data efficiently. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct RawJsonb<'a> { + /// The underlying byte slice representing the JSONB data. + pub(crate) data: &'a [u8], +} + +impl<'a> RawJsonb<'a> { + /// Creates a new RawJsonb from a byte slice. + /// + /// # Arguments + /// + /// * `data` - The byte slice containing the JSONB data. + /// + /// # Returns + /// + /// A new `RawJsonb` instance. + pub fn new(data: &'a [u8]) -> Self { + Self { data } + } + + /// Checks if the JSONB data is empty. + /// + /// # Returns + /// + /// `true` if the data is empty, `false` otherwise. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Returns the length of the JSONB data in bytes. + /// + /// # Returns + /// + /// The length of the data in bytes. + pub fn len(&self) -> usize { + self.data.as_ref().len() + } +} + +/// Converts a borrowed byte slice into a RawJsonb. +/// This provides a convenient way to create a RawJsonb from existing data without copying. +impl<'a> From<&'a [u8]> for RawJsonb<'a> { + fn from(data: &'a [u8]) -> Self { + Self { data } + } +} + +/// Allows accessing the underlying byte slice as a reference. +/// This enables easy integration with functions that expect a &[u8]. +impl AsRef<[u8]> for RawJsonb<'_> { + fn as_ref(&self) -> &[u8] { + self.data + } +} diff --git a/src/value.rs b/src/value.rs index 7869e17..1491ed7 100644 --- a/src/value.rs +++ b/src/value.rs @@ -21,6 +21,10 @@ use std::mem::discriminant; use super::number::Number; use super::ser::Encoder; +use rand::distributions::Alphanumeric; +use rand::distributions::DistString; +use rand::thread_rng; +use rand::Rng; pub type Object<'a> = BTreeMap>; @@ -36,7 +40,7 @@ pub enum Value<'a> { Object(Object<'a>), } -impl<'a> Debug for Value<'a> { +impl Debug for Value<'_> { fn fmt(&self, formatter: &mut Formatter) -> std::fmt::Result { match *self { Value::Null => formatter.debug_tuple("Null").finish(), @@ -57,7 +61,7 @@ impl<'a> Debug for Value<'a> { } } -impl<'a> Display for Value<'a> { +impl Display for Value<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Value::Null => write!(f, "null"), @@ -261,4 +265,61 @@ impl<'a> Value<'a> { pub fn eq_variant(&self, other: &Value) -> bool { discriminant(self) == discriminant(other) } + + /// generate random JSONB value + pub fn rand_value() -> Value<'static> { + let mut rng = thread_rng(); + let val = match rng.gen_range(0..=2) { + 0 => { + let len = rng.gen_range(0..=5); + let mut values = Vec::with_capacity(len); + for _ in 0..len { + values.push(Self::rand_scalar_value()); + } + Value::Array(values) + } + 1 => { + let len = rng.gen_range(0..=5); + let mut obj = Object::new(); + for _ in 0..len { + let k = Alphanumeric.sample_string(&mut rng, 5); + let v = Self::rand_scalar_value(); + obj.insert(k, v); + } + Value::Object(obj) + } + _ => Self::rand_scalar_value(), + }; + val + } + + fn rand_scalar_value() -> Value<'static> { + let mut rng = thread_rng(); + let val = match rng.gen_range(0..=3) { + 0 => { + let v = rng.gen_bool(0.5); + Value::Bool(v) + } + 1 => { + let s = Alphanumeric.sample_string(&mut rng, 5); + Value::String(Cow::from(s)) + } + 2 => match rng.gen_range(0..=2) { + 0 => { + let n: u64 = rng.gen_range(0..=100000); + Value::Number(Number::UInt64(n)) + } + 1 => { + let n: i64 = rng.gen_range(-100000..=100000); + Value::Number(Number::Int64(n)) + } + _ => { + let n: f64 = rng.gen_range(-4000.0..1.3e5); + Value::Number(Number::Float64(n)) + } + }, + _ => Value::Null, + }; + val + } } diff --git a/tests/it/functions.rs b/tests/it/functions.rs index d2119ac..91d6497 100644 --- a/tests/it/functions.rs +++ b/tests/it/functions.rs @@ -17,18 +17,16 @@ use std::cmp::Ordering; use std::collections::BTreeMap; use std::collections::BTreeSet; -use jsonb::{ - array_distinct, array_except, array_insert, array_intersection, array_length, array_overlap, - array_values, as_bool, as_null, as_number, as_str, build_array, build_object, compare, concat, - contains, convert_to_comparable, delete_by_index, delete_by_keypath, delete_by_name, - exists_all_keys, exists_any_keys, from_slice, get_by_index, get_by_keypath, get_by_name, - get_by_path, get_by_path_array, is_array, is_object, keypath::parse_key_paths, object_delete, - object_each, object_insert, object_keys, object_pick, parse_value, path_exists, path_match, - strip_nulls, to_bool, to_f64, to_i64, to_pretty_string, to_serde_json, to_serde_json_object, - to_str, to_string, to_u64, traverse_check_string, type_of, Error, Number, Object, Value, -}; - +use jsonb::from_slice; use jsonb::jsonpath::parse_json_path; +use jsonb::jsonpath::Mode; +use jsonb::keypath::parse_key_paths; +use jsonb::parse_value; +use jsonb::Error; +use jsonb::Number; +use jsonb::Object; +use jsonb::OwnedJsonb; +use jsonb::Value; use nom::AsBytes; #[test] @@ -41,30 +39,24 @@ fn test_build_array() { r#"{"k":"v"}"#, ]; let mut expect_array = Vec::with_capacity(sources.len()); - let mut offsets = Vec::with_capacity(sources.len()); - let mut buf: Vec = Vec::new(); + let mut owned_jsonbs = Vec::with_capacity(sources.len()); for s in sources { let value = parse_value(s.as_bytes()).unwrap(); expect_array.push(value.clone()); + let mut buf: Vec = Vec::new(); value.write_to_vec(&mut buf); - offsets.push(buf.len()); - } - let mut values = Vec::with_capacity(offsets.len()); - let mut last_offset = 0; - for offset in offsets { - values.push(&buf[last_offset..offset]); - last_offset = offset; + let owned_jsonb = OwnedJsonb::new(buf); + owned_jsonbs.push(owned_jsonb); } let expect_value = Value::Array(expect_array); let mut expect_buf: Vec = Vec::new(); expect_value.write_to_vec(&mut expect_buf); - let mut arr_buf = Vec::new(); - build_array(values, &mut arr_buf).unwrap(); - assert_eq!(arr_buf, expect_buf); + let owned_arr = OwnedJsonb::build_array(owned_jsonbs.iter().map(|v| v.as_raw())).unwrap(); + assert_eq!(owned_arr.as_ref(), &expect_buf); - let value = from_slice(&arr_buf).unwrap(); + let value = from_slice(owned_arr.as_ref()).unwrap(); assert!(value.is_array()); let array = value.as_array().unwrap(); assert_eq!(array.len(), 5); @@ -87,33 +79,27 @@ fn test_build_object() { "k5".to_string(), ]; - let mut buf: Vec = Vec::new(); - let mut offsets = Vec::with_capacity(sources.len()); let mut expect_object = Object::new(); + let mut owned_jsonbs = Vec::with_capacity(sources.len()); for (key, s) in keys.iter().zip(sources.iter()) { let value = parse_value(s.as_bytes()).unwrap(); expect_object.insert(key.clone(), value.clone()); + let mut buf: Vec = Vec::new(); value.write_to_vec(&mut buf); - offsets.push(buf.len()); - } - - let mut values = Vec::with_capacity(offsets.len()); - let mut last_offset = 0; - for (key, offset) in keys.iter().zip(offsets.iter()) { - values.push((key.as_str(), &buf[last_offset..*offset])); - last_offset = *offset; + let owned_jsonb = OwnedJsonb::new(buf); + owned_jsonbs.push((key.clone(), owned_jsonb)); } let expect_value = Value::Object(expect_object); let mut expect_buf: Vec = Vec::new(); expect_value.write_to_vec(&mut expect_buf); - let mut obj_buf = Vec::new(); - build_object(values, &mut obj_buf).unwrap(); - assert_eq!(obj_buf, expect_buf); + let owned_obj = + OwnedJsonb::build_object(owned_jsonbs.iter().map(|(k, v)| (k, v.as_raw()))).unwrap(); + assert_eq!(owned_obj.as_ref(), &expect_buf); - let value = from_slice(&obj_buf).unwrap(); + let value = from_slice(owned_obj.as_ref()).unwrap(); assert!(value.is_object()); let array = value.as_object().unwrap(); assert_eq!(array.len(), 5); @@ -130,15 +116,11 @@ fn test_array_length() { (r#"{"k":"v"}"#, None), ]; - let mut buf: Vec = Vec::new(); for (s, expect) in sources { - let res = array_length(s.as_bytes()); - assert_eq!(res, expect); - let value = parse_value(s.as_bytes()).unwrap(); - value.write_to_vec(&mut buf); - let res = array_length(&buf); - assert_eq!(res, expect); - buf.clear(); + let owned_jsonb = s.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); + let res = raw_jsonb.array_length(); + assert_eq!(res, Ok(expect)); } } @@ -161,19 +143,11 @@ fn test_path_exists() { (r#"{"a":1,"b":[1,2,3]}"#, r#"$.b[1 to last] > 1"#, true), ]; for (json, path, expect) in sources { - // Check from JSONB - { - let value = parse_value(json.as_bytes()).unwrap().to_vec(); - let json_path = parse_json_path(path.as_bytes()).unwrap(); - let res = path_exists(value.as_slice(), json_path); - assert_eq!(res, Ok(expect)); - } - // Check from String JSON - { - let json_path = parse_json_path(path.as_bytes()).unwrap(); - let res = path_exists(json.as_bytes(), json_path); - assert_eq!(res, Ok(expect)); - } + let owned_jsonb = json.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); + let json_path = parse_json_path(path.as_bytes()).unwrap(); + let res = raw_jsonb.path_exists(&json_path); + assert_eq!(res, Ok(expect)); } } @@ -229,20 +203,18 @@ fn test_path_exists_expr() { ), ]; - let mut buf: Vec = Vec::new(); - let value = parse_value(source.as_bytes()).unwrap(); - value.write_to_vec(&mut buf); - + let owned_jsonb = source.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); for (path, expected) in paths { - let mut out_buf: Vec = Vec::new(); - let mut out_offsets: Vec = Vec::new(); let json_path = parse_json_path(path.as_bytes()).unwrap(); - - let res = get_by_path_array(&buf, json_path, &mut out_buf, &mut out_offsets); + let res = raw_jsonb.get_by_path_opt(&json_path, Mode::Array); assert!(res.is_ok()); + let owned_jsonb_opt = res.unwrap(); + assert!(owned_jsonb_opt.is_some()); + let owned_jsonb = owned_jsonb_opt.unwrap(); let expected_buf = parse_value(expected.as_bytes()).unwrap().to_vec(); - assert_eq!(out_buf, expected_buf); + assert_eq!(owned_jsonb.to_vec(), expected_buf); } } @@ -305,41 +277,17 @@ fn test_get_by_path() { (r#"$.name == "Fred" && $.car_no == 123"#, vec!["true"]), ]; - let mut buf: Vec = Vec::new(); - let mut out_buf: Vec = Vec::new(); - let mut out_offsets: Vec = Vec::new(); - let value = parse_value(source.as_bytes()).unwrap(); - value.write_to_vec(&mut buf); + let owned_jsonb = source.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); for (path, expects) in paths { - out_buf.clear(); - out_offsets.clear(); let json_path = parse_json_path(path.as_bytes()).unwrap(); - let res = get_by_path(&buf, json_path, &mut out_buf, &mut out_offsets); + let res = raw_jsonb.get_by_path(&json_path, Mode::All); assert!(res.is_ok()); - if expects.is_empty() { - assert_eq!(out_offsets.len(), expects.len()); - } else if expects.len() == 1 { - let mut val_buf: Vec = Vec::new(); - let val_expect = parse_value(expects[0].as_bytes()).unwrap(); - val_expect.write_to_vec(&mut val_buf); - assert_eq!(out_buf, val_buf); - } else { - let mut offsets = Vec::with_capacity(expects.len()); - let mut val_buf: Vec = Vec::new(); - for expect in expects.iter() { - let val_expect = parse_value(expect.as_bytes()).unwrap(); - val_expect.write_to_vec(&mut val_buf); - offsets.push(val_buf.len()); - } - let mut values = Vec::with_capacity(offsets.len()); - let mut last_offset = 0; - for offset in offsets { - values.push(&val_buf[last_offset..offset]); - last_offset = offset; - } - let mut arr_buf = Vec::new(); - build_array(values, &mut arr_buf).unwrap(); - assert_eq!(out_buf, arr_buf); + let owned_jsonbs = res.unwrap(); + assert_eq!(owned_jsonbs.len(), expects.len()); + for (owned_jsonb, expect) in owned_jsonbs.into_iter().zip(expects.iter()) { + let expected_buf = parse_value(expect.as_bytes()).unwrap().to_vec(); + assert_eq!(owned_jsonb.to_vec(), expected_buf); } } } @@ -353,21 +301,21 @@ fn test_get_by_index() { (r#"["a","b","c"]"#, 0, Some(Value::String(Cow::from("a")))), ]; - let mut buf: Vec = Vec::new(); for (s, idx, expect) in sources { - let res = get_by_index(s.as_bytes(), idx); - match expect.clone() { - Some(expect) => assert_eq!(from_slice(&res.unwrap()).unwrap(), expect), - None => assert_eq!(res, None), - } - let value = parse_value(s.as_bytes()).unwrap(); - value.write_to_vec(&mut buf); - let res = get_by_index(&buf, idx); + let owned_jsonb = s.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); + let res = raw_jsonb.get_by_index(idx); + assert!(res.is_ok()); + let owned_jsonb_opt = res.unwrap(); match expect { - Some(expect) => assert_eq!(from_slice(&res.unwrap()).unwrap(), expect), - None => assert_eq!(res, None), + Some(expect) => { + assert!(owned_jsonb_opt.is_some()); + let owned_jsonb = owned_jsonb_opt.unwrap(); + let expected_buf = expect.to_vec(); + assert_eq!(owned_jsonb.to_vec(), expected_buf); + } + None => assert_eq!(owned_jsonb_opt, None), } - buf.clear(); } } @@ -389,21 +337,21 @@ fn test_get_by_name() { ), ]; - let mut buf: Vec = Vec::new(); for (s, name, expect) in sources { - let res = get_by_name(s.as_bytes(), &name, false); - match expect.clone() { - Some(expect) => assert_eq!(from_slice(&res.unwrap()).unwrap(), expect), - None => assert_eq!(res, None), - } - let value = parse_value(s.as_bytes()).unwrap(); - value.write_to_vec(&mut buf); - let res = get_by_name(&buf, &name, false); + let owned_jsonb = s.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); + let res = raw_jsonb.get_by_name(&name, false); + assert!(res.is_ok()); + let owned_jsonb_opt = res.unwrap(); match expect { - Some(expect) => assert_eq!(from_slice(&res.unwrap()).unwrap(), expect), - None => assert_eq!(res, None), + Some(expect) => { + assert!(owned_jsonb_opt.is_some()); + let owned_jsonb = owned_jsonb_opt.unwrap(); + let expected_buf = expect.to_vec(); + assert_eq!(owned_jsonb.to_vec(), expected_buf); + } + None => assert_eq!(owned_jsonb_opt, None), } - buf.clear(); } } @@ -425,21 +373,21 @@ fn test_get_by_name_ignore_case() { ), ]; - let mut buf: Vec = Vec::new(); for (s, name, expect) in sources { - let res = get_by_name(s.as_bytes(), &name, true); - match expect.clone() { - Some(expect) => assert_eq!(from_slice(&res.unwrap()).unwrap(), expect), - None => assert_eq!(res, None), - } - let value = parse_value(s.as_bytes()).unwrap(); - value.write_to_vec(&mut buf); - let res = get_by_name(&buf, &name, true); + let owned_jsonb = s.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); + let res = raw_jsonb.get_by_name(&name, true); + assert!(res.is_ok()); + let owned_jsonb_opt = res.unwrap(); match expect { - Some(expect) => assert_eq!(from_slice(&res.unwrap()).unwrap(), expect), - None => assert_eq!(res, None), + Some(expect) => { + assert!(owned_jsonb_opt.is_some()); + let owned_jsonb = owned_jsonb_opt.unwrap(); + let expected_buf = expect.to_vec(); + assert_eq!(owned_jsonb.to_vec(), expected_buf); + } + None => assert_eq!(owned_jsonb_opt, None), } - buf.clear(); } } @@ -463,21 +411,21 @@ fn test_object_keys() { ), ]; - let mut buf: Vec = Vec::new(); for (s, expect) in sources { - let res = object_keys(s.as_bytes()); - match expect.clone() { - Some(expect) => assert_eq!(from_slice(&res.unwrap()).unwrap(), expect), - None => assert_eq!(res, None), - } - let value = parse_value(s.as_bytes()).unwrap(); - value.write_to_vec(&mut buf); - let res = object_keys(&buf); + let owned_jsonb = s.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); + let res = raw_jsonb.object_keys(); + assert!(res.is_ok()); + let owned_jsonb_opt = res.unwrap(); match expect { - Some(expect) => assert_eq!(from_slice(&res.unwrap()).unwrap(), expect), - None => assert_eq!(res, None), + Some(expect) => { + assert!(owned_jsonb_opt.is_some()); + let owned_jsonb = owned_jsonb_opt.unwrap(); + let expected_buf = expect.to_vec(); + assert_eq!(owned_jsonb.to_vec(), expected_buf); + } + None => assert_eq!(owned_jsonb_opt, None), } - buf.clear(); } } @@ -489,43 +437,34 @@ fn test_array_values() { ( r#"[1,"a",[1,2]]"#, Some(vec![ - Value::Number(Number::Int64(1)), + Value::Number(Number::UInt64(1)), Value::String(Cow::from("a")), Value::Array(vec![ - Value::Number(Number::Int64(1)), - Value::Number(Number::Int64(2)), + Value::Number(Number::UInt64(1)), + Value::Number(Number::UInt64(2)), ]), ]), ), ]; - let mut buf: Vec = Vec::new(); for (s, expect) in sources { - let res = array_values(s.as_bytes()); - match expect.clone() { - Some(expect) => { - let arr = res.unwrap(); - assert_eq!(arr.len(), expect.len()); - for (v, e) in arr.iter().zip(expect.iter()) { - assert_eq!(&from_slice(v).unwrap(), e); - } - } - None => assert_eq!(res, None), - } - let value = parse_value(s.as_bytes()).unwrap(); - value.write_to_vec(&mut buf); - let res = array_values(&buf); + let owned_jsonb = s.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); + let res = raw_jsonb.array_values(); + assert!(res.is_ok()); + let owned_jsonb_opt = res.unwrap(); match expect { - Some(expect) => { - let arr = res.unwrap(); - assert_eq!(arr.len(), expect.len()); - for (v, e) in arr.iter().zip(expect.iter()) { - assert_eq!(&from_slice(v).unwrap(), e); + Some(expects) => { + assert!(owned_jsonb_opt.is_some()); + let owned_jsonbs = owned_jsonb_opt.unwrap(); + assert_eq!(owned_jsonbs.len(), expects.len()); + for (owned_jsonb, expect) in owned_jsonbs.into_iter().zip(expects.iter()) { + let expected_buf = expect.to_vec(); + assert_eq!(owned_jsonb.to_vec(), expected_buf); } } - None => assert_eq!(res, None), + None => assert_eq!(owned_jsonb_opt, None), } - buf.clear(); } } @@ -597,46 +536,14 @@ fn test_compare() { (r#"{"k1":"v1","k2":"v2"}"#, r#"false"#, Ordering::Greater), ]; - let mut lbuf: Vec = Vec::new(); - let mut lbuf2: Vec = Vec::new(); - let mut rbuf: Vec = Vec::new(); - let mut rbuf2: Vec = Vec::new(); - for (l, r, expect) in sources { - let res = compare(l.as_bytes(), r.as_bytes()).unwrap(); - assert_eq!(res, expect); - - let lvalue = parse_value(l.as_bytes()).unwrap(); - lvalue.write_to_vec(&mut lbuf); - let rvalue = parse_value(r.as_bytes()).unwrap(); - rvalue.write_to_vec(&mut rbuf); - - let res = compare(&lbuf, &rbuf).unwrap(); - assert_eq!(res, expect); - - convert_to_comparable(&lbuf, &mut lbuf2); - convert_to_comparable(&rbuf, &mut rbuf2); - - let mut res = Ordering::Equal; - for (lval, rval) in lbuf2.iter().zip(rbuf2.iter()) { - res = lval.cmp(rval); - match res { - Ordering::Equal => { - continue; - } - _ => { - break; - } - } - } - if res == Ordering::Equal { - res = lbuf2.len().cmp(&rbuf2.len()); - } - assert_eq!(res, expect); + for (left, right, expected) in sources { + let left_owned_jsonb = left.parse::().unwrap(); + let left_raw_jsonb = left_owned_jsonb.as_raw(); + let right_owned_jsonb = right.parse::().unwrap(); + let right_raw_jsonb = right_owned_jsonb.as_raw(); - lbuf.clear(); - lbuf2.clear(); - rbuf.clear(); - rbuf2.clear(); + let res = left_raw_jsonb.cmp(&right_raw_jsonb); + assert_eq!(res, expected); } } @@ -677,63 +584,48 @@ fn test_as_type() { (r#"{"k":"v"}"#, None, None, None, None, false, true), ]; - let mut buf: Vec = Vec::new(); for (s, expect_null, expect_bool, expect_number, expect_str, expect_array, expect_object) in sources { - let res = as_null(s.as_bytes()); - match expect_null { - Some(_) => assert!(res.is_some()), - None => assert_eq!(res, None), - } - let res = as_bool(s.as_bytes()); - match expect_bool { - Some(expect) => assert_eq!(res.unwrap(), expect), - None => assert_eq!(res, None), - } - let res = as_number(s.as_bytes()); - match expect_number.clone() { - Some(expect) => assert_eq!(res.unwrap(), expect), - None => assert_eq!(res, None), - } - let res = as_str(s.as_bytes()); - match expect_str.clone() { - Some(expect) => assert_eq!(res.unwrap(), expect), - None => assert_eq!(res, None), - } - let res = is_array(s.as_bytes()); - assert_eq!(res, expect_array); - let res = is_object(s.as_bytes()); - assert_eq!(res, expect_object); + let owned_jsonb = s.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); - let value = parse_value(s.as_bytes()).unwrap(); - value.write_to_vec(&mut buf); - let res = as_null(&buf); + let res = raw_jsonb.as_null(); + assert!(res.is_ok()); + let res = res.unwrap(); match expect_null { Some(_) => assert!(res.is_some()), None => assert_eq!(res, None), } - let res = as_bool(&buf); + let res = raw_jsonb.as_bool(); + assert!(res.is_ok()); + let res = res.unwrap(); match expect_bool { Some(expect) => assert_eq!(res.unwrap(), expect), None => assert_eq!(res, None), } - let res = as_number(&buf); + let res = raw_jsonb.as_number(); + assert!(res.is_ok()); + let res = res.unwrap(); match expect_number.clone() { Some(expect) => assert_eq!(res.unwrap(), expect), None => assert_eq!(res, None), } - let res = as_str(&buf); + let res = raw_jsonb.as_str(); + assert!(res.is_ok()); + let res = res.unwrap(); match expect_str.clone() { Some(expect) => assert_eq!(res.unwrap(), expect), None => assert_eq!(res, None), } - let res = is_array(&buf); + let res = raw_jsonb.is_array(); + assert!(res.is_ok()); + let res = res.unwrap(); assert_eq!(res, expect_array); - let res = is_object(&buf); + let res = raw_jsonb.is_object(); + assert!(res.is_ok()); + let res = res.unwrap(); assert_eq!(res, expect_object); - - buf.clear(); } } @@ -807,63 +699,50 @@ fn test_to_type() { ), ]; - let mut buf: Vec = Vec::new(); for (s, expect_bool, expect_i64, expect_u64, expect_f64, expect_str) in sources { - let res = to_bool(s.as_bytes()); - match expect_bool { - Some(expect) => assert_eq!(res.unwrap(), expect), - None => assert!(res.is_err()), - } - let res = to_i64(s.as_bytes()); - match expect_i64 { - Some(expect) => assert_eq!(res.unwrap(), expect), - None => assert!(res.is_err()), - } - let res = to_u64(s.as_bytes()); - match expect_u64 { - Some(expect) => assert_eq!(res.unwrap(), expect), - None => assert!(res.is_err()), - } - let res = to_f64(s.as_bytes()); - match expect_f64 { - Some(expect) => assert_eq!(res.unwrap(), expect), - None => assert!(res.is_err()), - } - let res = to_str(s.as_bytes()); - match expect_str { - Some(ref expect) => assert_eq!(&res.unwrap(), expect), - None => assert!(res.is_err()), - } + let owned_jsonb = s.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); - let value = parse_value(s.as_bytes()).unwrap(); - value.write_to_vec(&mut buf); - let res = to_bool(&buf); + let res = raw_jsonb.to_bool(); match expect_bool { - Some(expect) => assert_eq!(res.unwrap(), expect), + Some(expect) => { + assert!(res.is_ok()); + assert_eq!(res.unwrap(), expect); + } None => assert!(res.is_err()), } - let res = to_i64(&buf); + let res = raw_jsonb.to_i64(); match expect_i64 { - Some(expect) => assert_eq!(res.unwrap(), expect), + Some(expect) => { + assert!(res.is_ok()); + assert_eq!(res.unwrap(), expect); + } None => assert!(res.is_err()), } - let res = to_u64(&buf); + let res = raw_jsonb.to_u64(); match expect_u64 { - Some(expect) => assert_eq!(res.unwrap(), expect), + Some(expect) => { + assert!(res.is_ok()); + assert_eq!(res.unwrap(), expect); + } None => assert!(res.is_err()), } - let res = to_f64(&buf); + let res = raw_jsonb.to_f64(); match expect_f64 { - Some(expect) => assert_eq!(res.unwrap(), expect), + Some(expect) => { + assert!(res.is_ok()); + assert_eq!(res.unwrap(), expect); + } None => assert!(res.is_err()), } - let res = to_str(&buf); + let res = raw_jsonb.to_str(); match expect_str { - Some(ref expect) => assert_eq!(&res.unwrap(), expect), + Some(expect) => { + assert!(res.is_ok()); + assert_eq!(res.unwrap(), expect); + } None => assert!(res.is_err()), } - - buf.clear(); } } @@ -890,13 +769,11 @@ fn test_to_string() { r#"{"k1":"v1","k2":[1,2,3],"k3":{"a":"b"}}"#, ), ]; - let mut buf: Vec = Vec::new(); for (s, expect) in sources { - let value = parse_value(s.as_bytes()).unwrap(); - value.write_to_vec(&mut buf); - let res = to_string(&buf); + let owned_jsonb = s.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); + let res = raw_jsonb.to_string(); assert_eq!(res, expect); - buf.clear(); } } @@ -956,13 +833,11 @@ fn test_to_pretty_string() { ), ]; - let mut buf: Vec = Vec::new(); for (s, expect) in sources { - let value = parse_value(s.as_bytes()).unwrap(); - value.write_to_vec(&mut buf); - let res = to_pretty_string(&buf); + let owned_jsonb = s.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); + let res = raw_jsonb.to_pretty_string(); assert_eq!(res, expect); - buf.clear(); } } @@ -989,16 +864,14 @@ fn test_traverse_check_string() { false, ), ]; - let mut buf: Vec = Vec::new(); for (s, expect) in sources { - let value = parse_value(s.as_bytes()).unwrap(); - value.write_to_vec(&mut buf); - let res = traverse_check_string(&buf, |v| { + let owned_jsonb = s.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); + let res = raw_jsonb.traverse_check_string(|v| { let s = unsafe { std::str::from_utf8_unchecked(v) }; s == "c" }); - assert_eq!(res, expect); - buf.clear(); + assert_eq!(res, Ok(expect)); } } @@ -1015,25 +888,12 @@ fn test_strip_nulls() { ]; for (s, expect) in sources { - // Check from JSONB - { - let value = parse_value(s.as_bytes()).unwrap().to_vec(); - let mut buf = Vec::new(); - strip_nulls(&value, &mut buf).unwrap(); - assert_eq!( - parse_value(expect.as_bytes()).unwrap(), - from_slice(&buf).unwrap() - ); - } - // Check from String JSON - { - let mut buf = Vec::new(); - strip_nulls(s.as_bytes(), &mut buf).unwrap(); - assert_eq!( - parse_value(expect.as_bytes()).unwrap(), - from_slice(&buf).unwrap() - ); - } + let owned_jsonb = s.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); + + let res = raw_jsonb.strip_nulls(); + let expect_jsonb = expect.parse::().unwrap(); + assert_eq!(res, Ok(expect_jsonb)); } } @@ -1049,15 +909,11 @@ fn test_type_of() { ]; for (s, expect) in sources { - // Check from JSONB - { - let value = parse_value(s.as_bytes()).unwrap().to_vec(); - assert_eq!(expect, type_of(&value).unwrap()); - } - // Check from String JSON - { - assert_eq!(expect, type_of(s.as_bytes()).unwrap()); - } + let owned_jsonb = s.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); + + let res = raw_jsonb.type_of(); + assert_eq!(res, Ok(expect)); } } @@ -1069,7 +925,7 @@ fn test_object_each() { ( r#"{"a":1,"b":false}"#, Some(vec![ - ("a", Value::Number(Number::Int64(1))), + ("a", Value::Number(Number::UInt64(1))), ("b", Value::Bool(false)), ]), ), @@ -1079,45 +935,36 @@ fn test_object_each() { ( "a", Value::Array(vec![ - Value::Number(Number::Int64(1)), - Value::Number(Number::Int64(2)), - Value::Number(Number::Int64(3)), + Value::Number(Number::UInt64(1)), + Value::Number(Number::UInt64(2)), + Value::Number(Number::UInt64(3)), ]), ), ( "b", - init_object(vec![("k", Value::Number(Number::Int64(1)))]), + init_object(vec![("k", Value::Number(Number::UInt64(1)))]), ), ]), ), ]; for (src, expected) in sources { - { - let res = object_each(src.as_bytes()); - match expected.clone() { - Some(expected) => { - let arr = res.unwrap(); - for (v, e) in arr.iter().zip(expected.iter()) { - assert_eq!(v.0, e.0.as_bytes().to_vec()); - assert_eq!(from_slice(&v.1).unwrap(), e.1); - } - } - None => assert_eq!(res, None), - } - } - { - let jsonb = parse_value(src.as_bytes()).unwrap().to_vec(); - let res = object_each(&jsonb); - match expected { - Some(expected) => { - let arr = res.unwrap(); - for (v, e) in arr.iter().zip(expected.iter()) { - assert_eq!(v.0, e.0.as_bytes().to_vec()); - assert_eq!(from_slice(&v.1).unwrap(), e.1); - } + let owned_jsonb = src.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); + + let res = raw_jsonb.object_each(); + assert!(res.is_ok()); + let owned_jsonbs_opt = res.unwrap(); + match expected { + Some(expected) => { + assert!(owned_jsonbs_opt.is_some()); + let owned_jsonbs = owned_jsonbs_opt.unwrap(); + for (v, e) in owned_jsonbs.into_iter().zip(expected.iter()) { + assert_eq!(v.0, e.0.to_string()); + let expected_buf = e.1.to_vec(); + assert_eq!(v.1.to_vec(), expected_buf); } - None => assert_eq!(res, None), } + None => assert_eq!(owned_jsonbs_opt, None), } } } @@ -1153,22 +1000,21 @@ fn test_get_by_keypath() { ), ]; for (json_str, path_str, expected) in sources { + let owned_jsonb = json_str.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); let key_paths = parse_key_paths(path_str.as_bytes()).unwrap(); - { - let json = parse_value(json_str.as_bytes()).unwrap().to_vec(); - let result = get_by_keypath(&json, key_paths.paths.iter()); - match expected.clone() { - Some(e) => assert_eq!(e, from_slice(&result.unwrap()).unwrap()), - None => assert_eq!(result, None), - } - } - { - let json = json_str.as_bytes(); - let result = get_by_keypath(json, key_paths.paths.iter()); - match expected { - Some(e) => assert_eq!(e, from_slice(&result.unwrap()).unwrap()), - None => assert_eq!(result, None), + + let res = raw_jsonb.get_by_keypath(key_paths.paths.iter()); + assert!(res.is_ok()); + let res = res.unwrap(); + match expected { + Some(e) => { + assert!(res.is_some()); + let owned_jsonb = res.unwrap(); + let expected_buf = e.to_vec(); + assert_eq!(owned_jsonb.to_vec(), expected_buf); } + None => assert_eq!(res, None), } } } @@ -1188,17 +1034,13 @@ fn test_exists_all_keys() { (r#"{"a":1,"b":2,"c":3}"#, vec!["c", "f", "a"], false), ]; for (json, keys, expected) in sources { + let owned_jsonb = json.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); + let keys = keys.iter().map(|k| k.as_bytes()); - { - let json = parse_value(json.as_bytes()).unwrap().to_vec(); - let result = exists_all_keys(&json, keys.clone()); - assert_eq!(result, expected); - } - { - let json = json.as_bytes(); - let result = exists_all_keys(json, keys.clone()); - assert_eq!(result, expected); - } + let res = raw_jsonb.exists_all_keys(keys); + assert!(res.is_ok()); + assert_eq!(res.unwrap(), expected); } } @@ -1217,17 +1059,13 @@ fn test_exists_any_keys() { (r#"{"a":1,"b":2,"c":3}"#, vec!["z", "f", "x"], false), ]; for (json, keys, expected) in sources { + let owned_jsonb = json.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); + let keys = keys.iter().map(|k| k.as_bytes()); - { - let json = parse_value(json.as_bytes()).unwrap().to_vec(); - let result = exists_any_keys(&json, keys.clone()); - assert_eq!(result, expected); - } - { - let json = json.as_bytes(); - let result = exists_any_keys(json, keys.clone()); - assert_eq!(result, expected); - } + let res = raw_jsonb.exists_any_keys(keys); + assert!(res.is_ok()); + assert_eq!(res.unwrap(), expected); } } @@ -1256,16 +1094,14 @@ fn test_contains() { (r#"{"a":{"c":100,"d":200},"b":2}"#, r#"{"a":{}}"#, true), ]; for (left, right, expected) in sources { - { - let left = parse_value(left.as_bytes()).unwrap().to_vec(); - let right = parse_value(right.as_bytes()).unwrap().to_vec(); - let result = contains(&left, &right); - assert_eq!(result, expected); - } - { - let result = contains(left.as_bytes(), right.as_bytes()); - assert_eq!(result, expected); - } + let left_owned_jsonb = left.parse::().unwrap(); + let left_raw_jsonb = left_owned_jsonb.as_raw(); + let right_owned_jsonb = right.parse::().unwrap(); + let right_raw_jsonb = right_owned_jsonb.as_raw(); + + let res = left_raw_jsonb.contains(&right_raw_jsonb); + assert!(res.is_ok()); + assert_eq!(res.unwrap(), expected); } } @@ -1287,16 +1123,11 @@ fn test_path_match() { ), ]; for (json, predicate, expected) in sources { + let owned_jsonb = json.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); let json_path = parse_json_path(predicate.as_bytes()).unwrap(); - { - let result = path_match(json.as_bytes(), json_path.clone()).unwrap(); - assert_eq!(result, expected); - } - { - let json = parse_value(json.as_bytes()).unwrap().to_vec(); - let result = path_match(&json, json_path).unwrap(); - assert_eq!(result, expected); - } + let res = raw_jsonb.path_match(&json_path); + assert_eq!(res, Ok(expected)); } } @@ -1339,31 +1170,18 @@ fn test_concat() { r#"{"a":3,"b":4,"d":10}"#, ), ]; - for (left, right, result) in sources { - { - let mut buf = Vec::new(); - - concat(left.as_bytes(), right.as_bytes(), &mut buf).unwrap(); - - let actual = from_slice(&buf).unwrap(); - let expected = parse_value(result.as_bytes()).unwrap(); - - assert_eq!(actual, expected); - assert_eq!(to_string(&buf), result); - } - { - let mut buf = Vec::new(); - let left_json = parse_value(left.as_bytes()).unwrap().to_vec(); - let right_json = parse_value(right.as_bytes()).unwrap().to_vec(); - - concat(&left_json, &right_json, &mut buf).unwrap(); + for (left, right, expected) in sources { + let left_owned_jsonb = left.parse::().unwrap(); + let left_raw_jsonb = left_owned_jsonb.as_raw(); + let right_owned_jsonb = right.parse::().unwrap(); + let right_raw_jsonb = right_owned_jsonb.as_raw(); - let actual = from_slice(&buf).unwrap(); - let expected = parse_value(result.as_bytes()).unwrap(); + let res = left_raw_jsonb.concat(&right_raw_jsonb); + assert!(res.is_ok()); + let res_owned_jsonb = res.unwrap(); - assert_eq!(actual, expected); - assert_eq!(to_string(&buf), result); - } + let expected_owned_jsonb = expected.parse::().unwrap(); + assert_eq!(res_owned_jsonb.to_vec(), expected_owned_jsonb.to_vec()); } } @@ -1382,27 +1200,15 @@ fn test_delete_by_name() { (r#"{"a":1,"b":2}"#, "a", r#"{"b":2}"#), (r#"{"b":2}"#, "b", "{}"), ]; - for (json, name, result) in sources { - { - let mut buf = Vec::new(); - delete_by_name(json.as_bytes(), name, &mut buf).unwrap(); - - let actual = from_slice(&buf).unwrap(); - let expected = parse_value(result.as_bytes()).unwrap(); - - assert_eq!(actual, expected); - } - { - let json = parse_value(json.as_bytes()).unwrap().to_vec(); - let mut buf = Vec::new(); - - delete_by_name(&json, name, &mut buf).unwrap(); + for (json, name, expected) in sources { + let owned_jsonb = json.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); - let actual = from_slice(&buf).unwrap(); - let expected = parse_value(result.as_bytes()).unwrap(); - - assert_eq!(actual, expected); - } + let res = raw_jsonb.delete_by_name(name); + assert!(res.is_ok()); + let res_jsonb = res.unwrap(); + let expected_jsonb = expected.parse::().unwrap(); + assert_eq!(res_jsonb, expected_jsonb); } } @@ -1410,22 +1216,12 @@ fn test_delete_by_name() { fn test_delete_by_name_errors() { let sources = vec![(r#""asd""#, "asd"), ("true", "true"), ("1", "1")]; for (json, name) in sources { - { - let mut buf = Vec::new(); - let result = delete_by_name(json.as_bytes(), name, &mut buf); - - assert!(result.is_err()); - assert!(matches!(result.unwrap_err(), Error::InvalidJsonType)); - } - { - let json = parse_value(json.as_bytes()).unwrap().to_vec(); - let mut buf = Vec::new(); - - let result = delete_by_name(&json, name, &mut buf); + let owned_jsonb = json.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); - assert!(result.is_err()); - assert!(matches!(result.unwrap_err(), Error::InvalidJsonType)); - } + let res = raw_jsonb.delete_by_name(name); + assert!(res.is_err()); + assert!(matches!(res.unwrap_err(), Error::InvalidJsonType)); } } @@ -1441,27 +1237,15 @@ fn test_delete_by_index() { ("[1,2,3]", -4, "[1,2,3]"), (r#"[1,2,{"a":[1,2,3],"b":[40,50,60]}]"#, 2, "[1,2]"), ]; - for (json, index, result) in sources { - { - let mut buf = Vec::new(); - delete_by_index(json.as_bytes(), index, &mut buf).unwrap(); - - let actual = from_slice(&buf).unwrap(); - let expected = parse_value(result.as_bytes()).unwrap(); - - assert_eq!(actual, expected); - } - { - let json = parse_value(json.as_bytes()).unwrap().to_vec(); - let mut buf = Vec::new(); + for (json, index, expected) in sources { + let owned_jsonb = json.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); - delete_by_index(&json, index, &mut buf).unwrap(); - - let actual = from_slice(&buf).unwrap(); - let expected = parse_value(result.as_bytes()).unwrap(); - - assert_eq!(actual, expected); - } + let res = raw_jsonb.delete_by_index(index); + assert!(res.is_ok()); + let res_jsonb = res.unwrap(); + let expected_jsonb = expected.parse::().unwrap(); + assert_eq!(res_jsonb, expected_jsonb); } } @@ -1474,22 +1258,12 @@ fn test_delete_by_index_errors() { (r#"{"a":1,"b":2}"#, 20), ]; for (json, index) in sources { - { - let mut buf = Vec::new(); - let result = delete_by_index(json.as_bytes(), index, &mut buf); + let owned_jsonb = json.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); - assert!(result.is_err()); - assert!(matches!(result.unwrap_err(), Error::InvalidJsonType)); - } - { - let json = parse_value(json.as_bytes()).unwrap().to_vec(); - let mut buf = Vec::new(); - - let result = delete_by_index(&json, index, &mut buf); - - assert!(result.is_err()); - assert!(matches!(result.unwrap_err(), Error::InvalidJsonType)); - } + let res = raw_jsonb.delete_by_index(index); + assert!(res.is_err()); + assert!(matches!(res.unwrap_err(), Error::InvalidJsonType)); } } @@ -1512,31 +1286,16 @@ fn test_delete_by_keypath() { r#"{"a":1,"b":[1,2,3]}"#, ), ]; - for (json, keypath, result) in sources { - { - let json = json.as_bytes(); - let keypath = parse_key_paths(keypath.as_bytes()).unwrap(); - let mut buf = Vec::new(); + for (json, keypath, expected) in sources { + let owned_jsonb = json.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); + let keypath = parse_key_paths(keypath.as_bytes()).unwrap(); - delete_by_keypath(json, keypath.paths.iter(), &mut buf).unwrap(); - - let actual = from_slice(&buf).unwrap(); - let expected = parse_value(result.as_bytes()).unwrap(); - - assert_eq!(actual, expected); - } - { - let json = parse_value(json.as_bytes()).unwrap().to_vec(); - let keypath = parse_key_paths(keypath.as_bytes()).unwrap(); - let mut buf = Vec::new(); - - delete_by_keypath(&json, keypath.paths.iter(), &mut buf).unwrap(); - - let actual = from_slice(&buf).unwrap(); - let expected = parse_value(result.as_bytes()).unwrap(); - - assert_eq!(actual, expected); - } + let res = raw_jsonb.delete_by_keypath(keypath.paths.iter()); + assert!(res.is_ok()); + let res_jsonb = res.unwrap(); + let expected_jsonb = expected.parse::().unwrap(); + assert_eq!(res_jsonb, expected_jsonb); } } @@ -1550,25 +1309,17 @@ fn test_array_insert() { (r#"1"#, 1, r#"{"k":"v"}"#, r#"[1,{"k":"v"}]"#), (r#"{"k":"v"}"#, 2, r#"true"#, r#"[{"k":"v"},true]"#), ]; - for (val, pos, new_val, result) in sources { - { - let val = val.as_bytes(); - let new_val = new_val.as_bytes(); - let mut buf = Vec::new(); - array_insert(val, pos, new_val, &mut buf).unwrap(); - let actual = from_slice(&buf).unwrap(); - let expected = parse_value(result.as_bytes()).unwrap(); - assert_eq!(actual, expected); - } - { - let val = parse_value(val.as_bytes()).unwrap().to_vec(); - let new_val = parse_value(new_val.as_bytes()).unwrap().to_vec(); - let mut buf = Vec::new(); - array_insert(&val, pos, &new_val, &mut buf).unwrap(); - let actual = from_slice(&buf).unwrap(); - let expected = parse_value(result.as_bytes()).unwrap(); - assert_eq!(actual, expected); - } + for (val, pos, new_val, expected) in sources { + let owned_jsonb = val.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); + let new_owned_jsonb = new_val.parse::().unwrap(); + let new_raw_jsonb = new_owned_jsonb.as_raw(); + + let res = raw_jsonb.array_insert(pos, new_raw_jsonb); + assert!(res.is_ok()); + let res_jsonb = res.unwrap(); + let expected_jsonb = expected.parse::().unwrap(); + assert_eq!(res_jsonb, expected_jsonb); } } @@ -1589,23 +1340,15 @@ fn test_array_distinct() { (r#"1"#, r#"[1]"#), (r#"{"k":"v"}"#, r#"[{"k":"v"}]"#), ]; - for (val, result) in sources { - { - let val = val.as_bytes(); - let mut buf = Vec::new(); - array_distinct(val, &mut buf).unwrap(); - let actual = from_slice(&buf).unwrap(); - let expected = parse_value(result.as_bytes()).unwrap(); - assert_eq!(actual, expected); - } - { - let val = parse_value(val.as_bytes()).unwrap().to_vec(); - let mut buf = Vec::new(); - array_distinct(&val, &mut buf).unwrap(); - let actual = from_slice(&buf).unwrap(); - let expected = parse_value(result.as_bytes()).unwrap(); - assert_eq!(actual, expected); - } + for (val, expected) in sources { + let owned_jsonb = val.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); + + let res = raw_jsonb.array_distinct(); + assert!(res.is_ok()); + let res_jsonb = res.unwrap(); + let expected_jsonb = expected.parse::().unwrap(); + assert_eq!(res_jsonb, expected_jsonb); } } @@ -1631,25 +1374,17 @@ fn test_array_intersection() { (r#"1"#, r#"2"#, r#"[]"#), (r#"{"k":"v"}"#, r#"{"k":"v"}"#, r#"[{"k":"v"}]"#), ]; - for (val1, val2, result) in sources { - { - let val1 = val1.as_bytes(); - let val2 = val2.as_bytes(); - let mut buf = Vec::new(); - array_intersection(val1, val2, &mut buf).unwrap(); - let actual = from_slice(&buf).unwrap(); - let expected = parse_value(result.as_bytes()).unwrap(); - assert_eq!(actual, expected); - } - { - let val1 = parse_value(val1.as_bytes()).unwrap().to_vec(); - let val2 = parse_value(val2.as_bytes()).unwrap().to_vec(); - let mut buf = Vec::new(); - array_intersection(&val1, &val2, &mut buf).unwrap(); - let actual = from_slice(&buf).unwrap(); - let expected = parse_value(result.as_bytes()).unwrap(); - assert_eq!(actual, expected); - } + for (left, right, expected) in sources { + let left_owned_jsonb = left.parse::().unwrap(); + let left_raw_jsonb = left_owned_jsonb.as_raw(); + let right_owned_jsonb = right.parse::().unwrap(); + let right_raw_jsonb = right_owned_jsonb.as_raw(); + + let res = left_raw_jsonb.array_intersection(right_raw_jsonb); + assert!(res.is_ok()); + let res_jsonb = res.unwrap(); + let expected_jsonb = expected.parse::().unwrap(); + assert_eq!(res_jsonb, expected_jsonb); } } @@ -1683,25 +1418,17 @@ fn test_array_except() { (r#"1"#, r#"2"#, r#"[1]"#), (r#"{"k":"v"}"#, r#"{"k":"v"}"#, r#"[]"#), ]; - for (val1, val2, result) in sources { - { - let val1 = val1.as_bytes(); - let val2 = val2.as_bytes(); - let mut buf = Vec::new(); - array_except(val1, val2, &mut buf).unwrap(); - let actual = from_slice(&buf).unwrap(); - let expected = parse_value(result.as_bytes()).unwrap(); - assert_eq!(actual, expected); - } - { - let val1 = parse_value(val1.as_bytes()).unwrap().to_vec(); - let val2 = parse_value(val2.as_bytes()).unwrap().to_vec(); - let mut buf = Vec::new(); - array_except(&val1, &val2, &mut buf).unwrap(); - let actual = from_slice(&buf).unwrap(); - let expected = parse_value(result.as_bytes()).unwrap(); - assert_eq!(actual, expected); - } + for (left, right, expected) in sources { + let left_owned_jsonb = left.parse::().unwrap(); + let left_raw_jsonb = left_owned_jsonb.as_raw(); + let right_owned_jsonb = right.parse::().unwrap(); + let right_raw_jsonb = right_owned_jsonb.as_raw(); + + let res = left_raw_jsonb.array_except(right_raw_jsonb); + assert!(res.is_ok()); + let res_jsonb = res.unwrap(); + let expected_jsonb = expected.parse::().unwrap(); + assert_eq!(res_jsonb, expected_jsonb); } } @@ -1723,19 +1450,16 @@ fn test_array_overlap() { (r#"1"#, r#"2"#, false), (r#"{"k":"v"}"#, r#"{"k":"v"}"#, true), ]; - for (val1, val2, expected) in sources { - { - let val1 = val1.as_bytes(); - let val2 = val2.as_bytes(); - let actual = array_overlap(val1, val2).unwrap(); - assert_eq!(actual, expected); - } - { - let val1 = parse_value(val1.as_bytes()).unwrap().to_vec(); - let val2 = parse_value(val2.as_bytes()).unwrap().to_vec(); - let actual = array_overlap(&val1, &val2).unwrap(); - assert_eq!(actual, expected); - } + for (left, right, expected) in sources { + let left_owned_jsonb = left.parse::().unwrap(); + let left_raw_jsonb = left_owned_jsonb.as_raw(); + let right_owned_jsonb = right.parse::().unwrap(); + let right_raw_jsonb = right_owned_jsonb.as_raw(); + + let res = left_raw_jsonb.array_overlap(right_raw_jsonb); + assert!(res.is_ok()); + let res = res.unwrap(); + assert_eq!(res, expected); } } @@ -1781,40 +1505,21 @@ fn test_object_insert() { ), (r#"1"#, "xx", r#"{"k":"v"}"#, true, None), ]; - for (val, new_key, new_val, update_flag, result) in sources { - { - let val = val.as_bytes(); - let new_val = new_val.as_bytes(); - let mut buf = Vec::new(); - let ret = object_insert(val, new_key, new_val, update_flag, &mut buf); - match result { - Some(result) => { - assert!(ret.is_ok()); - let actual = from_slice(&buf).unwrap(); - let expected = parse_value(result.as_bytes()).unwrap(); - assert_eq!(actual, expected); - } - None => { - assert!(ret.is_err()); - } - } - } - { - let val = parse_value(val.as_bytes()).unwrap().to_vec(); - let new_val = parse_value(new_val.as_bytes()).unwrap().to_vec(); - let mut buf = Vec::new(); - let ret = object_insert(&val, new_key, &new_val, update_flag, &mut buf); - match result { - Some(result) => { - assert!(ret.is_ok()); - let actual = from_slice(&buf).unwrap(); - let expected = parse_value(result.as_bytes()).unwrap(); - assert_eq!(actual, expected); - } - None => { - assert!(ret.is_err()); - } + for (val, new_key, new_val, update_flag, expected) in sources { + let owned_jsonb = val.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); + let new_owned_jsonb = new_val.parse::().unwrap(); + let new_raw_jsonb = new_owned_jsonb.as_raw(); + + let res = raw_jsonb.object_insert(new_key, new_raw_jsonb, update_flag); + match expected { + Some(expected) => { + assert!(res.is_ok()); + let res_jsonb = res.unwrap(); + let expected_jsonb = expected.parse::().unwrap(); + assert_eq!(res_jsonb, expected_jsonb); } + None => assert!(res.is_err()), } } } @@ -1839,39 +1544,20 @@ fn test_object_pick() { ), (r#"1"#, vec!["a", "b"], None), ]; - for (val, keys, result) in sources { + for (val, keys, expected) in sources { + let owned_jsonb = val.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); let keys = BTreeSet::from_iter(keys); - { - let val = val.as_bytes(); - let mut buf = Vec::new(); - let ret = object_pick(val, &keys, &mut buf); - match result { - Some(result) => { - assert!(ret.is_ok()); - let actual = from_slice(&buf).unwrap(); - let expected = parse_value(result.as_bytes()).unwrap(); - assert_eq!(actual, expected); - } - None => { - assert!(ret.is_err()); - } - } - } - { - let val = parse_value(val.as_bytes()).unwrap().to_vec(); - let mut buf = Vec::new(); - let ret = object_pick(&val, &keys, &mut buf); - match result { - Some(result) => { - assert!(ret.is_ok()); - let actual = from_slice(&buf).unwrap(); - let expected = parse_value(result.as_bytes()).unwrap(); - assert_eq!(actual, expected); - } - None => { - assert!(ret.is_err()); - } + + let res = raw_jsonb.object_pick(&keys); + match expected { + Some(expected) => { + assert!(res.is_ok()); + let res_jsonb = res.unwrap(); + let expected_jsonb = expected.parse::().unwrap(); + assert_eq!(res_jsonb, expected_jsonb); } + None => assert!(res.is_err()), } } } @@ -1896,39 +1582,20 @@ fn test_object_delete() { ), (r#"1"#, vec!["a", "b"], None), ]; - for (val, keys, result) in sources { + for (val, keys, expected) in sources { + let owned_jsonb = val.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); let keys = BTreeSet::from_iter(keys); - { - let val = val.as_bytes(); - let mut buf = Vec::new(); - let ret = object_delete(val, &keys, &mut buf); - match result { - Some(result) => { - assert!(ret.is_ok()); - let actual = from_slice(&buf).unwrap(); - let expected = parse_value(result.as_bytes()).unwrap(); - assert_eq!(actual, expected); - } - None => { - assert!(ret.is_err()); - } - } - } - { - let val = parse_value(val.as_bytes()).unwrap().to_vec(); - let mut buf = Vec::new(); - let ret = object_delete(&val, &keys, &mut buf); - match result { - Some(result) => { - assert!(ret.is_ok()); - let actual = from_slice(&buf).unwrap(); - let expected = parse_value(result.as_bytes()).unwrap(); - assert_eq!(actual, expected); - } - None => { - assert!(ret.is_err()); - } + + let res = raw_jsonb.object_delete(&keys); + match expected { + Some(expected) => { + assert!(res.is_ok()); + let res_jsonb = res.unwrap(); + let expected_jsonb = expected.parse::().unwrap(); + assert_eq!(res_jsonb, expected_jsonb); } + None => assert!(res.is_err()), } } } @@ -1942,26 +1609,24 @@ fn test_to_serde_json() { r#"{"a":1,"b":[1,2,3]}"#, r#"{"ab":{"k1":"v1","k2":"v2"},"cd":[true,100.23,"测试"]}"#, ]; - let mut buf: Vec = Vec::new(); for s in sources { - let value = parse_value(s.as_bytes()).unwrap(); - buf.clear(); - value.write_to_vec(&mut buf); - let jsonb_val_str = to_string(&buf); - - let json_val = to_serde_json(&buf).unwrap(); - let json_val_str = json_val.to_string(); - assert_eq!(jsonb_val_str, json_val_str); - - let obj_val = to_serde_json_object(&buf).unwrap(); - if is_object(&buf) { - assert!(obj_val.is_some()); - let obj_val = obj_val.unwrap(); - let json_val = serde_json::Value::Object(obj_val); - let obj_val_str = json_val.to_string(); - assert_eq!(jsonb_val_str, obj_val_str); + let owned_jsonb = s.parse::().unwrap(); + let raw_jsonb = owned_jsonb.as_raw(); + let jsonb_val_str = raw_jsonb.to_string(); + + let serde_json_val = raw_jsonb.to_serde_json().unwrap(); + let serde_json_val_str = serde_json_val.to_string(); + assert_eq!(jsonb_val_str, serde_json_val_str); + + let serde_json_obj_val = raw_jsonb.to_serde_json_object().unwrap(); + if raw_jsonb.is_object().unwrap() { + assert!(serde_json_obj_val.is_some()); + let serde_json_obj_val = serde_json_obj_val.unwrap(); + let serde_json_val = serde_json::Value::Object(serde_json_obj_val); + let serde_json_obj_val_str = serde_json_val.to_string(); + assert_eq!(jsonb_val_str, serde_json_obj_val_str); } else { - assert!(obj_val.is_none()); + assert!(serde_json_obj_val.is_none()); } } }