use crate::{
data::{context::EvalError, value::Val, view::ValueView},
vm::Program,
};
use super::{eval_kernel, eval_view_kernel, BodyKernel, ViewKernelValue};
#[derive(Debug, Clone)]
pub(crate) struct RowProgram {
kernel: BodyKernel,
}
impl RowProgram {
pub(crate) fn from_kernel(kernel: BodyKernel) -> Option<Self> {
(!matches!(kernel, BodyKernel::Generic)).then_some(Self { kernel })
}
#[allow(dead_code)]
pub(crate) fn classify(program: &Program) -> Option<Self> {
Self::from_kernel(BodyKernel::classify(program))
}
#[inline]
pub(crate) fn kernel(&self) -> &BodyKernel {
&self.kernel
}
#[inline]
pub(crate) fn into_kernel(self) -> BodyKernel {
self.kernel
}
#[allow(dead_code)]
pub(crate) fn eval_val(&self, row: &Val) -> Result<Val, EvalError> {
eval_kernel(&self.kernel, row, |_| {
Err(EvalError(
"row program unexpectedly requires VM fallback".to_string(),
))
})
}
pub(crate) fn eval_view<'a, V>(&self, row: &V) -> Option<ViewKernelValue<V>>
where
V: ValueView<'a>,
{
eval_view_kernel(&self.kernel, row)
}
}
impl From<RowProgram> for BodyKernel {
fn from(program: RowProgram) -> Self {
program.into_kernel()
}
}
#[cfg(test)]
mod tests {
use serde_json::json;
use crate::{compile::compiler::Compiler, data::view::ValView};
use super::*;
#[test]
fn row_program_runs_object_projection_on_val_and_view() {
let expr = crate::parse::parser::parse("{id, total: price * qty}").unwrap();
let program = Compiler::compile(&expr, "<row-program-test>");
let row_program = RowProgram::classify(&program).expect("row program");
assert!(matches!(row_program.kernel(), BodyKernel::Object(_)));
let row: Val = (&json!({"id": 7, "price": 12, "qty": 3})).into();
let val_out = row_program.eval_val(&row).unwrap();
let view_out = match row_program.eval_view(&ValView::new(&row)).unwrap() {
ViewKernelValue::View(view) => view.materialize(),
ViewKernelValue::Owned(value) => value,
};
assert_eq!(val_out.get_field("id"), Val::Int(7));
assert_eq!(val_out.get_field("total"), Val::Int(36));
assert_eq!(view_out.get_field("id"), Val::Int(7));
assert_eq!(view_out.get_field("total"), Val::Int(36));
}
#[test]
fn row_program_rejects_generic_vm_fallback() {
let expr = crate::parse::parser::parse("let x = price in x").unwrap();
let program = Compiler::compile(&expr, "<row-program-test>");
assert!(RowProgram::classify(&program).is_none());
}
}