use super::{CompiledNode, populate_lits};
use crate::arena::DataValue;
use crate::opcode::OpCode;
use datavalue::OwnedDataValue;
#[derive(Clone)]
pub struct Logic {
pub(crate) root: CompiledNode,
pub(crate) arena_static_bytes: usize,
pub(crate) root_op_name: Option<std::borrow::Cow<'static, str>>,
}
impl std::fmt::Debug for Logic {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Logic")
.field("root", &self.root)
.field("arena_static_bytes", &self.arena_static_bytes)
.field("root_op_name", &self.root_op_name)
.finish_non_exhaustive()
}
}
#[inline]
fn root_op_name(node: &CompiledNode) -> Option<std::borrow::Cow<'static, str>> {
use std::borrow::Cow;
match node {
CompiledNode::BuiltinOperator { opcode, .. } => Some(Cow::Borrowed(opcode.as_str())),
CompiledNode::Var { .. } => Some(Cow::Borrowed("var")),
CompiledNode::Missing(_) => Some(Cow::Borrowed("missing")),
CompiledNode::MissingSome(_) => Some(Cow::Borrowed("missing_some")),
#[cfg(feature = "ext-control")]
CompiledNode::Exists(_) => Some(Cow::Borrowed("exists")),
#[cfg(feature = "error-handling")]
CompiledNode::Throw(_) => Some(Cow::Borrowed("throw")),
CompiledNode::CustomOperator(data) => Some(Cow::Owned(data.name.clone())),
CompiledNode::InvalidArgs { op_name, .. } => Some(Cow::Borrowed(op_name)),
_ => None,
}
}
impl Logic {
pub(crate) fn new(mut root: CompiledNode) -> Self {
let arena_static_bytes = estimate_arena_static_bytes(&root);
populate_lits(&mut root);
let root_op_name = root_op_name(&root);
Self {
root,
arena_static_bytes,
root_op_name,
}
}
pub fn is_static(&self) -> bool {
node_is_static(&self.root)
}
pub fn to_json(&self) -> String {
crate::node_serialize::node_to_json_string(&self.root)
}
}
impl std::fmt::Display for Logic {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.to_json())
}
}
fn estimate_arena_static_bytes(node: &CompiledNode) -> usize {
const PER_NODE: usize =
std::mem::size_of::<DataValue<'static>>() + std::mem::size_of::<&[u8]>();
let mut bytes = PER_NODE;
match node {
CompiledNode::Value { value, .. } => {
bytes += estimate_value_bytes(value);
}
#[cfg(feature = "error-handling")]
CompiledNode::Throw(data) => {
bytes += estimate_value_bytes(&data.error);
}
#[cfg(feature = "templating")]
CompiledNode::StructuredObject(data) => {
for (k, _) in data.fields.iter() {
bytes += k.len();
}
}
_ => {}
}
node.visit_children(&mut |child| {
bytes += estimate_arena_static_bytes(child);
});
bytes
}
fn estimate_value_bytes(v: &OwnedDataValue) -> usize {
match v {
OwnedDataValue::String(s) => s.len() + 16,
OwnedDataValue::Array(arr) => 16 + arr.iter().map(estimate_value_bytes).sum::<usize>(),
OwnedDataValue::Object(pairs) => {
16 + pairs
.iter()
.map(|(k, v)| k.len() + estimate_value_bytes(v))
.sum::<usize>()
}
_ => 0,
}
}
pub(crate) fn node_is_static(node: &CompiledNode) -> bool {
match node {
CompiledNode::Value { .. } => true,
CompiledNode::Array { nodes, .. } => nodes.iter().all(node_is_static),
CompiledNode::BuiltinOperator { opcode, args, .. } => opcode_is_static(opcode, args),
CompiledNode::CustomOperator(_) => false,
CompiledNode::Var { .. } => false,
#[cfg(feature = "ext-control")]
CompiledNode::Exists(_) => false,
#[cfg(feature = "error-handling")]
CompiledNode::Throw(_) => false,
#[cfg(feature = "templating")]
CompiledNode::StructuredObject(data) => {
data.fields.iter().all(|(_, node)| node_is_static(node))
}
CompiledNode::Missing(_) | CompiledNode::MissingSome(_) => false,
CompiledNode::InvalidArgs { .. } => false,
}
}
fn opcode_is_static(opcode: &OpCode, args: &[CompiledNode]) -> bool {
use OpCode::*;
let args_static = || args.iter().all(node_is_static);
match opcode {
Val | Missing | MissingSome => false,
#[cfg(feature = "ext-control")]
Exists => false,
Map | Filter | Reduce | All | Some | None => false,
#[cfg(feature = "error-handling")]
Try | Throw => false,
#[cfg(feature = "datetime")]
Now => false,
#[cfg(feature = "flagd")]
Fractional => false,
#[cfg(feature = "flagd")]
SemVer => args_static(),
Merge | Min | Max => false,
_ => args_static(),
}
}