From ad0ce61b8ae554f9a41604381b009d75f6c69e98 Mon Sep 17 00:00:00 2001 From: bfenty <1398236+bfenty@users.noreply.github.com> Date: Tue, 20 Oct 2026 12:15:27 +0000 Subject: [PATCH] fix(engine): strip one leading = when validating CF formulas Co-Authored-By: Claude --- crates/engine/src/cmd/insert.rs | 4 +++- crates/engine/tests/cf_formula_validate.rs | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/engine/src/cmd/insert.rs b/crates/engine/src/cmd/insert.rs index fb804778b..3f3fe80a3 100644 --- a/crates/engine/src/cmd/insert.rs +++ b/crates/engine/src/cmd/insert.rs @@ -1339,7 +1339,9 @@ fn preset_style(name: &str) -> Style { /// files carrying it, even in repair mode. Validated here so the API and the New Rule dialog /// (which dispatches this command) share one gate, the way Excel's object model refuses. fn check_cf_formula(cmd: &str, what: &str, formula: &str) -> Result<()> { - if gridcraft_formula::parse(formula.trim_start_matches('=')).is_err() { + // Strip exactly one leading '=', the way the XLSX writer does: a value like '===A1' + // only parses after several strips and would still be saved unparseable. (#1999 review) + if gridcraft_formula::parse(formula.strip_prefix('=').unwrap_or(formula)).is_err() { return Err(bad(cmd, format!("invalid {what} formula `{formula}` — it does not parse; Excel would reject the saved file"))); } Ok(()) diff --git a/crates/engine/tests/cf_formula_validate.rs b/crates/engine/tests/cf_formula_validate.rs index 45aa13275..5e9d2e280 100644 --- a/crates/engine/tests/cf_formula_validate.rs +++ b/crates/engine/tests/cf_formula_validate.rs @@ -20,6 +20,10 @@ fn invalid_cf_formulas_are_rejected() { // more garbage that the parser rejects json!({"type": "expression", "formula": "=IF(A1", "preset": "lightRedFill"}), json!({"type": "cellIs", "operator": "equal", "value": "=((A1", "preset": "lightRedFill"}), + // multiple leading '=' on a stored-as-typed threshold: the writer strips one, + // the rest must still parse (#1999 review) + json!({"type": "dataBar", "min": {"type": "formula", "value": "===A1"}}), + json!({"type": "dataBar", "max": {"type": "formula", "value": "==1+"}}), ] { let err = session() .execute("home.conditionalFormat", json!({"range": "A1:A5", "rule": rule.clone()}))