From ac36eee23db3271ebfe0a69426e8207ff2fb91cb Mon Sep 17 00:00:00 2001 From: prxnvt <53838599+prxnvt@users.noreply.github.com> Date: Sat, 24 Oct 2026 23:35:32 -0400 Subject: [PATCH] Engine: a JSON workbook with no sheets keeps its macros The JSON reader replaces a workbook with an empty sheet list by a new one before macros::load reads the file's macro entries, so they were lost with no warning. The entries are carried across the reset. The reset itself, which also starts the names and styles afresh, is unchanged. --- crates/engine/src/io.rs | 3 +++ crates/engine/src/macros/tests.rs | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/crates/engine/src/io.rs b/crates/engine/src/io.rs index b6a5d8473..b7a5ea709 100644 --- a/crates/engine/src/io.rs +++ b/crates/engine/src/io.rs @@ -166,7 +166,10 @@ fn read_plain(name: &str, bytes: &[u8], csv_as_text: bool) -> Result<(Workbook, let mut wb: Workbook = serde_json::from_slice(bytes).map_err(|e| EngineError::Other(format!("not a GridCraft JSON workbook: {e}")))?; wb.styles.rebuild_index(); if wb.sheets.is_empty() { + // The file's macros are read after this (`macros::load`), so they come along. + let macros = std::mem::take(&mut wb.macros_read); wb = Workbook::new(); + wb.macros_read = macros; } // The file's band placement and cell count are derived state: rebuild them from the // cells themselves, dropping what is outside the sheet grid (#1008). diff --git a/crates/engine/src/macros/tests.rs b/crates/engine/src/macros/tests.rs index d30ff021d..957b738f5 100644 --- a/crates/engine/src/macros/tests.rs +++ b/crates/engine/src/macros/tests.rs @@ -514,3 +514,16 @@ fn naming_clashing_macros_takes_linear_work() { taken.insert("a (2)"); assert_eq!(taken.unique("a"), "a (4)"); } + +#[test] +fn a_json_workbook_with_no_sheets_keeps_its_macros() { + // A JSON file with an empty sheet list opens as a new workbook; its macros come along. + let mut wb = Workbook::new(); + wb.macros.push(sample_macro("A")); + let mut v: serde_json::Value = serde_json::from_slice(&crate::io::save_bytes(&wb, "book.json").unwrap()).unwrap(); + v["sheets"] = json!([]); + let (back, warnings) = crate::io::open_bytes("book.json", v.to_string().as_bytes()).unwrap(); + assert!(warnings.is_empty(), "{warnings:?}"); + assert_eq!(back.sheets.len(), 1); + assert_eq!(back.macros, wb.macros); +}