formualizer-eval 0.9.3

High-performance Arrow-backed Excel formula engine with dependency graph and incremental recalculation
Documentation
use super::common::arrow_eval_config;
use crate::engine::Engine;
use crate::test_workbook::TestWorkbook;
use crate::traits::EvaluationContext;
use arrow_array::Array;
use formualizer_common::{ExcelError, LiteralValue};
use formualizer_parse::parser::ReferenceType;

#[test]
fn blank_masks_use_cell_types_across_base_overlay_and_chunk_slices() {
    let values = vec![
        LiteralValue::Number(1.0),
        LiteralValue::Boolean(true),
        LiteralValue::Empty,
        LiteralValue::Text(String::new()),
        LiteralValue::Text("x".into()),
        LiteralValue::Error(ExcelError::new_na()),
        LiteralValue::Number(0.0),
        LiteralValue::Boolean(false),
    ];
    for overlay in [false, true] {
        let mut engine = Engine::new(TestWorkbook::new(), arrow_eval_config());
        {
            let mut ingest = engine.begin_bulk_ingest_arrow();
            ingest.add_sheet("S", 1, 3);
            for v in &values {
                ingest
                    .append_row(
                        "S",
                        &[if overlay {
                            LiteralValue::Text("old".into())
                        } else {
                            v.clone()
                        }],
                    )
                    .unwrap();
            }
            ingest.finish().unwrap();
        }
        if overlay {
            for (i, v) in values.iter().enumerate() {
                engine
                    .set_cell_value("S", i as u32 + 1, 1, v.clone())
                    .unwrap();
            }
        }
        for (start, end) in [(1, 8), (2, 7), (3, 4), (1, 2)] {
            let range =
                ReferenceType::range(Some("S".into()), Some(start), Some(1), Some(end), Some(1));
            let view = engine.resolve_range_view(&range, "S").unwrap();
            for criterion in ["", "<>"] {
                let pred =
                    crate::args::parse_criteria(&LiteralValue::Text(criterion.into())).unwrap();
                for _ in 0..3 {
                    let mask = engine.build_criteria_mask(&view, 0, &pred).unwrap();
                    assert_eq!(mask.len(), (end - start + 1) as usize);
                    for (i, value) in values[(start - 1) as usize..end as usize]
                        .iter()
                        .enumerate()
                    {
                        let blank = matches!(value, LiteralValue::Empty)
                            || matches!(value, LiteralValue::Text(s) if s.is_empty());
                        assert_eq!(
                            mask.is_valid(i) && mask.value(i),
                            if criterion.is_empty() { blank } else { !blank },
                            "overlay={overlay} range={start}:{end} criterion={criterion} row={i}"
                        );
                    }
                }
            }
        }
    }
}