use smallvec::SmallVec;
use im::OrdMap;
use crate::collection::nitrite_id::NitriteId;
use crate::common::{ReadExecutor, Value, DOC_ID, DOC_MODIFIED, DOC_REVISION, DOC_SOURCE, RESERVED_FIELDS};
use crate::errors::{ErrorKind, NitriteError, NitriteResult};
use crate::FIELD_SEPARATOR;
use itertools::Itertools;
use std::borrow::Cow;
use std::collections::{BTreeMap, BTreeSet};
use std::fmt::{Debug, Display};
type FieldVec = SmallVec<[String; 8]>;
#[derive(Clone, Eq, PartialEq, Hash, Default, Ord, PartialOrd, serde::Deserialize, serde::Serialize)]
pub struct Document {
data: OrdMap<String, Value>,
}
impl Document {
pub fn new() -> Self {
Document {
data: OrdMap::new(),
}
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn put<'a, T: Into<Value>>(&mut self, key: impl Into<Cow<'a, str>>, value: T) -> NitriteResult<()> {
let key = key.into();
if key.is_empty() {
log::error!("Document does not support empty key");
return Err(NitriteError::new(
"Document does not support empty key",
ErrorKind::InvalidOperation,
));
}
let value = value.into();
if key == DOC_ID && !value.is_nitrite_id() {
log::error!("Document id is an auto generated field and cannot be set manually");
return Err(NitriteError::new(
"Document id is an auto generated field and cannot be set manually",
ErrorKind::InvalidOperation,
));
}
if FIELD_SEPARATOR.read_with(|sep| key.contains(sep)) {
let splits: Vec<&str> = FIELD_SEPARATOR.read_with(|it| key.split(it).collect());
self.deep_put(&splits, value)
} else {
self.data = self.data.update(key.to_string(), value);
Ok(())
}
}
pub fn get(&self, key: &str) -> NitriteResult<Value> {
match self.data.get(key) {
Some(value) => Ok(value.clone()),
None => {
if FIELD_SEPARATOR.read_with(|sep| key.contains(sep)) {
self.deep_get(key)
} else {
Ok(Value::Null)
}
}
}
}
pub fn id(&mut self) -> NitriteResult<NitriteId> {
if let Some(Value::NitriteId(id)) = self.data.get(DOC_ID) {
Ok(*id)
} else {
let nitrite_id = NitriteId::new();
self.data = self.data.update(
DOC_ID.to_string(),
Value::NitriteId(nitrite_id),
);
Ok(nitrite_id)
}
}
pub fn fields(&self) -> FieldVec {
self.get_fields_internal("")
}
pub fn has_id(&self) -> bool {
self.data.contains_key(DOC_ID)
}
pub fn remove(&mut self, key: &str) -> NitriteResult<()> {
if FIELD_SEPARATOR.read_with(|sep| key.contains(sep)) {
let splits: Vec<&str> = FIELD_SEPARATOR.read_with(|it| key.split(it).collect());
self.deep_remove(&splits)
} else {
self.data = self.data.without(key);
Ok(())
}
}
pub fn size(&self) -> usize {
self.data.len()
}
pub fn merge(&mut self, other: &Document) -> NitriteResult<()> {
for (key, value) in other.data.iter() {
match value {
Value::Document(obj) => {
if let Some(Value::Document(mut nested_obj)) = self.data.get(key).cloned() {
nested_obj.merge(obj)?;
self.data = self.data.update(key.clone(), Value::Document(nested_obj));
} else {
self.data = self.data.update(key.clone(), value.clone());
}
}
_ => {
self.data = self.data.update(key.clone(), value.clone());
}
}
}
Ok(())
}
pub fn contains_key(&self, key: &str) -> bool {
self.data.contains_key(key)
}
pub fn contains_field(&self, field: &str) -> bool {
if self.contains_key(field) {
true
} else {
self.fields().contains(&field.to_string())
}
}
pub fn revision(&self) -> NitriteResult<i32> {
if let Ok(Value::I32(revision)) = self.get(DOC_REVISION) {
Ok(revision)
} else {
Ok(0)
}
}
pub fn source(&self) -> NitriteResult<String> {
if let Ok(Value::String(source)) = self.get(DOC_SOURCE) {
Ok(source.clone())
} else {
Ok("".to_string())
}
}
pub fn last_modified_since_epoch(&self) -> NitriteResult<i64> {
if let Ok(Value::I64(modified)) = self.get(DOC_MODIFIED) {
Ok(modified)
} else {
Ok(0)
}
}
pub fn to_map(&self) -> BTreeMap<String, Value> {
self.data.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
}
pub fn iter(&self) -> DocumentIter {
DocumentIter {
keys: self.data.keys().cloned().collect(),
data: self.clone(),
index: 0,
}
}
pub(crate) fn to_pretty_json(&self, indent: usize) -> String {
if self.data.is_empty() {
return "{}".to_string();
}
let estimated_size = self.data.len() * 30 + indent * 2;
let mut json_string = String::with_capacity(estimated_size);
json_string.push_str("{\n");
let indent_str = " ".repeat(indent + 2);
for (key, value) in self.data.iter() {
json_string.push_str(&format!(
"{}\"{}\": {},\n",
indent_str,
key,
value.to_pretty_json(indent + 2)
));
}
json_string.pop();
json_string.pop();
json_string.push_str(&format!("\n{}}}", " ".repeat(indent)));
json_string
}
pub(crate) fn to_debug_string(&self, indent: usize) -> String {
if self.data.is_empty() {
return "{}".to_string();
}
let mut debug_string = String::new();
debug_string.push_str("{\n");
let indent_str = " ".repeat(indent + 2);
for (key, value) in self.data.iter() {
debug_string.push_str(&format!(
"{}\"{}\": {},\n",
indent_str,
key,
value.to_debug_string(indent + 2)
));
}
debug_string.pop();
debug_string.pop();
debug_string.push_str(&format!("\n{}}}", " ".repeat(indent)));
debug_string
}
fn is_embedded(&self, key: &str) -> bool {
FIELD_SEPARATOR.read_with(|it| key.contains(it))
}
fn get_fields_internal(&self, prefix: &str) -> FieldVec {
let mut fields = FieldVec::new();
let separator = FIELD_SEPARATOR.read_with(|s| s.clone());
for key in self.data.keys() {
if RESERVED_FIELDS.contains(&key.as_str()) {
continue;
}
if key.is_empty() {
continue;
}
let field = if prefix.is_empty() {
key.clone()
} else {
format!("{}{}{}", prefix, separator, key)
};
if let Some(Value::Document(doc)) = self.data.get(key) {
fields.append(&mut doc.get_fields_internal(&field));
} else {
fields.push(field);
}
}
fields
}
fn deep_get(&self, key: &str) -> NitriteResult<Value> {
if !self.is_embedded(key) {
Ok(Value::Null)
} else {
self.get_by_embedded_key(key)
}
}
fn deep_put(&mut self, splits: &[&str], value: Value) -> NitriteResult<()> {
if splits.is_empty() {
log::error!("Empty embedded key");
return Err(NitriteError::new(
"Empty embedded key",
ErrorKind::ValidationError,
));
}
let key = splits[0];
if key.is_empty() {
log::error!("Document does not support empty key");
return Err(NitriteError::new(
"Document does not support empty key",
ErrorKind::InvalidOperation,
));
}
if splits.len() == 1 {
self.put(key, value)
} else {
let remaining_splits = &splits[1..];
if let Some(Value::Document(mut obj)) = self.data.get(key).cloned() {
let result = obj.deep_put(remaining_splits, value);
self.data = self.data.update(key.to_string(), Value::Document(obj));
result
} else {
let mut nested_doc = Document::new();
let result = nested_doc.deep_put(remaining_splits, value);
self.data = self.data.update(key.to_string(), Value::Document(nested_doc));
result
}
}
}
fn deep_remove(&mut self, splits: &[&str]) -> NitriteResult<()> {
if splits.is_empty() {
log::error!("Empty embedded key");
return Err(NitriteError::new(
"Empty embedded key",
ErrorKind::ValidationError,
));
}
let key = splits[0];
if key.is_empty() {
log::error!("Document does not support empty key");
return Err(NitriteError::new(
"Document does not support empty key",
ErrorKind::InvalidOperation,
));
}
if splits.len() == 1 {
self.remove(key)
} else {
let remaining_splits = &splits[1..];
match self.data.get(key) {
Some(Value::Document(obj)) => {
let mut nested_doc = obj.clone();
let result = nested_doc.deep_remove(remaining_splits);
if nested_doc.is_empty() {
self.data = self.data.without(key);
} else {
self.data = self.data.update(key.to_string(), Value::Document(nested_doc));
}
result
}
Some(Value::Array(arr)) => {
let first = splits[1];
if let Ok(index) = first.parse::<isize>() {
if index < 0 {
log::error!(
"Invalid array index {} to access array inside a document",
&index
);
return Err(NitriteError::new(
&format!(
"Invalid array index {} to access array inside a document",
&index
),
ErrorKind::ValidationError,
));
}
let index = index as usize;
if index >= arr.len() {
log::error!("Array index {} out of bound", &index);
return Err(NitriteError::new(
&format!("Array index {} out of bound", &index),
ErrorKind::ValidationError,
));
}
let item = &arr[index];
if let (Value::Document(obj), true) = (item, splits.len() > 2) {
let mut nested_doc = obj.clone();
let result = nested_doc.deep_remove(&remaining_splits[1..]);
if nested_doc.is_empty() {
let mut new_arr = arr.clone();
new_arr.remove(index);
self.data = self.data.update(key.to_string(), Value::Array(new_arr));
} else {
let mut new_arr = arr.clone();
new_arr[index] = Value::Document(nested_doc);
self.data = self.data.update(key.to_string(), Value::Array(new_arr));
}
result
} else {
let mut new_arr = arr.clone();
new_arr.remove(index);
self.data = self.data.update(key.to_string(), Value::Array(new_arr));
Ok(())
}
} else {
log::error!(
"Invalid array index {} to access array inside a document",
first
);
Err(NitriteError::new(
&format!(
"Invalid array index {} to access array inside a document",
first
),
ErrorKind::ValidationError,
))
}
}
_ => {
self.data = self.data.without(key);
Ok(())
}
}
}
}
fn get_by_embedded_key(&self, key: &str) -> NitriteResult<Value> {
let separator = FIELD_SEPARATOR.read_with(|s| s.clone());
let splits: Vec<&str> = key.split(&separator).collect();
if splits.is_empty() {
return Ok(Value::Null);
}
let first = splits[0];
if first.is_empty() {
log::error!("Document does not support empty key");
return Err(NitriteError::new(
"Document does not support empty key",
ErrorKind::InvalidOperation,
));
}
self.recursive_get(self.data.get(first), &splits[1..])
}
fn recursive_get(&self, value: Option<&Value>, splits: &[&str]) -> NitriteResult<Value> {
let value = match value {
None => return Ok(Value::Null),
Some(v) => v,
};
if splits.is_empty() {
return Ok(value.clone());
}
let key = splits[0];
if key.is_empty() {
log::error!("Document does not support empty key");
return Err(NitriteError::new(
"Document does not support empty key",
ErrorKind::InvalidOperation,
));
}
match value {
Value::Document(obj) => {
self.recursive_get(obj.data.get(key), &splits[1..])
}
Value::Array(arr) => {
let first = key;
if let Ok(index) = first.parse::<isize>() {
if index < 0 {
log::error!(
"Invalid array index {} to access array inside a document",
&index
);
return Err(NitriteError::new(
&format!(
"Invalid array index {} to access array inside a document",
&index
),
ErrorKind::ValidationError,
));
}
let index = index as usize;
if index >= arr.len() {
log::error!("Array index {} out of bound", &index);
return Err(NitriteError::new(
&format!("Array index {} out of bound", &index),
ErrorKind::ValidationError,
));
}
let item = &arr[index];
self.recursive_get(Some(item), &splits[1..])
} else {
self.decompose(arr, splits)
}
}
_ => Ok(Value::Null), }
}
fn decompose(&self, arr: &[Value], splits: &[&str]) -> NitriteResult<Value> {
let mut items: Vec<Value> = Vec::with_capacity(arr.len());
for item in arr {
let result = self.recursive_get(Some(item), splits)?;
match result {
Value::Array(arr) => {
for v in arr {
items.push(v);
}
}
value => {
items.push(value);
}
}
}
Ok(Value::Array(items.iter().unique().cloned().collect::<Vec<_>>()))
}
}
impl Debug for Document {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_debug_string(0))
}
}
impl Display for Document {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_pretty_json(0))
}
}
pub struct DocumentIter {
keys: Vec<String>,
data: Document,
index: usize,
}
impl Iterator for DocumentIter {
type Item = (String, Value);
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.keys.len() {
let key = &self.keys[self.index];
if let Some(value) = self.data.data.get(key) {
let result = (key.clone(), value.clone());
self.index += 1;
return Some(result);
}
self.index += 1;
self.next()
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.keys.len().saturating_sub(self.index);
(remaining, Some(remaining))
}
}
pub fn normalize(value: &str) -> String {
value.trim_matches('"').to_string()
}
#[macro_export]
macro_rules! doc {
({}) => {
$crate::collection::Document::new()
};
() => {
$crate::collection::Document::new()
};
({ $($key:tt : $value:tt),* $(,)? }) => {
$crate::doc!($($key : $value),*)
};
($($key:tt : $value:tt),* $(,)?) => {
{
#[allow(unused_imports)]
use $crate::doc_value;
let mut doc = $crate::collection::Document::new();
$(
doc.put(&$crate::collection::normalize(stringify!($key)), $crate::doc_value!($value))
.expect(&format!("Failed to put value {} in document", stringify!($value)));
)*
doc
}
};
}
#[macro_export]
macro_rules! doc_value {
({ $($key:tt : $value:tt),* $(,)? }) => {
{
$crate::common::Value::Document($crate::doc!{ $($key : $value),* })
}
};
([ $($value:tt),* $(,)? ]) => {
$crate::common::Value::Array(vec![$($crate::doc_value!($value)),*])
};
($value:expr) => {
$crate::common::Value::from($value)
};
}
#[cfg(test)]
mod tests {
use std::string;
use super::*;
use crate::collection::Document;
use crate::common::Value::Null;
use crate::{create_document, document_from_map, empty_document};
fn set_up() -> Document {
doc!{
score: 1034,
location: {
state: "NY",
city: "New York",
address: {
line1: "40",
line2: "ABC Street",
house: ["1", "2", "3"],
zip: 10001,
},
},
category: ["food", "produce", "grocery"],
obj_array: [
{
value: 1,
},
{
value: 2,
},
]
}
}
#[test]
fn test_normalize() {
let value = "\"ABC\"".to_string();
let result = normalize(&value);
assert_eq!(result, "ABC");
let value = "ABC".to_string();
let result = normalize(&value);
assert_eq!(result, "ABC");
}
#[test]
fn test_empty_document() {
let doc = empty_document();
assert!(doc.is_empty());
}
#[test]
fn test_new() {
let doc = Document::new();
assert!(doc.is_empty());
}
#[test]
fn test_document_from_map() {
let mut map = BTreeMap::new();
map.insert("key1".to_string(), Value::I32(1));
map.insert("key2".to_string(), Value::String("value".to_string()));
map.insert(
"key3".to_string(),
Value::Array(vec![Value::I32(1), Value::I32(2)]),
);
map.insert("key4".to_string(), Value::Document(Document::new()));
let doc = document_from_map(&map).unwrap();
assert_eq!(doc.size(), 4);
}
#[test]
fn test_create_document() {
let doc = create_document("key", Value::I32(1)).unwrap();
assert_eq!(doc.size(), 1);
}
#[test]
fn test_is_empty() {
let doc = empty_document();
assert!(doc.is_empty());
let doc = set_up();
assert!(!doc.is_empty());
}
#[test]
fn test_get() {
let doc = set_up();
let mut value = doc.get("").unwrap();
assert_eq!(value, Null);
value = doc.get("score").unwrap();
assert_eq!(value, Value::I32(1034));
value = doc.get("location.state").unwrap();
assert_eq!(value, Value::String("NY".to_string()));
value = doc.get("location.address").unwrap();
assert_eq!(
value,
Value::Document(doc!{
line1: "40",
line2: "ABC Street",
house: ["1", "2", "3"],
zip: 10001,
})
);
value = doc.get("location.address.line1").unwrap();
assert_eq!(value, Value::String("40".to_string()));
value = doc.get("location.address.line2").unwrap();
assert_eq!(value, Value::String("ABC Street".to_string()));
value = doc.get("location.address.house").unwrap();
assert_eq!(
value,
Value::Array(vec![
Value::String("1".to_string()),
Value::String("2".to_string()),
Value::String("3".to_string())
])
);
value = doc.get("location.address.house.0").unwrap();
assert_eq!(value, Value::String("1".to_string()));
value = doc.get("location.address.house.1").unwrap();
assert_eq!(value, Value::String("2".to_string()));
value = doc.get("location.address.house.2").unwrap();
assert_eq!(value, Value::String("3".to_string()));
value = doc.get("location.address.zip").unwrap();
assert_eq!(value, Value::I32(10001));
value = doc.get("category").unwrap();
assert_eq!(
value,
Value::Array(vec![
Value::String("food".to_string()),
Value::String("produce".to_string()),
Value::String("grocery".to_string())
])
);
value = doc.get("category.0").unwrap();
assert_eq!(value, Value::String("food".to_string()));
value = doc.get("category.1").unwrap();
assert_eq!(value, Value::String("produce".to_string()));
value = doc.get("category.2").unwrap();
assert_eq!(value, Value::String("grocery".to_string()));
value = doc.get("obj_array").unwrap();
assert_eq!(
value,
Value::Array(vec![
Value::Document(doc!{ value: 1 }),
Value::Document(doc!{ value: 2 }),
])
);
value = doc.get("obj_array.0").unwrap();
assert_eq!(value, Value::Document(doc!{ value: 1 }));
value = doc.get("obj_array.1").unwrap();
assert_eq!(value, Value::Document(doc!{ value: 2 }));
value = doc.get("obj_array.0.value").unwrap();
assert_eq!(value, Value::I32(1));
value = doc.get("obj_array.1.value").unwrap();
assert_eq!(value, Value::I32(2));
value = doc.get("location.address.test").unwrap();
assert_eq!(value, Null);
assert!(doc.get("location.address.house.3").is_err());
assert!(doc.get("location.address.house.-1").is_err());
assert!(doc.get(".").is_err());
assert!(doc.get("..").is_err());
assert_eq!(doc.get("score.test").unwrap(), Null);
}
#[test]
fn test_put_null() {
let mut doc = empty_document();
doc.put("key", Null).unwrap();
assert_eq!(doc.size(), 1);
assert_eq!(doc.get("key").unwrap(), Null);
}
#[test]
fn test_put_and_get() {
let mut doc = Document::new();
doc.put("key", Value::I32(1)).unwrap();
assert_eq!(doc.get("key").unwrap(), Value::I32(1));
}
#[test]
fn test_put_empty_key() {
let mut doc = Document::new();
let result = doc.put("", Value::I32(1));
assert!(result.is_err());
}
#[test]
fn test_put_reserved_id() {
let mut doc = Document::new();
let result = doc.put(DOC_ID, Value::String("id".to_string()));
assert!(result.is_err());
}
#[test]
fn test_put_id() {
let mut doc = empty_document();
let result = doc.put(DOC_ID, Value::String("id".to_string()));
assert!(result.is_err());
}
#[test]
fn test_put_valid_nitrite_id() {
let mut doc = empty_document();
let result = doc.put(DOC_ID, Value::NitriteId(NitriteId::new()));
assert!(result.is_ok());
}
#[test]
fn test_get_invalid_id() {
let mut map = BTreeMap::new();
map.insert(DOC_ID.to_string(), Value::String("invalid_id".to_string()));
let err = document_from_map(&map).is_err();
assert!(err);
}
#[test]
fn test_get_non_existent_key() {
let doc = Document::new();
assert_eq!(doc.get("non_existent").unwrap(), Null);
}
#[test]
fn test_invalid_get() {
let key = "first.array.-1";
let doc = doc!{
first: {
array: [1, 2, 3],
},
};
let err = doc.get(key).is_err();
assert!(err);
}
#[test]
fn test_id() {
let mut doc = empty_document();
let id = doc.id().unwrap();
assert!(NitriteId::valid_id(id.id_value()).unwrap());
assert!(doc.has_id());
}
#[test]
fn test_fields() {
let doc = doc!{
key1: 1,
key2: "value",
key3: [1, 2, 3],
key4: {
key5: 5,
key6: "value",
},
};
let fields = doc.fields();
assert_eq!(fields.len(), 5);
}
#[test]
fn test_has_id() {
let mut doc = empty_document();
assert!(!doc.has_id());
doc.put(DOC_ID, Value::NitriteId(NitriteId::new())).unwrap();
assert!(doc.has_id());
}
#[test]
fn test_contains_key() {
let doc = set_up();
assert!(doc.contains_key("score"));
assert!(!doc.contains_key("non_existent"));
}
#[test]
fn test_contains_field() {
let doc = set_up();
assert!(doc.contains_field("location.state"));
assert!(!doc.contains_field("location.country"));
}
#[test]
fn test_remove() {
let mut doc = empty_document();
doc.put("key", Value::I32(1)).unwrap();
assert_eq!(doc.size(), 1);
doc.remove("key").unwrap();
assert_eq!(doc.size(), 0);
}
#[test]
fn test_size() {
let doc = set_up();
assert_eq!(doc.size(), 4);
}
#[test]
fn test_revision() {
let mut doc = Document::new();
doc.put(DOC_REVISION, Value::I32(1)).unwrap();
assert_eq!(doc.revision().unwrap(), 1);
}
#[test]
fn test_source() {
let mut doc = Document::new();
doc.put(DOC_SOURCE, Value::String("source".to_string())).unwrap();
assert_eq!(doc.source().unwrap(), "source");
}
#[test]
fn test_last_modified_since_epoch() {
let mut doc = Document::new();
doc.put(DOC_MODIFIED, Value::I64(123456789)).unwrap();
assert_eq!(doc.last_modified_since_epoch().unwrap(), 123456789);
}
#[test]
fn test_to_map() {
let doc = set_up();
let map = doc.to_map();
assert_eq!(map.len(), 4);
}
#[test]
fn test_iter() {
let doc = doc!{
key1: "value1",
key2: 2,
};
let mut iter = doc.iter();
let (key, value) = iter.next().unwrap();
assert_eq!(key, "key1");
assert_eq!(value, Value::String("value1".to_string()));
let (key, value) = iter.next().unwrap();
assert_eq!(key, "key2");
assert_eq!(value, Value::I32(2));
}
#[test]
fn test_get_fields() {
let doc = set_up();
let fields = doc.fields();
assert_eq!(fields.len(), 9);
assert!(fields.contains(&"score".to_string()));
assert!(fields.contains(&"location.state".to_string()));
assert!(fields.contains(&"location.city".to_string()));
assert!(fields.contains(&"location.address.line1".to_string()));
assert!(fields.contains(&"location.address.line2".to_string()));
assert!(fields.contains(&"location.address.house".to_string()));
assert!(fields.contains(&"location.address.zip".to_string()));
assert!(fields.contains(&"category".to_string()));
assert!(fields.contains(&"obj_array".to_string()));
}
#[test]
fn test_get_embedded_array_fields() {
let doc = doc!{
first: "value",
second: ["1", "2"],
third: Null,
fourth: {
first: "value",
second: ["1", "2"],
third: {
first: [1, 2],
second: "other",
},
},
fifth: [
{
first: "value",
second: [1, 2, 3],
third: {
first: "value",
second: [1, 2],
},
fourth: [
{
first: "value",
second: [1, 2],
},
{
first: "value",
second: [1, 2],
},
],
},
{
first: "value",
second: [3, 4, 5],
third: {
first: "value",
second: [1, 2],
},
fourth: [
{
first: "value",
second: [1, 2],
},
{
first: "value",
second: [1, 2],
},
],
},
{
first: "value",
second: [5, 6, 7],
third: {
first: "value",
second: [1, 2],
},
fourth: [
{
first: "value",
second: [1, 2],
},
{
first: "value",
second: [3, 4],
},
],
},
]
};
let val = doc.get("fifth.second").unwrap();
let list = val.as_array().unwrap();
assert_eq!(list.len(), 7);
let val = doc.get("fifth.fourth.second").unwrap();
let list = val.as_array().unwrap();
assert_eq!(list.len(), 4);
let val = doc.get("fourth.third.second").unwrap();
assert_eq!(val, Value::String("other".to_string()));
let val = doc.get("fifth.0.second.0").unwrap();
assert_eq!(val, Value::I32(1));
let val = doc.get("fifth.1.fourth.0.second.1").unwrap();
assert_eq!(val, Value::I32(2));
}
#[test]
fn test_deep_put() {
let mut doc = set_up();
doc.put("location.address.pin", Value::I32(700037)).unwrap();
assert_eq!(doc.get("location.address.pin").unwrap(), Value::I32(700037));
doc.put("location.address.business.pin", Value::I32(700037))
.unwrap();
assert_eq!(
doc.get("location.address.business.pin").unwrap(),
Value::I32(700037)
);
}
#[test]
fn test_deep_remove() {
let mut doc = set_up();
doc.remove("location.address.zip").unwrap();
assert_eq!(doc.get("location.address.zip").unwrap(), Null);
doc.remove("location.address.line1").unwrap();
assert_eq!(doc.get("location.address.line1").unwrap(), Null);
doc.remove("location.address.line2").unwrap();
assert_eq!(doc.get("location.address.line2").unwrap(), Null);
doc.remove("location.address.house").unwrap();
assert_eq!(doc.get("location.address.house").unwrap(), Null);
doc.remove("location.address").unwrap();
assert_eq!(doc.get("location.address").unwrap(), Null);
}
#[test]
fn test_deep_get() {
let doc = set_up();
let value = doc.get("location.address.line1").unwrap();
assert_eq!(value, Value::String("40".to_string()));
}
#[test]
fn test_recursive_get() {
let doc = set_up();
let value = doc.get("location.address.house.0").unwrap();
assert_eq!(value, Value::String("1".to_string()));
}
#[test]
fn test_decompose() {
let doc = doc!{
some_key: [{key: 1}, {key: 2}, {key: 3}],
};
let value = doc.get("some_key").unwrap();
let array = value.as_array().unwrap();
let decomposed = doc.decompose(array, &["key"]).unwrap();
assert_eq!(decomposed, Value::Array(vec![Value::I32(1), Value::I32(2), Value::I32(3)]));
}
#[test]
fn test_deep_put_invalid_field() {
let mut doc = empty_document();
let result = doc.put("..invalid..field", Value::I32(1));
assert!(result.is_err());
}
#[test]
fn test_deep_remove_invalid_field() {
let mut doc = empty_document();
let result = doc.remove("..invalid..field");
assert!(result.is_err());
}
#[test]
fn test_merge_documents() {
let mut doc1 = doc!{
"key1": "value1",
"nested": {
"key2": "value2",
},
};
let doc2 = doc!{
"key3": "value3",
"nested": {
"key4": "value4",
},
};
doc1.merge(&doc2).unwrap();
assert_eq!(doc1.size(), 3);
assert_eq!(
doc1.get("key1").unwrap(),
Value::String("value1".to_string())
);
assert_eq!(
doc1.get("key3").unwrap(),
Value::String("value3".to_string())
);
assert_eq!(
doc1.get("nested.key2").unwrap(),
Value::String("value2".to_string())
);
assert_eq!(
doc1.get("nested.key4").unwrap(),
Value::String("value4".to_string())
);
}
#[test]
fn test_display() {
let doc = doc!{
key1: "value1",
key2: 2,
};
let display = format!("{}", doc);
assert!(display.contains("\"key1\": \"value1\""));
assert!(display.contains("\"key2\": 2"));
}
#[test]
fn test_debug() {
let doc = doc!{
key1: "value1",
key2: 2,
};
let debug = format!("{:?}", doc);
assert!(debug.contains("\"key1\": string(\"value1\")"));
assert!(debug.contains("\"key2\": i32(2)"));
}
#[test]
fn test_put_invalid_id() {
let mut doc = empty_document();
let result = doc.put(DOC_ID, Value::String("invalid_id".to_string()));
assert!(result.is_err());
}
#[test]
fn test_get_invalid_key() {
let doc = empty_document();
let result = doc.get("invalid.key");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Null);
}
#[test]
fn test_remove_invalid_key() {
let mut doc = empty_document();
let result = doc.remove("invalid.key");
assert!(result.is_ok());
}
#[test]
fn test_merge_empty_document() {
let mut doc1 = empty_document();
let doc2 = empty_document();
doc1.merge(&doc2).unwrap();
assert_eq!(doc1.size(), 0);
}
#[test]
fn test_get_invalid_array_index() {
let doc = doc!{
key: [1, 2, 3],
};
let result = doc.get("key.-1");
assert!(result.is_err());
}
#[test]
fn test_remove_invalid_array_index() {
let mut doc = doc!{
key: [1, 2, 3],
};
let result = doc.remove("key.-1");
assert!(result.is_err());
}
#[test]
fn test_merge_conflicting_keys() {
let mut doc1 = doc!{
key1: "value1",
key2: "value2",
};
let doc2 = doc!{
key2: "value3",
key3: "value4",
};
doc1.merge(&doc2).unwrap();
assert_eq!(doc1.size(), 3);
assert_eq!(
doc1.get("key1").unwrap(),
Value::String("value1".to_string())
);
assert_eq!(
doc1.get("key2").unwrap(),
Value::String("value3".to_string())
);
assert_eq!(
doc1.get("key3").unwrap(),
Value::String("value4".to_string())
);
}
#[test]
fn test_deep_put_invalid_path() {
let mut doc = empty_document();
let result = doc.put("key..key", Value::I32(1));
assert!(result.is_err());
}
#[test]
fn test_deep_get_invalid_path() {
let doc = set_up();
let result = doc.get("location..key");
assert!(result.is_err());
}
#[test]
fn contains_field_with_existing_field() {
let doc = set_up();
assert!(doc.contains_field("location.state"));
}
#[test]
fn contains_field_with_non_existing_field() {
let doc = set_up();
assert!(!doc.contains_field("location.country"));
}
#[test]
fn revision_with_existing_revision() {
let mut doc = empty_document();
doc.put(DOC_REVISION, Value::I32(1)).unwrap();
assert_eq!(doc.revision().unwrap(), 1);
}
#[test]
fn revision_with_non_existing_revision() {
let doc = empty_document();
assert_eq!(doc.revision().unwrap(), 0);
}
#[test]
fn source_with_existing_source() {
let mut doc = empty_document();
doc.put(DOC_SOURCE, Value::String("source".to_string()))
.unwrap();
assert_eq!(doc.source().unwrap(), "source");
}
#[test]
fn source_with_non_existing_source() {
let doc = empty_document();
assert_eq!(doc.source().unwrap(), "");
}
#[test]
fn last_modified_since_epoch_with_existing_modified() {
let mut doc = empty_document();
doc.put(DOC_MODIFIED, Value::I64(123456789)).unwrap();
assert_eq!(doc.last_modified_since_epoch().unwrap(), 123456789);
}
#[test]
fn last_modified_since_epoch_with_non_existing_modified() {
let doc = empty_document();
assert_eq!(doc.last_modified_since_epoch().unwrap(), 0);
}
#[test]
fn test_macro() {
let string_key = "c".to_string();
let document = doc!{
s: string_key,
a: 12,
b: "c",
"d": [1, 2, 3],
e: ["f", "g"],
h: {
i: 10,
j: [1, 2, 3],
},
k: [
{
l: 15,
m: [10, 15],
"n": {
o: 1.25,
},
},
{
p: 15,
q: [
{
r: "45",
s: 1569,
},
{
t: 20,
u: 1.25,
},
],
}
],
u: [
["v", "w"],
["x", "y", "z"],
],
v: {
w: {
x: {
y: {
z: true,
}
}
}
}
};
let mut doc2 = empty_document();
doc2.put("s", Value::String("c".to_string())).unwrap();
doc2.put("a", Value::I32(12)).unwrap();
doc2.put("b", Value::String("c".to_string())).unwrap();
doc2.put(
"d",
Value::Array(vec![Value::I32(1), Value::I32(2), Value::I32(3)]),
)
.unwrap();
doc2.put(
"e",
Value::Array(vec![
Value::String("f".to_string()),
Value::String("g".to_string()),
]),
)
.unwrap();
doc2.put("h", Value::Document(doc!{ i: 10, j: [1, 2, 3] }))
.unwrap();
doc2.put(
"k",
Value::Array(vec![
Value::Document(doc!{ l: 15, m: [10, 15], "n": { o: 1.25 } }),
Value::Document(doc!{ p: 15, q: [
{ r: "45", s: 1569 },
{ t: 20, u: 1.25 },
] }),
]),
)
.unwrap();
doc2.put(
"u",
Value::Array(vec![
Value::Array(vec![
Value::String("v".to_string()),
Value::String("w".to_string()),
]),
Value::Array(vec![
Value::String("x".to_string()),
Value::String("y".to_string()),
Value::String("z".to_string()),
]),
]),
)
.unwrap();
doc2.put("v", Value::Document(doc!{ w: { x: { y: { z: true } } } }))
.unwrap();
assert_eq!(document, doc2);
}
#[test]
fn test_empty_document_macro() {
let doc = doc!{};
assert!(doc.is_empty());
}
#[test]
fn test_document_with_key_value_string() {
let doc = doc!{
key: "value",
};
assert_eq!(doc.size(), 1);
assert_eq!(doc.get("key").unwrap(), Value::String("value".to_string()));
}
#[test]
fn test_nested_document_macro() {
let doc = doc!{
key1: "value1",
key2: {
key3: "value3",
},
};
assert_eq!(doc.size(), 2);
assert_eq!(
doc.get("key1").unwrap(),
Value::String("value1".to_string())
);
assert_eq!(
doc.get("key2.key3").unwrap(),
Value::String("value3".to_string())
);
}
#[test]
fn test_array_in_document_macro() {
let doc = doc!{
key1: "value1",
key2: [1, 2, 3],
};
assert_eq!(doc.size(), 2);
assert_eq!(
doc.get("key1").unwrap(),
Value::String("value1".to_string())
);
assert_eq!(
doc.get("key2").unwrap(),
Value::Array(vec![Value::I32(1), Value::I32(2), Value::I32(3)])
);
}
#[test]
fn test_complex_document_macro() {
let document = doc!{
key1: "value1",
key2: {
nested_key1: "nested_value",
nested_key2: [10, 20, 30]
},
key3: [true, false, {
"deep_nested_key": "deep_value"
}]
};
assert_eq!(document.size(), 3);
assert_eq!(
document.get("key1").unwrap(),
Value::String("value1".to_string())
);
let binding = document.get("key2").unwrap();
let nested_doc = binding.as_document().unwrap();
assert_eq!(nested_doc.size(), 2);
assert_eq!(
nested_doc.get("nested_key1").unwrap(),
Value::String("nested_value".to_string())
);
let nested_array = nested_doc.get("nested_key2").unwrap();
let array = nested_array.as_array().unwrap();
assert_eq!(array.len(), 3);
assert_eq!(array[0], Value::I32(10));
assert_eq!(array[1], Value::I32(20));
assert_eq!(array[2], Value::I32(30));
let binding = document.get("key3").unwrap();
let array = binding.as_array().unwrap();
assert_eq!(array.len(), 3);
assert_eq!(array[0], Value::Bool(true));
assert_eq!(array[1], Value::Bool(false));
let binding = &array[2];
let nested_doc = binding.as_document().unwrap();
assert_eq!(nested_doc.size(), 1);
assert_eq!(
nested_doc.get("deep_nested_key").unwrap(),
Value::String("deep_value".to_string())
);
}
#[test]
fn test_recursive_get_null_value() {
let doc = Document::new();
let result = doc.recursive_get(None, &[]);
assert_eq!(result.unwrap(), Null);
}
#[test]
fn test_recursive_get_simple_value() {
let doc = doc!{
"name": "Alice",
"age": 25
};
let value = doc.get("name").unwrap();
assert_eq!(value, Value::String("Alice".to_string()));
let value = doc.get("age").unwrap();
assert_eq!(value, Value::I32(25));
}
#[test]
fn test_recursive_get_nested_document() {
let doc = doc!{
"user": {
"profile": {
"name": "Bob",
"age": 30
}
}
};
let value = doc.get("user").unwrap();
assert!(matches!(value, Value::Document(_)));
let value = doc.get("user.profile").unwrap();
assert!(matches!(value, Value::Document(_)));
let value = doc.get("user.profile.name").unwrap();
assert_eq!(value, Value::String("Bob".to_string()));
}
#[test]
fn test_recursive_get_array_index() {
let doc = doc!{
"items": [
{"name": "Item 1"},
{"name": "Item 2"},
{"name": "Item 3"}
]
};
let value = doc.get("items.0").unwrap();
assert!(matches!(value, Value::Document(_)));
let value = doc.get("items.0.name").unwrap();
assert_eq!(value, Value::String("Item 1".to_string()));
let value = doc.get("items.2.name").unwrap();
assert_eq!(value, Value::String("Item 3".to_string()));
}
#[test]
fn test_recursive_get_nonexistent_path() {
let doc = doc!{
"name": "Charlie",
"age": 35
};
let value = doc.get("nonexistent").unwrap();
assert_eq!(value, Null);
let value = doc.get("name.nested").unwrap();
assert_eq!(value, Null);
}
#[test]
fn test_recursive_get_deep_nesting() {
let doc = doc!{
"level1": {
"level2": {
"level3": {
"level4": {
"value": "deep"
}
}
}
}
};
let value = doc.get("level1.level2.level3.level4.value").unwrap();
assert_eq!(value, Value::String("deep".to_string()));
}
#[test]
fn test_recursive_get_mixed_types() {
let doc = doc!{
"name": "David",
"age": 28,
"tags": ["tag1", "tag2"],
"meta": {
"created": "2025-01-01"
}
};
assert_eq!(doc.get("name").unwrap(), Value::String("David".to_string()));
assert_eq!(doc.get("age").unwrap(), Value::I32(28));
assert!(matches!(doc.get("tags").unwrap(), Value::Array(_)));
assert!(matches!(doc.get("meta").unwrap(), Value::Document(_)));
}
#[test]
fn bench_get_top_level_key() {
let doc = doc!{
key1: "value1",
key2: "value2",
key3: "value3",
key4: "value4",
key5: "value5",
};
let start = std::time::Instant::now();
for _ in 0..10000 {
let _ = doc.get("key1");
}
let elapsed = start.elapsed();
println!("10000 top-level get calls: {:?}", elapsed);
assert!(elapsed.as_millis() < 500);
}
#[test]
fn bench_get_embedded_key() {
let doc = doc!{
level1: {
level2: {
level3: "value"
}
}
};
let start = std::time::Instant::now();
for _ in 0..1000 {
let _ = doc.get("level1.level2.level3");
}
let elapsed = start.elapsed();
println!("1000 embedded get calls: {:?}", elapsed);
assert!(elapsed.as_millis() < 200);
}
#[test]
fn bench_put_operations() {
let start = std::time::Instant::now();
for i in 0..1000 {
let mut doc = Document::new();
doc.put(format!("key{}", i), Value::I32(i)).ok();
}
let elapsed = start.elapsed();
println!("1000 put operations: {:?}", elapsed);
assert!(elapsed.as_millis() < 300);
}
#[test]
fn bench_merge_documents() {
let start = std::time::Instant::now();
for _ in 0..100 {
let mut doc1 = doc!{
key1: "value1",
nested: {
key2: "value2",
}
};
let doc2 = doc!{
key3: "value3",
nested: {
key4: "value4",
}
};
doc1.merge(&doc2).ok();
}
let elapsed = start.elapsed();
println!("100 merge operations: {:?}", elapsed);
assert!(elapsed.as_millis() < 200);
}
#[test]
fn bench_fields_collection() {
let doc = doc!{
key1: "value1",
key2: 2,
nested: {
key3: "value3",
deep: {
key4: "value4"
}
},
array: [1, 2, 3]
};
let start = std::time::Instant::now();
for _ in 0..1000 {
let _ = doc.fields();
}
let elapsed = start.elapsed();
println!("1000 fields() calls: {:?}", elapsed);
assert!(elapsed.as_millis() < 300);
}
#[test]
fn test_doc_macro_with_variables() {
let name = "Alice";
let age = 30;
let city = String::from("NYC");
let doc = doc!{
name: name,
age: age,
city: city
};
assert_eq!(doc.get("name").unwrap(), Value::String("Alice".to_string()));
assert_eq!(doc.get("age").unwrap(), Value::I32(30));
assert_eq!(doc.get("city").unwrap(), Value::String("NYC".to_string()));
}
#[test]
fn test_doc_macro_with_expressions() {
let base = 100;
let multiplier = 2;
let doc = doc!{
computed: (base * multiplier),
sum: (10 + 20 + 30),
concat: (format!("Hello {}", "World"))
};
assert_eq!(doc.get("computed").unwrap(), Value::I32(200));
assert_eq!(doc.get("sum").unwrap(), Value::I32(60));
assert_eq!(doc.get("concat").unwrap(), Value::String("Hello World".to_string()));
}
#[test]
fn test_doc_macro_with_function_calls() {
fn get_name() -> &'static str {
"Bob"
}
fn calculate_score() -> i32 {
42
}
let doc = doc!{
name: (get_name()),
score: (calculate_score()),
length: ("hello".len())
};
assert_eq!(doc.get("name").unwrap(), Value::String("Bob".to_string()));
assert_eq!(doc.get("score").unwrap(), Value::I32(42));
assert_eq!(doc.get("length").unwrap(), Value::U64(5));
}
#[test]
fn test_doc_macro_with_mixed_expressions_and_nested() {
let user_name = "Charlie";
let base_score = 50;
let doc = doc!{
user: {
name: user_name,
score: (base_score * 2)
},
tags: ["admin", user_name],
values: [1, (2 + 3), 6]
};
assert_eq!(doc.get("user.name").unwrap(), Value::String("Charlie".to_string()));
assert_eq!(doc.get("user.score").unwrap(), Value::I32(100));
let tags = doc.get("tags").unwrap();
let tags_arr = tags.as_array().unwrap();
assert_eq!(tags_arr[0], Value::String("admin".to_string()));
assert_eq!(tags_arr[1], Value::String("Charlie".to_string()));
let values = doc.get("values").unwrap();
let values_arr = values.as_array().unwrap();
assert_eq!(values_arr[0], Value::I32(1));
assert_eq!(values_arr[1], Value::I32(5));
assert_eq!(values_arr[2], Value::I32(6));
}
#[test]
fn test_doc_macro_empty_new_syntax() {
let doc = doc!{};
assert!(doc.is_empty());
}
#[test]
fn test_merge_nested_document_in_place() {
let mut doc1 = doc!{
nested: {
key1: "value1",
inner: {
a: 1
}
}
};
let doc2 = doc!{
nested: {
key2: "value2",
inner: {
b: 2
}
}
};
doc1.merge(&doc2).unwrap();
assert_eq!(doc1.get("nested.key1").unwrap(), Value::String("value1".to_string()));
assert_eq!(doc1.get("nested.key2").unwrap(), Value::String("value2".to_string()));
assert_eq!(doc1.get("nested.inner.a").unwrap(), Value::I32(1));
assert_eq!(doc1.get("nested.inner.b").unwrap(), Value::I32(2));
}
#[test]
fn test_merge_document_overwrites_non_document() {
let mut doc1 = doc!{
field: "string_value"
};
let doc2 = doc!{
field: {
nested: "value"
}
};
doc1.merge(&doc2).unwrap();
assert_eq!(doc1.get("field.nested").unwrap(), Value::String("value".to_string()));
}
#[test]
fn test_fields_with_prefix() {
let doc = doc!{
level1: {
level2: {
leaf: "value"
}
}
};
let fields = doc.fields();
assert!(fields.contains(&"level1.level2.leaf".to_string()));
}
#[test]
fn test_iterator_size_hint() {
let doc = doc!{
key1: "value1",
key2: "value2",
key3: "value3"
};
let iter = doc.iter();
let (lower, upper) = iter.size_hint();
assert_eq!(lower, 3);
assert_eq!(upper, Some(3));
}
#[test]
fn test_remove_array_element_by_index() {
let mut doc = doc!{
items: [1, 2, 3, 4, 5]
};
doc.remove("items.2").unwrap();
let items = doc.get("items").unwrap();
let arr = items.as_array().unwrap();
assert_eq!(arr.len(), 4);
assert_eq!(arr[0], Value::I32(1));
assert_eq!(arr[1], Value::I32(2));
assert_eq!(arr[2], Value::I32(4)); assert_eq!(arr[3], Value::I32(5)); }
#[test]
fn test_remove_nested_document_in_array() {
let mut doc = doc!{
items: [
{ name: "item1", value: 1 },
{ name: "item2", value: 2 },
{ name: "item3", value: 3 }
]
};
doc.remove("items.1.value").unwrap();
let item1 = doc.get("items.1").unwrap();
let item_doc = item1.as_document().unwrap();
assert_eq!(item_doc.size(), 1);
assert_eq!(item_doc.get("name").unwrap(), Value::String("item2".to_string()));
assert_eq!(item_doc.get("value").unwrap(), Null);
}
#[test]
fn test_remove_entire_nested_document_in_array_when_empty() {
let mut doc = doc!{
items: [
{ only_field: "value" }
]
};
doc.remove("items.0.only_field").unwrap();
let items = doc.get("items").unwrap();
let arr = items.as_array().unwrap();
assert_eq!(arr.len(), 0);
}
#[test]
fn test_remove_array_out_of_bounds() {
let mut doc = doc!{
items: [1, 2, 3]
};
let result = doc.remove("items.10");
assert!(result.is_err());
}
#[test]
fn test_remove_array_negative_index() {
let mut doc = doc!{
items: [1, 2, 3]
};
let result = doc.remove("items.-1");
assert!(result.is_err());
}
#[test]
fn test_remove_array_invalid_index_string() {
let mut doc = doc!{
items: [1, 2, 3]
};
let result = doc.remove("items.abc");
assert!(result.is_err());
}
#[test]
fn test_remove_non_existent_nested_path() {
let mut doc = doc!{
existing: "value"
};
let result = doc.remove("non_existent.nested.path");
assert!(result.is_ok());
}
#[test]
fn test_deep_put_creates_nested_structure() {
let mut doc = doc!{};
doc.put("a.b.c.d", Value::String("deep".to_string())).unwrap();
assert_eq!(doc.get("a.b.c.d").unwrap(), Value::String("deep".to_string()));
}
#[test]
fn test_deep_put_into_existing_nested_document() {
let mut doc = doc!{
existing: {
nested: {
key1: "value1"
}
}
};
doc.put("existing.nested.key2", Value::String("value2".to_string())).unwrap();
assert_eq!(doc.get("existing.nested.key1").unwrap(), Value::String("value1".to_string()));
assert_eq!(doc.get("existing.nested.key2").unwrap(), Value::String("value2".to_string()));
}
#[test]
fn test_fields_skips_reserved_fields() {
let mut doc = doc!{
regular_field: "value"
};
doc.put(DOC_ID, Value::NitriteId(NitriteId::new())).unwrap();
doc.put(DOC_REVISION, Value::I32(1)).unwrap();
doc.put(DOC_SOURCE, Value::String("source".to_string())).unwrap();
doc.put(DOC_MODIFIED, Value::I64(12345)).unwrap();
let fields = doc.fields();
assert_eq!(fields.len(), 1);
assert!(fields.contains(&"regular_field".to_string()));
assert!(!fields.contains(&DOC_ID.to_string()));
assert!(!fields.contains(&DOC_REVISION.to_string()));
}
#[test]
fn test_fields_skips_empty_keys() {
let doc = doc!{
valid_key: "value"
};
let fields = doc.fields();
assert_eq!(fields.len(), 1);
assert!(fields.contains(&"valid_key".to_string()));
}
#[test]
fn test_to_pretty_json_empty() {
let doc = doc!{};
let json = doc.to_pretty_json(0);
assert_eq!(json, "{}");
}
#[test]
fn test_to_debug_string_empty() {
let doc = doc!{};
let debug = doc.to_debug_string(0);
assert_eq!(debug, "{}");
}
#[test]
fn test_to_pretty_json_with_indent() {
let doc = doc!{
key: "value"
};
let json = doc.to_pretty_json(2);
assert!(json.contains(" \"key\"")); }
#[test]
fn test_to_debug_string_with_indent() {
let doc = doc!{
key: "value"
};
let debug = doc.to_debug_string(2);
assert!(debug.contains(" \"key\"")); }
#[test]
fn test_contains_field_embedded_path() {
let mut doc = doc!{
outer: "temp"
};
let inner = doc!{
inner: "value"
};
doc.put("outer", inner).unwrap();
assert!(doc.contains_field("outer"));
assert!(doc.contains_field("outer.inner"));
assert!(!doc.contains_field("outer.nonexistent"));
assert!(!doc.contains_field("nonexistent.path"));
}
#[test]
fn test_deep_get_non_embedded_key() {
let doc = doc!{
name: "test"
};
let result = doc.get("nonexistent");
assert!(result.is_ok());
assert_eq!(result.unwrap(), Value::Null);
}
#[cfg(test)]
mod custom_separator_test {
use super::*;
use crate::nitrite_config::NitriteConfig;
#[test]
#[cfg_attr(not(feature = "custom_separator"), ignore)]
fn custom_separator_test_remove_array() {
NitriteConfig::new().set_field_separator(":").expect("Failed to set separator");
let mut doc = set_up();
assert_eq!(
doc.get("location:address:house")
.unwrap()
.as_array()
.unwrap()
.len(),
3
);
doc.remove("location:address:house:0").unwrap();
assert_eq!(
doc.get("location:address:house")
.unwrap()
.as_array()
.unwrap()
.len(),
2
);
assert_eq!(doc.get("obj_array").unwrap().as_array().unwrap().len(), 2);
doc.remove("obj_array:0:value").unwrap();
assert_eq!(doc.get("obj_array").unwrap().as_array().unwrap().len(), 1);
assert_eq!(
doc.get("obj_array:0")
.unwrap()
.as_document()
.unwrap()
.size(),
1
);
}
#[test]
#[cfg_attr(not(feature = "custom_separator"), ignore)]
fn custom_separator_test_remove() {
NitriteConfig::new().set_field_separator(":").expect("Failed to set separator");
let mut doc = set_up();
assert_eq!(
doc.get("location:address")
.unwrap()
.as_document()
.unwrap()
.size(),
4
);
doc.remove("location:address:line1").unwrap();
assert_eq!(
doc.get("location:address")
.unwrap()
.as_document()
.unwrap()
.size(),
3
);
}
#[test]
#[cfg_attr(not(feature = "custom_separator"), ignore)]
fn custom_separator_test_get() {
NitriteConfig::new().set_field_separator(":").expect("Failed to set separator");
let doc = set_up();
let mut value = doc.get("").unwrap();
assert_eq!(value, Null);
value = doc.get("score").unwrap();
assert_eq!(value, Value::I32(1034));
value = doc.get("location:state").unwrap();
assert_eq!(value, Value::String("NY".to_string()));
value = doc.get("location:address").unwrap();
assert_eq!(
value,
Value::Document(doc!{
line1: "40",
line2: "ABC Street",
house: ["1", "2", "3"],
zip: 10001,
})
);
value = doc.get("location:address:line1").unwrap();
assert_eq!(value, Value::String("40".to_string()));
value = doc.get("location:address:line2").unwrap();
assert_eq!(value, Value::String("ABC Street".to_string()));
value = doc.get("location:address:house:0").unwrap();
assert_eq!(value, Value::String("1".to_string()));
value = doc.get("location:address:house:1").unwrap();
assert_eq!(value, Value::String("2".to_string()));
value = doc.get("location:address:house:2").unwrap();
assert_eq!(value, Value::String("3".to_string()));
value = doc.get("location:address:zip").unwrap();
assert_eq!(value, Value::I32(10001));
value = doc.get("category:0").unwrap();
assert_eq!(value, Value::String("food".to_string()));
value = doc.get("category:1").unwrap();
assert_eq!(value, Value::String("produce".to_string()));
value = doc.get("category:2").unwrap();
assert_eq!(value, Value::String("grocery".to_string()));
value = doc.get("obj_array:0").unwrap();
assert_eq!(value, Value::Document(doc!{ value: 1 }));
value = doc.get("obj_array:1").unwrap();
assert_eq!(value, Value::Document(doc!{ value: 2 }));
value = doc.get("obj_array:0:value").unwrap();
assert_eq!(value, Value::I32(1));
value = doc.get("obj_array:1:value").unwrap();
assert_eq!(value, Value::I32(2));
value = doc.get("location:address:test").unwrap();
assert_eq!(value, Null);
assert!(doc.get("location:address:house:3").is_err());
assert!(doc.get("location:address:house:-1").is_err());
assert!(doc.get(":").is_err());
assert!(doc.get("::").is_err());
assert_eq!(doc.get("score:test").unwrap(), Null);
}
#[test]
#[cfg_attr(not(feature = "custom_separator"), ignore)]
fn custom_separator_test_default_separator_fails() {
NitriteConfig::new().set_field_separator(":").expect("Failed to set separator");
let doc = set_up();
let value = doc.get("location.address.house.0").unwrap();
assert_eq!(value, Null);
let value = doc.get("location:address:house:0").unwrap();
assert_eq!(value, Value::String("1".to_string()));
}
}
}