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); +}