use serde_json::{Map, Value};
use std::borrow::Cow;
use std::sync::{
atomic::{AtomicU64, Ordering},
Arc,
};
use crate::jsoneval::path_utils;
static NEXT_INSTANCE_ID: AtomicU64 = AtomicU64::new(0);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DataVersion(pub u64);
pub struct EvalData {
instance_id: u64,
data: Arc<Value>,
}
impl EvalData {
pub fn new(data: Value) -> Self {
Self {
instance_id: NEXT_INSTANCE_ID.fetch_add(1, Ordering::Relaxed),
data: Arc::new(data),
}
}
#[inline]
pub fn from_arc(data: Arc<Value>) -> Self {
Self {
instance_id: NEXT_INSTANCE_ID.fetch_add(1, Ordering::Relaxed),
data,
}
}
pub fn with_schema_data_context(
evaluated_schema: &Value,
input_data: &Value,
context_data: &Value,
) -> Self {
let mut data_map = Map::new();
if let Some(params) = evaluated_schema.get("$params") {
data_map.insert("$params".to_string(), params.clone());
}
if let Value::Object(input_obj) = input_data {
for (key, value) in input_obj {
data_map.insert(key.clone(), value.clone());
}
}
data_map.insert("$context".to_string(), context_data.clone());
Self::new(Value::Object(data_map))
}
pub fn replace_data_and_context(&mut self, input_data: Value, context_data: Value) {
let data = Arc::make_mut(&mut self.data); input_data
.as_object()
.unwrap()
.iter()
.for_each(|(key, value)| {
Self::set_by_pointer(data, &format!("/{key}"), value.clone());
});
Self::set_by_pointer(data, "/$context", context_data);
}
#[inline(always)]
pub fn instance_id(&self) -> u64 {
self.instance_id
}
#[inline(always)]
pub fn data(&self) -> &Value {
&*self.data
}
#[inline(always)]
pub fn snapshot_data(&self) -> Arc<Value> {
Arc::clone(&self.data)
}
#[inline]
pub fn snapshot_data_clone(&self) -> Value {
(*self.data).clone()
}
#[inline]
pub fn exclusive_clone(&self) -> Self {
Self::new((*self.data).clone())
}
pub fn set(&mut self, path: &str, value: Value) {
let pointer = path_utils::normalize_to_json_pointer(path);
let data = Arc::make_mut(&mut self.data); Self::set_by_pointer(data, &pointer, value);
}
pub fn push_to_array(&mut self, path: &str, value: Value) {
let pointer = path_utils::normalize_to_json_pointer(path);
let data = Arc::make_mut(&mut self.data); if let Some(arr) = data.pointer_mut(&pointer) {
if let Some(array) = arr.as_array_mut() {
array.push(value);
}
}
}
#[inline]
pub fn get(&self, path: &str) -> Option<&Value> {
let pointer = path_utils::normalize_to_json_pointer(path);
path_utils::get_value_by_pointer(&self.data, &pointer)
}
#[inline]
pub fn get_without_properties(&self, path: &str) -> Option<&Value> {
let pointer = path_utils::normalize_to_json_pointer(path);
path_utils::get_value_by_pointer_without_properties(&self.data, &pointer)
}
#[inline]
pub fn get_array_element(&self, array_path: &str, index: usize) -> Option<&Value> {
let pointer = path_utils::normalize_to_json_pointer(array_path);
path_utils::get_array_element_by_pointer(&self.data, &pointer, index)
}
pub fn get_mut(&mut self, path: &str) -> Option<&mut Value> {
let pointer = path_utils::normalize_to_json_pointer(path);
let data = Arc::make_mut(&mut self.data); if pointer.is_empty() {
Some(data)
} else {
data.pointer_mut(&pointer)
}
}
#[inline(always)]
pub fn get_table_row_mut(
&mut self,
path: &str,
index: usize,
) -> Option<&mut Map<String, Value>> {
let pointer = path_utils::normalize_to_json_pointer(path);
let data = Arc::make_mut(&mut self.data); let array = if pointer.is_empty() {
data
} else {
data.pointer_mut(&pointer)?
};
array.as_array_mut()?.get_mut(index)?.as_object_mut()
}
#[inline(always)]
pub fn get_table_row_mut_by_segments(
&mut self,
segments: &[&str],
index: usize,
) -> Option<&mut Map<String, Value>> {
let mut target = Arc::make_mut(&mut self.data);
for &segment in segments {
target = match target {
Value::Object(map) => map.get_mut(segment)?,
Value::Array(list) => {
let idx = segment.parse::<usize>().ok()?;
list.get_mut(idx)?
}
_ => return None,
};
}
target.as_array_mut()?.get_mut(index)?.as_object_mut()
}
pub fn get_values<'a>(&'a self, paths: &'a [String]) -> Vec<Cow<'a, Value>> {
let pointers: Vec<String> = paths
.iter()
.map(|path| path_utils::normalize_to_json_pointer(path).into_owned())
.collect();
path_utils::get_values_by_pointers(&self.data, &pointers)
.into_iter()
.map(|opt_val| {
opt_val
.map(Cow::Borrowed)
.unwrap_or(Cow::Owned(Value::Null))
})
.collect()
}
fn set_by_pointer(data: &mut Value, pointer: &str, new_value: Value) {
if pointer.is_empty() {
return;
}
let path = &pointer[1..];
let segments: Vec<&str> = path.split('/').collect();
if segments.is_empty() {
return;
}
let mut current = data;
for (i, segment) in segments.iter().enumerate() {
let is_last = i == segments.len() - 1;
if let Ok(index) = segment.parse::<usize>() {
if !current.is_array() {
return; }
let arr = current.as_array_mut().unwrap();
while arr.len() <= index {
arr.push(if is_last {
Value::Null
} else {
Value::Object(Map::new())
});
}
if is_last {
arr[index] = new_value;
return;
} else {
current = &mut arr[index];
}
} else {
if !current.is_object() {
return; }
let map = current.as_object_mut().unwrap();
if is_last {
map.insert(segment.to_string(), new_value);
return;
} else {
let next_segment = segments[i + 1];
let is_array_next = next_segment.parse::<usize>().is_ok();
current = map.entry(segment.to_string()).or_insert_with(|| {
if is_array_next {
Value::Array(Vec::new())
} else {
Value::Object(Map::new())
}
});
}
}
}
}
}
impl From<Value> for EvalData {
fn from(value: Value) -> Self {
Self::new(value)
}
}
impl Clone for EvalData {
fn clone(&self) -> Self {
Self {
instance_id: self.instance_id, data: Arc::clone(&self.data), }
}
}