use crate::config::TruthyEvaluator;
use datavalue::OwnedDataValue;
#[derive(Clone, Copy)]
pub(crate) enum Shape {
Null,
Bool(bool),
Number {
zero: bool,
nan: bool,
},
Sized {
empty: bool,
},
#[cfg_attr(not(feature = "datetime"), allow(dead_code))]
Always,
}
pub(crate) trait Truthy {
fn shape(&self) -> Shape;
fn custom(&self, f: &(dyn Fn(&OwnedDataValue) -> bool + Send + Sync)) -> bool;
}
#[inline(always)]
pub(crate) fn truthy_by<V: Truthy + ?Sized>(value: &V, evaluator: &TruthyEvaluator) -> bool {
match evaluator {
TruthyEvaluator::JavaScript => js(value.shape()),
TruthyEvaluator::Python => match value.shape() {
Shape::Number { nan: true, .. } => true,
shape => js(shape),
},
TruthyEvaluator::StrictBoolean => match value.shape() {
Shape::Null => false,
Shape::Bool(b) => b,
_ => true,
},
TruthyEvaluator::Custom(f) => value.custom(&**f),
}
}
#[inline(always)]
fn js(shape: Shape) -> bool {
match shape {
Shape::Null => false,
Shape::Bool(b) => b,
Shape::Number { zero, nan } => !zero && !nan,
Shape::Sized { empty } => !empty,
Shape::Always => true,
}
}
impl Truthy for crate::arena::DataValue<'_> {
#[inline(always)]
fn shape(&self) -> Shape {
use crate::arena::DataValue;
match self {
DataValue::Null => Shape::Null,
DataValue::Bool(b) => Shape::Bool(*b),
DataValue::Number(n) => Shape::Number {
zero: n.is_zero(),
nan: n.is_nan(),
},
DataValue::String(s) => Shape::Sized {
empty: s.is_empty(),
},
DataValue::Array(items) => Shape::Sized {
empty: items.is_empty(),
},
DataValue::Object(pairs) => Shape::Sized {
empty: pairs.is_empty(),
},
#[cfg(feature = "datetime")]
DataValue::DateTime(_) | DataValue::Duration(_) => Shape::Always,
#[cfg(feature = "tensor")]
DataValue::Tensor(t) => Shape::Sized {
empty: t.numel() == 0,
},
}
}
#[inline]
fn custom(&self, f: &(dyn Fn(&OwnedDataValue) -> bool + Send + Sync)) -> bool {
f(&self.to_owned())
}
}
impl Truthy for OwnedDataValue {
#[inline(always)]
fn shape(&self) -> Shape {
match self {
OwnedDataValue::Null => Shape::Null,
OwnedDataValue::Bool(b) => Shape::Bool(*b),
OwnedDataValue::Number(n) => Shape::Number {
zero: n.is_zero(),
nan: n.is_nan(),
},
OwnedDataValue::String(s) => Shape::Sized {
empty: s.is_empty(),
},
OwnedDataValue::Array(items) => Shape::Sized {
empty: items.is_empty(),
},
OwnedDataValue::Object(pairs) => Shape::Sized {
empty: pairs.is_empty(),
},
#[cfg(feature = "datetime")]
OwnedDataValue::DateTime(_) | OwnedDataValue::Duration(_) => Shape::Always,
#[cfg(feature = "tensor")]
OwnedDataValue::Tensor(t) => Shape::Sized {
empty: t.numel() == 0,
},
}
}
#[inline]
fn custom(&self, f: &(dyn Fn(&OwnedDataValue) -> bool + Send + Sync)) -> bool {
f(self)
}
}
#[cfg(feature = "serde_json")]
impl Truthy for serde_json::Value {
#[inline]
fn shape(&self) -> Shape {
use serde_json::Value;
match self {
Value::Null => Shape::Null,
Value::Bool(b) => Shape::Bool(*b),
Value::Number(n) => Shape::Number {
zero: !n.as_f64().is_some_and(|f| f != 0.0),
nan: false,
},
Value::String(s) => Shape::Sized {
empty: s.is_empty(),
},
Value::Array(items) => Shape::Sized {
empty: items.is_empty(),
},
Value::Object(fields) => Shape::Sized {
empty: fields.is_empty(),
},
}
}
#[inline]
fn custom(&self, f: &(dyn Fn(&OwnedDataValue) -> bool + Send + Sync)) -> bool {
f(&crate::serde_bridge::owned_from_serde(self))
}
}
#[inline]
pub(crate) fn truthy_owned(value: &OwnedDataValue, engine: &crate::Engine) -> bool {
truthy_by(value, &engine.config().truthy_evaluator)
}