use crate::{CompiledNode, Engine, Result};
use crate::arena::{ContextStack, DataValue};
use bumpalo::Bump;
#[inline(always)]
fn lookup_data<'a>(ctx: &ContextStack<'a>) -> &'a DataValue<'a> {
if ctx.depth() > 0 {
use crate::arena::context::ContextRef;
match ctx.current() {
ContextRef::Frame(f) => f.data(),
ContextRef::Root(av) => av,
}
} else {
ctx.root_input()
}
}
#[inline]
pub(crate) fn evaluate_missing<'a>(
args: &'a [CompiledNode],
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
) -> Result<&'a DataValue<'a>> {
let lookup = lookup_data(ctx);
let mut missing: bumpalo::collections::Vec<'a, DataValue<'a>> =
bumpalo::collections::Vec::with_capacity_in(args.len(), arena);
for arg in args {
let av = engine.dispatch_node(arg, ctx, arena)?;
match av {
DataValue::Array(items) => {
for it in *items {
if let Some(path) = value_as_str(it) {
if !crate::arena::value::path_exists_str(lookup, path) {
missing.push(DataValue::String(arena.alloc_str(path)));
}
}
}
}
DataValue::String(s) if !crate::arena::value::path_exists_str(lookup, s) => {
missing.push(DataValue::String(arena.alloc_str(s)));
}
_ => {}
}
}
if missing.is_empty() {
return Ok(crate::arena::singletons::singleton_empty_array());
}
Ok(arena.alloc(DataValue::Array(missing.into_bump_slice())))
}
#[inline]
pub(crate) fn evaluate_missing_some<'a>(
args: &'a [CompiledNode],
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
) -> Result<&'a DataValue<'a>> {
if args.len() < 2 {
return Ok(crate::arena::singletons::singleton_empty_array());
}
let min_av = engine.dispatch_node(&args[0], ctx, arena)?;
let min_present = min_av.as_i64().unwrap_or(1).max(0) as usize;
let paths_av = engine.dispatch_node(&args[1], ctx, arena)?;
let lookup = lookup_data(ctx);
let mut missing: bumpalo::collections::Vec<'a, DataValue<'a>> =
bumpalo::collections::Vec::new_in(arena);
let mut present_count: usize = 0;
let process_path = |path: &str,
missing: &mut bumpalo::collections::Vec<'a, DataValue<'a>>,
present_count: &mut usize|
-> bool {
if !crate::arena::value::path_exists_str(lookup, path) {
missing.push(DataValue::String(arena.alloc_str(path)));
} else {
*present_count += 1;
if *present_count >= min_present {
return true; }
}
false
};
let short_circuit = match paths_av {
DataValue::Array(items) => items.iter().any(|it| {
value_as_str(it).is_some_and(|p| process_path(p, &mut missing, &mut present_count))
}),
_ => false,
};
if short_circuit || present_count >= min_present || missing.is_empty() {
return Ok(crate::arena::singletons::singleton_empty_array());
}
Ok(arena.alloc(DataValue::Array(missing.into_bump_slice())))
}
#[inline]
fn value_as_str<'a>(av: &'a DataValue<'a>) -> Option<&'a str> {
match av {
DataValue::String(s) => Some(*s),
_ => None,
}
}
use crate::node::{
CompiledMissingArg, CompiledMissingData, CompiledMissingMin, CompiledMissingPaths,
CompiledMissingSomeData,
};
#[inline]
pub(crate) fn evaluate_compiled_missing<'a>(
data: &'a CompiledMissingData,
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
) -> Result<&'a DataValue<'a>> {
let lookup = lookup_data(ctx);
let mut missing: Option<bumpalo::collections::Vec<'a, DataValue<'a>>> = None;
for arg in data.args.iter() {
match arg {
CompiledMissingArg::Now((path, segments)) => {
if !crate::arena::value::path_exists_segments(lookup, segments) {
missing
.get_or_insert_with(|| {
bumpalo::collections::Vec::with_capacity_in(data.args.len(), arena)
})
.push(DataValue::String(path.as_ref()));
}
}
CompiledMissingArg::Later(node) => {
let av = engine.dispatch_node(node, ctx, arena)?;
let buf = missing.get_or_insert_with(|| {
bumpalo::collections::Vec::with_capacity_in(data.args.len(), arena)
});
accumulate_dynamic_missing(av, lookup, buf, arena);
}
}
}
match missing {
Some(v) if !v.is_empty() => Ok(arena.alloc(DataValue::Array(v.into_bump_slice()))),
_ => Ok(crate::arena::singletons::singleton_empty_array()),
}
}
#[inline]
pub(crate) fn evaluate_compiled_missing_some<'a>(
data: &'a CompiledMissingSomeData,
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
) -> Result<&'a DataValue<'a>> {
let min_present = match &data.min_present {
CompiledMissingMin::Now(n) => *n,
CompiledMissingMin::Later(node) => {
let av = engine.dispatch_node(node, ctx, arena)?;
av.as_i64().unwrap_or(1).max(0) as usize
}
};
let lookup = lookup_data(ctx);
match &data.paths {
CompiledMissingPaths::Now(paths) => {
let mut missing: Option<bumpalo::collections::Vec<'a, DataValue<'a>>> = None;
let mut present = 0usize;
for (path, segments) in paths.iter() {
if crate::arena::value::path_exists_segments(lookup, segments) {
present += 1;
if present >= min_present {
return Ok(crate::arena::singletons::singleton_empty_array());
}
} else {
missing
.get_or_insert_with(|| {
bumpalo::collections::Vec::with_capacity_in(paths.len(), arena)
})
.push(DataValue::String(path.as_ref()));
}
}
match missing {
Some(v) if present < min_present && !v.is_empty() => {
Ok(arena.alloc(DataValue::Array(v.into_bump_slice())))
}
_ => Ok(crate::arena::singletons::singleton_empty_array()),
}
}
CompiledMissingPaths::Later(node) => {
let paths_av = engine.dispatch_node(node, ctx, arena)?;
let mut missing: bumpalo::collections::Vec<'a, DataValue<'a>> =
bumpalo::collections::Vec::new_in(arena);
let mut present = 0usize;
let short = match paths_av {
DataValue::Array(items) => items.iter().any(|it| {
value_as_str(it).is_some_and(|p| {
check_path(p, lookup, &mut missing, &mut present, min_present, arena)
})
}),
_ => false,
};
if short || present >= min_present || missing.is_empty() {
return Ok(crate::arena::singletons::singleton_empty_array());
}
Ok(arena.alloc(DataValue::Array(missing.into_bump_slice())))
}
}
}
#[inline]
fn check_path<'a>(
path: &str,
lookup: &'a DataValue<'a>,
missing: &mut bumpalo::collections::Vec<'a, DataValue<'a>>,
present: &mut usize,
min_present: usize,
arena: &'a Bump,
) -> bool {
if !crate::arena::value::path_exists_str(lookup, path) {
missing.push(DataValue::String(arena.alloc_str(path)));
} else {
*present += 1;
if *present >= min_present {
return true;
}
}
false
}
#[inline]
fn accumulate_dynamic_missing<'a>(
av: &'a DataValue<'a>,
lookup: &'a DataValue<'a>,
missing: &mut bumpalo::collections::Vec<'a, DataValue<'a>>,
arena: &'a Bump,
) {
match av {
DataValue::Array(items) => {
for it in *items {
if let Some(path) = value_as_str(it) {
if !crate::arena::value::path_exists_str(lookup, path) {
missing.push(DataValue::String(arena.alloc_str(path)));
}
}
}
}
DataValue::String(s) if !crate::arena::value::path_exists_str(lookup, s) => {
missing.push(DataValue::String(arena.alloc_str(s)));
}
_ => {}
}
}