Skip to main content

xlsbye_xml/
workbook.rs

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}