use crate::arena::{ContextStack, DataValue, IterGuard};
use crate::node::{MetadataHint, ReduceHint};
use crate::{CompiledNode, Engine, Result};
use bumpalo::Bump;
use std::ops::ControlFlow;
#[derive(Clone, Copy)]
pub(crate) struct IterSrc<'a>(pub(crate) &'a [DataValue<'a>]);
impl<'a> IterSrc<'a> {
#[inline]
pub(crate) fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub(crate) fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
pub(crate) fn get(&self, i: usize) -> &'a DataValue<'a> {
&self.0[i]
}
}
pub(crate) enum ResolvedInput<'a> {
Iterable(IterSrc<'a>),
Empty,
Bridge(&'a DataValue<'a>),
}
pub(crate) enum Items<'a> {
Array(IterSrc<'a>),
Object(&'a [(&'a str, DataValue<'a>)]),
Scalar(&'a DataValue<'a>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum IterArgKind {
RootVarBorrow { path_segments_empty: bool },
General,
}
impl IterArgKind {
pub(crate) fn classify(arg: &CompiledNode) -> Self {
match plain_var_segments(arg) {
Some(segments) => IterArgKind::RootVarBorrow {
path_segments_empty: segments.is_empty(),
},
None => IterArgKind::General,
}
}
}
#[inline]
pub(super) fn plain_var_segments(node: &CompiledNode) -> Option<&[crate::node::PathSegment]> {
if let CompiledNode::Var {
scope_level: 0,
segments,
reduce_hint: ReduceHint::None,
metadata_hint: MetadataHint::None,
default_value: None,
..
} = node
{
Some(segments.as_ref())
} else {
None
}
}
#[inline(always)]
pub(crate) fn resolve_iter_input<'a>(
arg: &'a CompiledNode,
kind: IterArgKind,
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
) -> Result<ResolvedInput<'a>> {
if let IterArgKind::RootVarBorrow {
path_segments_empty,
} = kind
&& ctx.depth() == 0
{
let root = ctx.root_input();
let av = if path_segments_empty {
Some(root)
} else if let CompiledNode::Var { segments, .. } = arg {
crate::arena::value::traverse_segments(root, segments)
} else {
None
};
if let Some(av) = av {
return charged(value_as_iter(av), ctx);
}
}
let av = engine.dispatch_node(arg, ctx, arena)?;
charged(value_as_iter(av), ctx)
}
#[inline]
pub(super) fn resolve_value<'a>(
av: &'a DataValue<'a>,
ctx: &mut ContextStack<'a>,
) -> Result<ResolvedInput<'a>> {
charged(value_as_iter(av), ctx)
}
#[inline(always)]
fn charged<'a>(input: ResolvedInput<'a>, ctx: &mut ContextStack<'a>) -> Result<ResolvedInput<'a>> {
let items = match &input {
ResolvedInput::Iterable(src) => src.len() as u64,
ResolvedInput::Bridge(DataValue::Object(pairs)) => pairs.len() as u64,
_ => 1,
};
ctx.charge(items)?;
Ok(input)
}
#[inline]
fn value_as_iter<'a>(av: &'a DataValue<'a>) -> ResolvedInput<'a> {
match av {
DataValue::Null => ResolvedInput::Empty,
DataValue::Array(items) => ResolvedInput::Iterable(IterSrc(items)),
_ => ResolvedInput::Bridge(av),
}
}
#[inline]
pub(super) fn for_each_iter_array<'a, F>(
items: &'a [DataValue<'a>],
body: &'a CompiledNode,
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
mut step_fn: F,
) -> Result<()>
where
F: FnMut(usize, &'a DataValue<'a>, &'a DataValue<'a>) -> Result<ControlFlow<()>>,
{
let total = items.len() as u32;
let mut guard = IterGuard::new(ctx);
for (i, item_av) in items.iter().enumerate() {
guard.step_indexed(item_av, i);
let av = engine.run_iter_body(body, guard.stack(), arena, i as u32, total)?;
if step_fn(i, item_av, av)?.is_break() {
break;
}
}
drop(guard);
Ok(())
}
#[inline]
pub(super) fn for_each_iter_object<'a, F>(
pairs: &'a [(&'a str, DataValue<'a>)],
body: &'a CompiledNode,
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
mut step_fn: F,
) -> Result<()>
where
F: FnMut(usize, &'a DataValue<'a>, &'a str, &'a DataValue<'a>) -> Result<ControlFlow<()>>,
{
let total = pairs.len() as u32;
let mut guard = IterGuard::new(ctx);
for (i, (k, v)) in pairs.iter().enumerate() {
guard.step_keyed(v, i, k);
let av = engine.run_iter_body(body, guard.stack(), arena, i as u32, total)?;
if step_fn(i, v, k, av)?.is_break() {
break;
}
}
drop(guard);
Ok(())
}