fix(engine): strip one leading = when validating CF formulas

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
bfenty
2026-10-20 12:15:27 +00:00
co-authored by Claude
parent 8ff06e215e
commit ad0ce61b8a
2 changed files with 7 additions and 1 deletions
+3 -1
View File
@@ -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(())
@@ -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()}))