use crate::jsoneval::path_utils;
use serde_json::{Number, Value};
#[inline(always)]
pub fn f64_to_json(f: f64, safe_nan_handling: bool) -> Value {
if f.is_finite() {
if f == f.floor() && f.abs() < 9007199254740991.0 {
return Value::Number(Number::from(f as i64));
}
Number::from_f64(f)
.map(Value::Number)
.unwrap_or(Value::Null)
} else if safe_nan_handling {
Value::Number(Number::from(0))
} else {
Value::Null
}
}
#[inline(always)]
pub fn to_f64(value: &Value) -> f64 {
match value {
Value::Number(n) => n.as_f64().unwrap_or(0.0),
Value::Bool(true) => 1.0,
Value::Bool(false) => 0.0,
Value::String(s) => s.parse::<f64>().unwrap_or(0.0),
Value::Array(arr) => {
if arr.len() == 1 {
to_f64(&arr[0])
} else {
0.0
}
}
_ => 0.0,
}
}
#[inline]
pub fn to_number(value: &Value) -> f64 {
to_f64(value)
}
#[inline]
pub fn parse_string_to_f64(s: &str) -> Option<f64> {
if s.is_empty() {
Some(0.0)
} else {
s.parse::<f64>().ok()
}
}
#[inline]
pub fn to_string(value: &Value) -> String {
match value {
Value::Null => String::new(),
Value::Bool(b) => b.to_string(),
Value::Number(n) => {
if let Some(f) = n.as_f64() {
if f.is_finite() && f == f.floor() && f.abs() < 1e15 {
format!("{}", f as i64)
} else {
n.to_string()
}
} else {
n.to_string()
}
}
Value::String(s) => s.clone(),
Value::Array(_) | Value::Object(_) => value.to_string(),
}
}
#[inline]
pub fn normalize_ref_path(path: &str) -> String {
path_utils::normalize_to_json_pointer(path).into_owned()
}
use crate::rlogic::Evaluator;
impl Evaluator {
#[inline(always)]
pub(crate) fn get_var<'a>(&'a self, data: &'a Value, name: &str) -> Option<&'a Value> {
if name.is_empty() {
return Some(data);
}
if name.starts_with("/$") {
let scope = unsafe { &*self.table_scope.get() };
if let Some(ts) = scope.as_ref() {
if let Some(row_idx) = ts.current_row {
let field = &name[2..]; let rows = unsafe { &*ts.rows };
if let Some(row) = rows.get(row_idx) {
if let Value::Object(obj) = row {
if let Some(cell) = obj.get(field) {
return Some(cell);
}
}
}
}
}
}
if let Some(arrays) = &self.static_arrays {
if name.starts_with("/$params/") && name.len() > 9 {
let end_idx = name[9..].find('/').map(|i| i + 9).unwrap_or(name.len());
let base_path = &name[..end_idx];
if let Some(arc_val) = arrays.get(base_path) {
if end_idx == name.len() {
return Some(&**arc_val);
} else {
let remainder = &name[end_idx..];
return path_utils::get_value_by_pointer_without_properties(
arc_val, remainder,
);
}
}
}
}
let val = path_utils::get_value_by_pointer_without_properties(data, name);
if let Some(v) = val {
if let Some(obj) = v.as_object() {
if let Some(Value::String(static_path)) = obj.get("$static_array") {
if let Some(arrays) = &self.static_arrays {
if let Some(arc_val) = arrays.get(static_path) {
return Some(&**arc_val);
}
}
}
}
}
val
}
#[inline]
pub(crate) fn is_key_missing(&self, data: &Value, key: &str) -> bool {
if key.is_empty() {
return false;
}
let pointer = path_utils::normalize_to_json_pointer(key);
if pointer.is_empty() {
return false;
}
self.get_var(data, &pointer)
.map(|v| v.is_null())
.unwrap_or(true)
}
}
#[inline]
pub fn is_truthy(value: &Value) -> bool {
match value {
Value::Null => false,
Value::Bool(b) => *b,
Value::Number(n) => n.as_f64().unwrap_or(0.0) != 0.0,
Value::String(s) => !s.is_empty(),
Value::Array(arr) => !arr.is_empty(),
Value::Object(_) => true,
}
}
#[inline]
pub fn is_null_like(value: &Value) -> bool {
match value {
Value::Null => true,
Value::String(s) if s.is_empty() => true,
Value::Number(n) if n.is_f64() && n.as_f64().unwrap().is_nan() => true,
_ => false,
}
}
#[inline]
pub fn build_iso_date_string(date: chrono::NaiveDate) -> String {
let mut result = String::with_capacity(24);
result.push_str(&date.format("%Y-%m-%d").to_string());
result.push_str("T00:00:00.000Z");
result
}
#[inline]
pub fn compare(a: &Value, b: &Value) -> f64 {
let num_a = to_f64(a);
let num_b = to_f64(b);
num_a - num_b
}
#[inline]
pub fn create_option(label: &Value, value: &Value) -> Value {
serde_json::json!({"label": label, "value": value})
}
#[inline]
pub fn scalar_hash_key(value: &Value) -> Option<String> {
match value {
Value::Null => Some(String::from("null")),
Value::Bool(b) => Some(b.to_string()),
Value::Number(n) => Some(n.to_string()),
Value::String(s) => Some(s.clone()),
_ => None,
}
}
pub fn loose_equal(a: &Value, b: &Value) -> bool {
match (a, b) {
(Value::Null, Value::Null) => true,
(Value::Bool(a), Value::Bool(b)) => a == b,
(Value::Number(a), Value::Number(b)) => {
let a_f64 = a.as_f64().unwrap_or(0.0);
let b_f64 = b.as_f64().unwrap_or(0.0);
a_f64 == b_f64
}
(Value::String(a), Value::String(b)) => a == b,
(Value::Number(n), Value::String(s)) | (Value::String(s), Value::Number(n)) => {
let n_val = n.as_f64().unwrap_or(0.0);
parse_string_to_f64(s)
.map(|parsed| n_val == parsed)
.unwrap_or(false)
}
(Value::Bool(b), Value::Number(n)) | (Value::Number(n), Value::Bool(b)) => {
let b_num = if *b { 1.0 } else { 0.0 };
b_num == n.as_f64().unwrap_or(0.0)
}
(Value::Bool(b), Value::String(s)) | (Value::String(s), Value::Bool(b)) => {
let b_num = if *b { 1.0 } else { 0.0 };
parse_string_to_f64(s)
.map(|parsed| b_num == parsed)
.unwrap_or(false)
}
(Value::Null, _) | (_, Value::Null) => false,
_ => a == b,
}
}