use std::collections::HashSet;
use std::sync::Arc;
use crate::data::value::Val;
use crate::util::JsonView;
pub(crate) trait ValueView<'a>: Clone {
fn scalar(&self) -> JsonView<'_>;
fn field(&self, key: &str) -> Self;
fn has_key(&self, key: &str) -> Option<bool>;
fn object_keys(&self) -> Option<Val>;
fn object_values(&self) -> Option<Val>;
fn object_entries(&self) -> Option<Val>;
fn pick_keys(&self, keys: &[Arc<str>]) -> Option<Val>;
fn omit_keys(&self, keys: &[Arc<str>]) -> Option<Val>;
fn index(&self, idx: i64) -> Self;
fn array_iter(&self) -> Option<Box<dyn Iterator<Item = Self> + 'a>>;
fn materialize(&self) -> Val;
}
#[inline]
pub(crate) fn scalar_view_to_owned_val(view: JsonView<'_>) -> Option<Val> {
match view {
JsonView::Null => Some(Val::Null),
JsonView::Bool(value) => Some(Val::Bool(value)),
JsonView::Int(value) => Some(Val::Int(value)),
JsonView::UInt(value) => Some(if value <= i64::MAX as u64 {
Val::Int(value as i64)
} else {
Val::Float(value as f64)
}),
JsonView::Float(value) => Some(Val::Float(value)),
JsonView::Str(value) => Some(Val::Str(Arc::from(value))),
JsonView::ArrayLen(_) | JsonView::ObjectLen(_) => None,
}
}
#[derive(Clone)]
pub(crate) enum ValView<'a> {
Borrowed(&'a Val),
Owned(Val),
}
impl<'a> ValView<'a> {
#[inline]
pub(crate) fn new(value: &'a Val) -> Self {
Self::Borrowed(value)
}
#[inline]
fn value(&self) -> &Val {
match self {
Self::Borrowed(value) => value,
Self::Owned(value) => value,
}
}
}
impl<'a> ValueView<'a> for ValView<'a> {
#[inline]
fn scalar(&self) -> JsonView<'_> {
JsonView::from_val(self.value())
}
#[inline]
fn field(&self, key: &str) -> Self {
match self {
Self::Borrowed(Val::Obj(map)) => map
.get(key)
.map(Self::Borrowed)
.unwrap_or_else(|| Self::Owned(Val::Null)),
Self::Borrowed(Val::ObjSmall(pairs)) => pairs
.iter()
.find_map(|(k, v)| (k.as_ref() == key).then_some(Self::Borrowed(v)))
.unwrap_or_else(|| Self::Owned(Val::Null)),
Self::Borrowed(_) => Self::Owned(Val::Null),
Self::Owned(value) => Self::Owned(value.get_field(key)),
}
}
#[inline]
fn has_key(&self, key: &str) -> Option<bool> {
match self.value() {
Val::Obj(map) => Some(map.contains_key(key)),
Val::ObjSmall(pairs) => Some(pairs.iter().any(|(k, _)| k.as_ref() == key)),
_ => None,
}
}
#[inline]
fn object_keys(&self) -> Option<Val> {
match self.value() {
Val::Obj(map) => Some(Val::arr(
map.keys().cloned().map(Val::Str).collect::<Vec<_>>(),
)),
Val::ObjSmall(pairs) => Some(Val::arr(
pairs
.iter()
.map(|(key, _)| Val::Str(Arc::clone(key)))
.collect::<Vec<_>>(),
)),
_ => None,
}
}
#[inline]
fn object_values(&self) -> Option<Val> {
match self.value() {
Val::Obj(map) => Some(Val::arr(map.values().cloned().collect::<Vec<_>>())),
Val::ObjSmall(pairs) => Some(Val::arr(
pairs
.iter()
.map(|(_, value)| value.clone())
.collect::<Vec<_>>(),
)),
_ => None,
}
}
#[inline]
fn object_entries(&self) -> Option<Val> {
match self.value() {
Val::Obj(map) => Some(Val::arr(
map.iter()
.map(|(key, value)| Val::arr(vec![Val::Str(Arc::clone(key)), value.clone()]))
.collect::<Vec<_>>(),
)),
Val::ObjSmall(pairs) => Some(Val::arr(
pairs
.iter()
.map(|(key, value)| Val::arr(vec![Val::Str(Arc::clone(key)), value.clone()]))
.collect::<Vec<_>>(),
)),
_ => None,
}
}
#[inline]
fn pick_keys(&self, keys: &[Arc<str>]) -> Option<Val> {
match self.value() {
Val::Obj(map) => {
let mut out = indexmap::IndexMap::with_capacity(keys.len());
for key in keys {
if let Some(value) = map.get(key.as_ref()) {
out.insert(Arc::clone(key), value.clone());
}
}
Some(Val::obj(out))
}
Val::ObjSmall(pairs) => {
let mut out = indexmap::IndexMap::with_capacity(keys.len());
for key in keys {
if let Some((_, value)) = pairs.iter().find(|(k, _)| k.as_ref() == key.as_ref())
{
out.insert(Arc::clone(key), value.clone());
}
}
Some(Val::obj(out))
}
_ => None,
}
}
#[inline]
fn omit_keys(&self, keys: &[Arc<str>]) -> Option<Val> {
let omitted: HashSet<&str> = keys.iter().map(|key| key.as_ref()).collect();
match self.value() {
Val::Obj(map) => Some(Val::obj(
map.iter()
.filter(|(key, _)| !omitted.contains(key.as_ref()))
.map(|(key, value)| (Arc::clone(key), value.clone()))
.collect(),
)),
Val::ObjSmall(pairs) => Some(Val::obj(
pairs
.iter()
.filter(|(key, _)| !omitted.contains(key.as_ref()))
.map(|(key, value)| (Arc::clone(key), value.clone()))
.collect(),
)),
_ => None,
}
}
#[inline]
fn index(&self, idx: i64) -> Self {
match self {
Self::Borrowed(Val::Arr(items)) => normalize_index(items.len(), idx)
.and_then(|idx| items.get(idx))
.map(Self::Borrowed)
.unwrap_or_else(|| Self::Owned(Val::Null)),
Self::Borrowed(Val::IntVec(items)) => normalize_index(items.len(), idx)
.and_then(|idx| items.get(idx).copied())
.map(Val::Int)
.map(Self::Owned)
.unwrap_or_else(|| Self::Owned(Val::Null)),
Self::Borrowed(Val::FloatVec(items)) => normalize_index(items.len(), idx)
.and_then(|idx| items.get(idx).copied())
.map(Val::Float)
.map(Self::Owned)
.unwrap_or_else(|| Self::Owned(Val::Null)),
Self::Borrowed(Val::StrVec(items)) => normalize_index(items.len(), idx)
.and_then(|idx| items.get(idx).cloned())
.map(Val::Str)
.map(Self::Owned)
.unwrap_or_else(|| Self::Owned(Val::Null)),
Self::Borrowed(Val::StrSliceVec(items)) => normalize_index(items.len(), idx)
.and_then(|idx| items.get(idx).cloned())
.map(Val::StrSlice)
.map(Self::Owned)
.unwrap_or_else(|| Self::Owned(Val::Null)),
Self::Borrowed(_) => Self::Owned(Val::Null),
Self::Owned(value) => Self::Owned(value.get_index(idx)),
}
}
#[inline]
fn array_iter(&self) -> Option<Box<dyn Iterator<Item = Self> + 'a>> {
match self {
Self::Borrowed(Val::Arr(items)) => Some(Box::new(items.iter().map(Self::Borrowed))),
Self::Borrowed(Val::IntVec(items)) => Some(Box::new(
items.iter().copied().map(Val::Int).map(Self::Owned),
)),
Self::Borrowed(Val::FloatVec(items)) => Some(Box::new(
items.iter().copied().map(Val::Float).map(Self::Owned),
)),
Self::Borrowed(Val::StrVec(items)) => Some(Box::new(
items.iter().cloned().map(Val::Str).map(Self::Owned),
)),
Self::Borrowed(Val::StrSliceVec(items)) => Some(Box::new(
items.iter().cloned().map(Val::StrSlice).map(Self::Owned),
)),
Self::Borrowed(_) => None,
Self::Owned(value) => match value {
Val::Arr(items) => Some(Box::new(
Arc::clone(items)
.as_ref()
.clone()
.into_iter()
.map(Self::Owned),
)),
Val::IntVec(items) => Some(Box::new(
Arc::clone(items)
.as_ref()
.clone()
.into_iter()
.map(Val::Int)
.map(Self::Owned),
)),
Val::FloatVec(items) => Some(Box::new(
Arc::clone(items)
.as_ref()
.clone()
.into_iter()
.map(Val::Float)
.map(Self::Owned),
)),
Val::StrVec(items) => Some(Box::new(
Arc::clone(items)
.as_ref()
.clone()
.into_iter()
.map(Val::Str)
.map(Self::Owned),
)),
Val::StrSliceVec(items) => Some(Box::new(
Arc::clone(items)
.as_ref()
.clone()
.into_iter()
.map(Val::StrSlice)
.map(Self::Owned),
)),
_ => None,
},
}
}
#[inline]
fn materialize(&self) -> Val {
self.value().clone()
}
}
#[derive(Clone, Copy)]
pub(crate) enum TapeView<'a> {
Node {
tape: &'a crate::data::tape::TapeData,
idx: usize,
},
Missing,
}
impl<'a> TapeView<'a> {
#[inline]
pub(crate) fn root(tape: &'a crate::data::tape::TapeData) -> Self {
if tape.nodes.is_empty() {
Self::Missing
} else {
Self::Node { tape, idx: 0 }
}
}
#[inline]
fn materialize_at(tape: &'a crate::data::tape::TapeData, idx: &mut usize) -> Val {
use crate::data::tape::TapeNode;
use simd_json::StaticNode as SN;
let here = tape.nodes[*idx];
*idx += 1;
match here {
TapeNode::Static(SN::Null) => Val::Null,
TapeNode::Static(SN::Bool(b)) => Val::Bool(b),
TapeNode::Static(SN::I64(n)) => Val::Int(n),
TapeNode::Static(SN::U64(n)) => {
if n <= i64::MAX as u64 {
Val::Int(n as i64)
} else {
Val::Float(n as f64)
}
}
TapeNode::Static(SN::F64(f)) => Val::Float(f),
TapeNode::String(_) => Val::StrSlice(tape.str_ref_at(*idx - 1)),
TapeNode::Array { len, .. } => {
let mut out = Vec::with_capacity(len);
for _ in 0..len {
out.push(Self::materialize_at(tape, idx));
}
Val::arr(out)
}
TapeNode::Object { len, .. } => {
let mut out = indexmap::IndexMap::with_capacity(len);
for _ in 0..len {
let key = tape.str_at(*idx);
*idx += 1;
let value = Self::materialize_at(tape, idx);
out.insert(crate::data::value::intern_key(key), value);
}
Val::Obj(std::sync::Arc::new(out))
}
}
}
}
impl<'a> ValueView<'a> for TapeView<'a> {
#[inline]
fn scalar(&self) -> JsonView<'_> {
use crate::data::tape::TapeNode;
use simd_json::StaticNode as SN;
let Self::Node { tape, idx } = self else {
return JsonView::Null;
};
match tape.nodes[*idx] {
TapeNode::Static(SN::Null) => JsonView::Null,
TapeNode::Static(SN::Bool(b)) => JsonView::Bool(b),
TapeNode::Static(SN::I64(n)) => JsonView::Int(n),
TapeNode::Static(SN::U64(n)) => JsonView::UInt(n),
TapeNode::Static(SN::F64(f)) => JsonView::Float(f),
TapeNode::String(_) => JsonView::Str(tape.str_at(*idx)),
TapeNode::Array { len, .. } => JsonView::ArrayLen(len),
TapeNode::Object { len, .. } => JsonView::ObjectLen(len),
}
}
#[inline]
fn field(&self, key: &str) -> Self {
use crate::data::tape::TapeNode;
let Self::Node { tape, idx } = self else {
return Self::Missing;
};
let TapeNode::Object { len, .. } = tape.nodes[*idx] else {
return Self::Missing;
};
let mut cur = *idx + 1;
for _ in 0..len {
let current_key = tape.str_at(cur);
cur += 1;
if current_key == key {
return Self::Node { tape, idx: cur };
}
cur += tape.span(cur);
}
Self::Missing
}
#[inline]
fn has_key(&self, key: &str) -> Option<bool> {
use crate::data::tape::TapeNode;
let Self::Node { tape, idx } = self else {
return None;
};
let TapeNode::Object { len, .. } = tape.nodes[*idx] else {
return None;
};
let mut cur = *idx + 1;
for _ in 0..len {
let current_key = tape.str_at(cur);
cur += 1;
if current_key == key {
return Some(true);
}
cur += tape.span(cur);
}
Some(false)
}
#[inline]
fn object_keys(&self) -> Option<Val> {
use crate::data::tape::TapeNode;
let Self::Node { tape, idx } = self else {
return None;
};
let TapeNode::Object { len, .. } = tape.nodes[*idx] else {
return None;
};
let mut out = Vec::with_capacity(len);
let mut cur = *idx + 1;
for _ in 0..len {
out.push(Val::Str(Arc::from(tape.str_at(cur))));
cur += 1;
cur += tape.span(cur);
}
Some(Val::arr(out))
}
#[inline]
fn object_values(&self) -> Option<Val> {
use crate::data::tape::TapeNode;
let Self::Node { tape, idx } = self else {
return None;
};
let TapeNode::Object { len, .. } = tape.nodes[*idx] else {
return None;
};
let mut out = Vec::with_capacity(len);
let mut cur = *idx + 1;
for _ in 0..len {
cur += 1;
let mut value_idx = cur;
out.push(Self::materialize_at(tape, &mut value_idx));
cur += tape.span(cur);
}
Some(Val::arr(out))
}
#[inline]
fn object_entries(&self) -> Option<Val> {
use crate::data::tape::TapeNode;
let Self::Node { tape, idx } = self else {
return None;
};
let TapeNode::Object { len, .. } = tape.nodes[*idx] else {
return None;
};
let mut out = Vec::with_capacity(len);
let mut cur = *idx + 1;
for _ in 0..len {
let key = Arc::from(tape.str_at(cur));
cur += 1;
let mut value_idx = cur;
out.push(Val::arr(vec![
Val::Str(key),
Self::materialize_at(tape, &mut value_idx),
]));
cur += tape.span(cur);
}
Some(Val::arr(out))
}
#[inline]
fn pick_keys(&self, keys: &[Arc<str>]) -> Option<Val> {
use crate::data::tape::TapeNode;
let Self::Node { tape, idx } = self else {
return None;
};
let TapeNode::Object { len, .. } = tape.nodes[*idx] else {
return None;
};
let mut out = indexmap::IndexMap::with_capacity(keys.len());
let mut remaining: HashSet<&str> = keys.iter().map(|key| key.as_ref()).collect();
let mut cur = *idx + 1;
for _ in 0..len {
let current_key = tape.str_at(cur);
cur += 1;
if remaining.remove(current_key) {
let mut value_idx = cur;
out.insert(
crate::data::value::intern_key(current_key),
Self::materialize_at(tape, &mut value_idx),
);
if remaining.is_empty() {
break;
}
}
cur += tape.span(cur);
}
Some(Val::obj(out))
}
#[inline]
fn omit_keys(&self, keys: &[Arc<str>]) -> Option<Val> {
use crate::data::tape::TapeNode;
let Self::Node { tape, idx } = self else {
return None;
};
let TapeNode::Object { len, .. } = tape.nodes[*idx] else {
return None;
};
let omitted: HashSet<&str> = keys.iter().map(|key| key.as_ref()).collect();
let mut out = indexmap::IndexMap::with_capacity(len.saturating_sub(omitted.len()));
let mut cur = *idx + 1;
for _ in 0..len {
let current_key = tape.str_at(cur);
cur += 1;
if !omitted.contains(current_key) {
let mut value_idx = cur;
out.insert(
crate::data::value::intern_key(current_key),
Self::materialize_at(tape, &mut value_idx),
);
}
cur += tape.span(cur);
}
Some(Val::obj(out))
}
#[inline]
fn index(&self, idx: i64) -> Self {
use crate::data::tape::TapeNode;
let Self::Node { tape, idx: node } = self else {
return Self::Missing;
};
let TapeNode::Array { len, .. } = tape.nodes[*node] else {
return Self::Missing;
};
let Some(target) = normalize_index(len, idx) else {
return Self::Missing;
};
let mut cur = *node + 1;
for _ in 0..target {
cur += tape.span(cur);
}
Self::Node { tape, idx: cur }
}
#[inline]
fn array_iter(&self) -> Option<Box<dyn Iterator<Item = Self> + 'a>> {
use crate::data::tape::TapeNode;
let Self::Node { tape, idx } = self else {
return None;
};
let TapeNode::Array { len, .. } = tape.nodes[*idx] else {
return None;
};
Some(Box::new(TapeArrayIter {
tape,
remaining: len,
cur: *idx + 1,
}))
}
#[inline]
fn materialize(&self) -> Val {
match self {
Self::Node { tape, idx } => {
#[cfg(test)]
tape.observe_materialized_subtree();
let mut idx = *idx;
Self::materialize_at(tape, &mut idx)
}
Self::Missing => Val::Null,
}
}
}
struct TapeArrayIter<'a> {
tape: &'a crate::data::tape::TapeData,
remaining: usize,
cur: usize,
}
impl<'a> Iterator for TapeArrayIter<'a> {
type Item = TapeView<'a>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
let idx = self.cur;
self.remaining -= 1;
self.cur += self.tape.span(self.cur);
Some(TapeView::Node {
tape: self.tape,
idx,
})
}
}
#[derive(Clone, Copy)]
pub(crate) enum TapeScratchView<'a> {
Node {
tape: &'a crate::data::tape::TapeScratch,
idx: usize,
},
Missing,
}
impl<'a> TapeScratchView<'a> {
#[inline]
fn materialize_at(tape: &'a crate::data::tape::TapeScratch, idx: &mut usize) -> Val {
use crate::data::tape::TapeNode;
use simd_json::StaticNode as SN;
let here = tape.nodes[*idx];
*idx += 1;
match here {
TapeNode::Static(SN::Null) => Val::Null,
TapeNode::Static(SN::Bool(b)) => Val::Bool(b),
TapeNode::Static(SN::I64(n)) => Val::Int(n),
TapeNode::Static(SN::U64(n)) => {
if n <= i64::MAX as u64 {
Val::Int(n as i64)
} else {
Val::Float(n as f64)
}
}
TapeNode::Static(SN::F64(f)) => Val::Float(f),
TapeNode::String(_) => {
Val::StrSlice(crate::data::tape::StrRef::from(tape.str_at(*idx - 1)))
}
TapeNode::Array { len, .. } => {
let mut out = Vec::with_capacity(len);
for _ in 0..len {
out.push(Self::materialize_at(tape, idx));
}
Val::arr(out)
}
TapeNode::Object { len, .. } => {
let mut out = indexmap::IndexMap::with_capacity(len);
for _ in 0..len {
let key = tape.str_at(*idx);
*idx += 1;
let value = Self::materialize_at(tape, idx);
out.insert(crate::data::value::intern_key(key), value);
}
Val::Obj(std::sync::Arc::new(out))
}
}
}
}
impl<'a> ValueView<'a> for TapeScratchView<'a> {
#[inline]
fn scalar(&self) -> JsonView<'_> {
use crate::data::tape::TapeNode;
use simd_json::StaticNode as SN;
let Self::Node { tape, idx } = self else {
return JsonView::Null;
};
match tape.nodes[*idx] {
TapeNode::Static(SN::Null) => JsonView::Null,
TapeNode::Static(SN::Bool(b)) => JsonView::Bool(b),
TapeNode::Static(SN::I64(n)) => JsonView::Int(n),
TapeNode::Static(SN::U64(n)) => JsonView::UInt(n),
TapeNode::Static(SN::F64(f)) => JsonView::Float(f),
TapeNode::String(_) => JsonView::Str(tape.str_at(*idx)),
TapeNode::Array { len, .. } => JsonView::ArrayLen(len),
TapeNode::Object { len, .. } => JsonView::ObjectLen(len),
}
}
#[inline]
fn field(&self, key: &str) -> Self {
use crate::data::tape::TapeNode;
let Self::Node { tape, idx } = self else {
return Self::Missing;
};
let TapeNode::Object { len, .. } = tape.nodes[*idx] else {
return Self::Missing;
};
let mut cur = *idx + 1;
for _ in 0..len {
let current_key = tape.str_at(cur);
cur += 1;
if current_key == key {
return Self::Node { tape, idx: cur };
}
cur += tape.span(cur);
}
Self::Missing
}
#[inline]
fn has_key(&self, key: &str) -> Option<bool> {
use crate::data::tape::TapeNode;
let Self::Node { tape, idx } = self else {
return None;
};
let TapeNode::Object { len, .. } = tape.nodes[*idx] else {
return None;
};
let mut cur = *idx + 1;
for _ in 0..len {
let current_key = tape.str_at(cur);
cur += 1;
if current_key == key {
return Some(true);
}
cur += tape.span(cur);
}
Some(false)
}
fn object_keys(&self) -> Option<Val> {
let materialized = self.materialize();
ValView::new(&materialized).object_keys()
}
fn object_values(&self) -> Option<Val> {
let materialized = self.materialize();
ValView::new(&materialized).object_values()
}
fn object_entries(&self) -> Option<Val> {
let materialized = self.materialize();
ValView::new(&materialized).object_entries()
}
fn pick_keys(&self, keys: &[Arc<str>]) -> Option<Val> {
let materialized = self.materialize();
ValView::new(&materialized).pick_keys(keys)
}
fn omit_keys(&self, keys: &[Arc<str>]) -> Option<Val> {
let materialized = self.materialize();
ValView::new(&materialized).omit_keys(keys)
}
#[inline]
fn index(&self, idx: i64) -> Self {
use crate::data::tape::TapeNode;
let Self::Node { tape, idx: node } = self else {
return Self::Missing;
};
let TapeNode::Array { len, .. } = tape.nodes[*node] else {
return Self::Missing;
};
let Some(target) = normalize_index(len, idx) else {
return Self::Missing;
};
let mut cur = *node + 1;
for _ in 0..target {
cur += tape.span(cur);
}
Self::Node { tape, idx: cur }
}
#[inline]
fn array_iter(&self) -> Option<Box<dyn Iterator<Item = Self> + 'a>> {
use crate::data::tape::TapeNode;
let Self::Node { tape, idx } = self else {
return None;
};
let TapeNode::Array { len, .. } = tape.nodes[*idx] else {
return None;
};
Some(Box::new(TapeScratchArrayIter {
tape,
remaining: len,
cur: *idx + 1,
}))
}
#[inline]
fn materialize(&self) -> Val {
match self {
Self::Node { tape, idx } => {
let mut idx = *idx;
Self::materialize_at(tape, &mut idx)
}
Self::Missing => Val::Null,
}
}
}
struct TapeScratchArrayIter<'a> {
tape: &'a crate::data::tape::TapeScratch,
remaining: usize,
cur: usize,
}
impl<'a> Iterator for TapeScratchArrayIter<'a> {
type Item = TapeScratchView<'a>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
let idx = self.cur;
self.remaining -= 1;
self.cur += self.tape.span(self.cur);
Some(TapeScratchView::Node {
tape: self.tape,
idx,
})
}
}
#[inline]
fn normalize_index(len: usize, idx: i64) -> Option<usize> {
let idx = if idx < 0 {
len.checked_sub(idx.unsigned_abs() as usize)?
} else {
idx as usize
};
(idx < len).then_some(idx)
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use serde_json::json;
use super::{scalar_view_to_owned_val, ValView, ValueView};
use crate::util::{json_cmp_binop, JsonView};
use crate::{data::value::Val, parse::ast::BinOp};
#[test]
fn val_view_reads_nested_fields_without_materializing_parent() {
let value = Val::from(&json!({
"book": {"title": "Dune", "score": 901},
"unused": {"payload": [1, 2, 3]}
}));
let root = ValView::new(&value);
let title = root.field("book").field("title");
let score = root.field("book").field("score");
assert!(matches!(title.scalar(), JsonView::Str("Dune")));
assert!(json_cmp_binop(
score.scalar(),
BinOp::Gt,
JsonView::Int(900)
));
}
#[test]
fn val_view_checks_object_keys_without_reading_values() {
let value = Val::from(&json!({
"book": {"title": "Dune", "score": 901}
}));
let book = ValView::new(&value).field("book");
assert_eq!(book.has_key("title"), Some(true));
assert_eq!(book.has_key("missing"), Some(false));
assert_eq!(book.field("title").has_key("x"), None);
}
#[test]
fn val_view_object_helpers_match_object_semantics() {
let value = Val::from(&json!({
"book": {"title": "Dune", "score": 901, "debug": true}
}));
let book = ValView::new(&value).field("book");
assert_eq!(
serde_json::Value::from(book.object_keys().unwrap()),
json!(["debug", "score", "title"])
);
assert_eq!(
serde_json::Value::from(book.pick_keys(&[Arc::from("score")]).unwrap()),
json!({"score": 901})
);
assert_eq!(
serde_json::Value::from(book.omit_keys(&[Arc::from("debug")]).unwrap()),
json!({"score": 901, "title": "Dune"})
);
}
#[test]
fn val_view_indexes_columnar_arrays() {
let nums = Val::IntVec(Arc::new(vec![10, 20, 30]));
let view = ValView::new(&nums);
assert!(matches!(view.index(1).scalar(), JsonView::Int(20)));
assert!(matches!(view.index(-1).scalar(), JsonView::Int(30)));
assert!(matches!(view.index(99).scalar(), JsonView::Null));
}
#[test]
fn val_view_materializes_current_view_only() {
let value = Val::from(&json!({"items": [{"id": 1}, {"id": 2}]}));
let item = ValView::new(&value).field("items").index(1);
assert_eq!(
serde_json::Value::from(item.materialize()),
json!({"id": 2})
);
}
#[test]
fn scalar_view_to_owned_val_converts_only_scalars() {
assert_eq!(scalar_view_to_owned_val(JsonView::Null), Some(Val::Null));
assert_eq!(
scalar_view_to_owned_val(JsonView::Str("ada")),
Some(Val::Str(Arc::from("ada")))
);
assert!(scalar_view_to_owned_val(JsonView::ArrayLen(3)).is_none());
assert!(scalar_view_to_owned_val(JsonView::ObjectLen(2)).is_none());
}
#[test]
fn tape_view_matches_val_view_for_field_index_scalar_reads() {
use super::TapeView;
let bytes =
br#"{"books":[{"title":"low","score":1},{"title":"Dune","score":901}]}"#.to_vec();
let tape = crate::data::tape::TapeData::parse(bytes).unwrap();
let val = Val::from_tape_data(&tape);
let tape_score_view = TapeView::root(&tape).field("books").index(1).field("score");
let tape_score = tape_score_view.scalar();
let val_score_view = ValView::new(&val).field("books").index(1).field("score");
let val_score = val_score_view.scalar();
assert!(matches!(
(tape_score, val_score),
(JsonView::Int(901), JsonView::Int(901))
));
let tape_book = TapeView::root(&tape).field("books").index(1);
let val_book = ValView::new(&val).field("books").index(1);
assert_eq!(tape_book.has_key("title"), Some(true));
assert_eq!(tape_book.has_key("missing"), Some(false));
assert_eq!(tape_book.has_key("title"), val_book.has_key("title"));
assert_eq!(
tape_book.object_keys().map(serde_json::Value::from),
val_book.object_keys().map(serde_json::Value::from)
);
assert_eq!(
tape_book.object_values().map(serde_json::Value::from),
val_book.object_values().map(serde_json::Value::from)
);
assert_eq!(
tape_book.object_entries().map(serde_json::Value::from),
val_book.object_entries().map(serde_json::Value::from)
);
assert_eq!(
tape_book
.pick_keys(&[Arc::from("score")])
.map(serde_json::Value::from),
val_book
.pick_keys(&[Arc::from("score")])
.map(serde_json::Value::from)
);
assert_eq!(
tape_book
.omit_keys(&[Arc::from("title")])
.map(serde_json::Value::from),
val_book
.omit_keys(&[Arc::from("title")])
.map(serde_json::Value::from)
);
}
#[test]
fn tape_view_materializes_current_subtree_only() {
use super::TapeView;
let tape = crate::data::tape::TapeData::parse(
br#"{"items":[{"id":1},{"id":2}],"unused":[0]}"#.to_vec(),
)
.unwrap();
let item = TapeView::root(&tape).field("items").index(1);
assert_eq!(
serde_json::Value::from(item.materialize()),
json!({"id": 2})
);
}
}