1use crate::writer::{Result, XmlWriter};
2use std::io::Write;
3use xlsbye_core::types::{SheetVisibility, WorkbookMeta};
4use xlsbye_core::xml_names::{RELATIONSHIPS_NS, SPREADSHEET_NS};
5
6pub fn write_workbook(writer: impl Write, meta: &WorkbookMeta, is_xlsm: bool) -> Result<()> {
7 let mut writer = XmlWriter::new(writer);
8 let defined_names = filtered_defined_names(meta);
9
10 writer.write_xml_declaration()?;
11 writer.write_start_element_with_ns(
12 "workbook",
13 [("", SPREADSHEET_NS), ("r", RELATIONSHIPS_NS)],
14 std::iter::empty::<(&str, &str)>(),
15 )?;
16
17 let date1904 = if meta.date1904 { "true" } else { "false" };
18 let mut workbook_pr_attrs = vec![("date1904".to_string(), date1904.to_string())];
19 if is_xlsm || meta.has_vba {
20 workbook_pr_attrs.push(("codeName".to_string(), "ThisWorkbook".to_string()));
21 }
22 writer.write_empty_element("workbookPr", workbook_pr_attrs)?;
23
24 writer.write_start_element("sheets", std::iter::empty::<(&str, &str)>())?;
25 for sheet in &meta.sheets {
26 let mut attrs = vec![
27 ("name".to_string(), sheet.name.clone()),
28 ("sheetId".to_string(), sheet.sheet_id.to_string()),
29 ("r:id".to_string(), sheet.rel_id.clone()),
30 ];
31
32 match sheet.state {
33 SheetVisibility::Visible => {}
34 SheetVisibility::Hidden => attrs.push(("state".to_string(), "hidden".to_string())),
35 SheetVisibility::VeryHidden => {
36 attrs.push(("state".to_string(), "veryHidden".to_string()))
37 }
38 }
39
40 writer.write_empty_element("sheet", attrs)?;
41 }
42 writer.write_end_element("sheets")?;
43
44 if !defined_names.is_empty() {
45 writer.write_start_element("definedNames", std::iter::empty::<(&str, &str)>())?;
46 for defined_name in defined_names {
47 let mut attrs = vec![("name".to_string(), defined_name.name.clone())];
48 if let Some(sheet_index) = defined_name.sheet_index {
49 attrs.push(("localSheetId".to_string(), sheet_index.to_string()));
50 }
51 if defined_name.hidden {
52 attrs.push(("hidden".to_string(), "1".to_string()));
53 }
54 writer.write_text_element("definedName", attrs, &defined_name.formula)?;
55 }
56 writer.write_end_element("definedNames")?;
57 }
58
59 writer.write_end_element("workbook")?;
60 Ok(())
61}
62
63fn filtered_defined_names<'a>(meta: &'a WorkbookMeta) -> Vec<&'a xlsbye_core::types::DefinedName> {
64 meta.defined_names
65 .iter()
66 .filter(|defined_name| !defined_name.name.starts_with("_xlfn."))
67 .filter(|defined_name| !defined_name.formula.trim().is_empty())
68 .filter(|defined_name| match defined_name.sheet_index {
69 Some(sheet_index) => (sheet_index as usize) < meta.sheets.len(),
70 None => true,
71 })
72 .collect()
73}