formualizer-eval 0.14.0

High-performance Arrow-backed Excel formula engine with dependency graph and incremental recalculation
Documentation
use super::common::arrow_eval_config;
use crate::{
    args::ArgSchema,
    engine::{Engine, EvalConfig},
    function::{FamilyKernel, FnCaps, Function},
    operation_diagnostics::{OperationDiagnosticOptions, UnavailableOperation},
    test_workbook::TestWorkbook,
    traits::{ArgumentHandle, CalcValue, FunctionContext},
};
use formualizer_common::{ExcelError, LiteralValue};
use formualizer_parse::parser::parse;
use std::sync::{Arc, LazyLock};

struct KernelProvider {
    name: &'static str,
    reporting: bool,
    limitation: bool,
}
impl Function for KernelProvider {
    fn name(&self) -> &'static str {
        self.name
    }
    fn caps(&self) -> FnCaps {
        FnCaps::PURE
    }
    fn reports_known_limitations(&self) -> bool {
        self.reporting
    }
    fn family_kernel(&self) -> Option<FamilyKernel> {
        Some(match self.name {
            "ABS" => FamilyKernel::Abs,
            "SUM" => FamilyKernel::Sum,
            "COUNTIF" => FamilyKernel::CriteriaAggregate,
            _ => unreachable!(),
        })
    }
    fn min_args(&self) -> usize {
        if self.name == "COUNTIF" { 2 } else { 1 }
    }
    fn arg_schema(&self) -> &'static [ArgSchema] {
        static ONE: LazyLock<Vec<ArgSchema>> = LazyLock::new(|| vec![ArgSchema::any()]);
        static TWO: LazyLock<Vec<ArgSchema>> =
            LazyLock::new(|| vec![ArgSchema::any(), ArgSchema::any()]);
        if self.name == "COUNTIF" { &TWO } else { &ONE }
    }
    fn eval<'a, 'b, 'c>(
        &self,
        _: &'c [ArgumentHandle<'a, 'b>],
        ctx: &dyn FunctionContext<'b>,
    ) -> Result<CalcValue<'b>, ExcelError> {
        if self.limitation {
            ctx.report_unavailable_operation(UnavailableOperation::UnverifiedFunctionMode {
                name: self.name.into(),
                mode: "declared_provider_mode",
            });
        }
        Ok(CalcValue::Scalar(LiteralValue::Number(
            if self.name == "COUNTIF" { 2.0 } else { 1.0 },
        )))
    }
}
fn kernel_case(
    name: &'static str,
    reporting: bool,
    limitation: bool,
    tracking: bool,
    active: bool,
    lift: bool,
    kernels: bool,
) -> crate::operation_diagnostics::OperationDiagnostics {
    crate::builtins::load_builtins();
    let mut engine = Engine::new(
        TestWorkbook::new().with_function(Arc::new(KernelProvider {
            name,
            reporting,
            limitation,
        })),
        EvalConfig {
            enable_parallel: false,
            family_lift: lift,
            family_kernels: kernels,
            ..arrow_eval_config()
        },
    );
    engine.configure_operation_diagnostics(OperationDiagnosticOptions {
        track_unverified_providers: tracking,
        sample_limit: 0,
        ..Default::default()
    });
    for row in 1..=8 {
        engine
            .set_cell_value("Sheet1", row, 1, LiteralValue::Number(1.0))
            .unwrap();
        let call = if name == "COUNTIF" {
            format!("COUNTIF($A$1:$A$2,A{row})")
        } else {
            format!("{name}(A{row})")
        };
        let formula = if active {
            format!("={call}")
        } else {
            format!("=IF(TRUE,7,{call})")
        };
        engine
            .set_cell_formula("Sheet1", row, 2, parse(&formula).unwrap())
            .unwrap();
    }
    engine.evaluate_all().unwrap();
    for row in 1..=8 {
        let expected = if !active {
            7.0
        } else if name == "COUNTIF" {
            2.0
        } else {
            1.0
        };
        assert_eq!(
            engine.get_cell_value("Sheet1", row, 2),
            Some(LiteralValue::Number(expected))
        );
    }
    engine.operation_diagnostics().unwrap().unwrap()
}
#[test]
fn recalc_contract_provider_uncertainty_survives_optimized_kernels() {
    let mut failures = Vec::new();
    for name in ["ABS", "SUM", "COUNTIF"] {
        for (lift, kernels) in [(false, false), (true, false), (false, true), (true, true)] {
            let report = kernel_case(name, false, false, true, true, lift, kernels);
            if report.logical_root_cell_count != 8 {
                failures.push(format!("{name} lift={lift} kernels={kernels}: {report:?}"));
            }
        }
    }
    assert!(failures.is_empty(), "{}", failures.join("\n"));
}
#[test]
fn recalc_contract_provider_kernel_reporting_and_inactive_controls() {
    for name in ["ABS", "SUM", "COUNTIF"] {
        for (reporting, active) in [(true, true), (false, false)] {
            let report = kernel_case(name, reporting, false, true, active, true, true);
            assert!(
                report.roots.is_empty(),
                "{name} reporting={reporting} active={active}: {report:?}"
            );
        }
    }
}

struct Nested {
    name: &'static str,
}
impl Function for Nested {
    fn name(&self) -> &'static str {
        self.name
    }
    fn min_args(&self) -> usize {
        1
    }
    fn arg_schema(&self) -> &'static [ArgSchema] {
        static SCHEMA: LazyLock<Vec<ArgSchema>> = LazyLock::new(|| vec![ArgSchema::any()]);
        &SCHEMA
    }
    fn eval<'a, 'b, 'c>(
        &self,
        args: &'c [ArgumentHandle<'a, 'b>],
        _: &dyn FunctionContext<'b>,
    ) -> Result<CalcValue<'b>, ExcelError> {
        let _ = args[0].value()?;
        Ok(CalcValue::Scalar(LiteralValue::Number(1.0)))
    }
}
#[test]
fn recalc_contract_provider_outer_uncertainty_is_independent_of_descendants() {
    let mut failures = Vec::new();
    for text in [
        "=EVIDENCE_OUTER(EVIDENCE_INNER(A1))",
        "=EVIDENCE_OUTER(IFERROR(CELL(\"format\",A1),1))",
    ] {
        for tree in [false, true] {
            crate::builtins::load_builtins();
            let wb = TestWorkbook::new()
                .with_function(Arc::new(Nested {
                    name: "EVIDENCE_OUTER",
                }))
                .with_function(Arc::new(Nested {
                    name: "EVIDENCE_INNER",
                }));
            let mut e = Engine::new(
                wb,
                EvalConfig {
                    enable_parallel: false,
                    ..arrow_eval_config()
                },
            );
            e.configure_operation_diagnostics(OperationDiagnosticOptions {
                track_unverified_providers: true,
                ..Default::default()
            });
            e.set_cell_value("Sheet1", 1, 1, LiteralValue::Number(1.0))
                .unwrap();
            if tree {
                let interpreter = crate::interpreter::Interpreter::new_with_cell(
                    &e,
                    "Sheet1",
                    crate::CellRef::new_absolute(0, 0, 1),
                );
                assert_eq!(
                    interpreter
                        .evaluate_ast(&parse(text).unwrap())
                        .unwrap()
                        .into_literal(),
                    LiteralValue::Number(1.0)
                );
            } else {
                e.set_cell_formula("Sheet1", 1, 2, parse(text).unwrap())
                    .unwrap();
                e.evaluate_all().unwrap();
                assert_eq!(
                    e.get_cell_value("Sheet1", 1, 2),
                    Some(LiteralValue::Number(1.0))
                );
            }
            let report = e.operation_diagnostics().unwrap().unwrap();
            let outer = report.roots.iter().any(|root| {
                matches!(root.operation.as_ref(),
                UnavailableOperation::UnverifiedProvider { name } if name=="EVIDENCE_OUTER")
            });
            if report.logical_cell_reason_count != 2 || !outer {
                failures.push(format!("{text} tree={tree}: {report:?}"));
            }
        }
    }
    assert!(failures.is_empty(), "{}", failures.join("\n"));
}

#[test]
fn recalc_contract_attested_provider_mode_events_survive_kernels_without_uncertainty_tracking() {
    let mut failures = Vec::new();
    for name in ["ABS", "SUM", "COUNTIF"] {
        for tracking in [false, true] {
            let report = kernel_case(name, true, true, tracking, true, true, true);
            if report.logical_root_cell_count != 8 {
                failures.push(format!("{name} tracking={tracking}: {report:?}"));
            }
            assert!(
                report.roots.iter().all(|root| !matches!(
                    root.operation.as_ref(),
                    UnavailableOperation::UnverifiedProvider { .. }
                )),
                "explicit contract must not become unverified provider"
            );
        }
    }
    assert!(failures.is_empty(), "{}", failures.join("\n"));
}

#[test]
fn recalc_contract_provider_kernel_evidence_preserves_builtin_and_no_collector_lifts() {
    for builtin in [false, true] {
        for evidence in [false, true] {
            crate::builtins::load_builtins();
            let workbook = if builtin {
                TestWorkbook::new()
            } else {
                TestWorkbook::new().with_function(Arc::new(KernelProvider {
                    name: "ABS",
                    reporting: true,
                    limitation: false,
                }))
            };
            let mut engine = Engine::new(
                workbook,
                EvalConfig {
                    enable_parallel: false,
                    family_lift: true,
                    family_kernels: false,
                    ..arrow_eval_config()
                },
            );
            if evidence {
                engine.configure_operation_diagnostics(OperationDiagnosticOptions::default());
            }
            for row in 1..=8 {
                engine
                    .set_cell_value("Sheet1", row, 1, LiteralValue::Number(1.0))
                    .unwrap();
                engine
                    .set_cell_formula("Sheet1", row, 2, parse(format!("=ABS(A{row})")).unwrap())
                    .unwrap();
            }
            engine.evaluate_all().unwrap();
            let lifted = engine.lifted_members_for_test();
            if evidence && !builtin {
                assert_eq!(lifted, 0);
            } else {
                assert!(
                    lifted > 0,
                    "builtin={builtin} evidence={evidence}: no actual lift"
                );
            }
            for row in 1..=8 {
                assert_eq!(
                    engine.get_cell_value("Sheet1", row, 2),
                    Some(LiteralValue::Number(1.0))
                );
            }
            if evidence {
                assert!(
                    engine
                        .operation_diagnostics()
                        .unwrap()
                        .unwrap()
                        .roots
                        .is_empty()
                );
            }
        }
    }
}