xlsbye-xml 0.1.0

SpreadsheetML XML writer for xlsbye
Documentation
use crate::writer::{Result, XmlWriter};
use std::io::Write;
use xlsbye_core::types::{SheetVisibility, WorkbookMeta};
use xlsbye_core::xml_names::{RELATIONSHIPS_NS, SPREADSHEET_NS};

pub fn write_workbook(writer: impl Write, meta: &WorkbookMeta, is_xlsm: bool) -> Result<()> {
    let mut writer = XmlWriter::new(writer);
    let defined_names = filtered_defined_names(meta);

    writer.write_xml_declaration()?;
    writer.write_start_element_with_ns(
        "workbook",
        [("", SPREADSHEET_NS), ("r", RELATIONSHIPS_NS)],
        std::iter::empty::<(&str, &str)>(),
    )?;

    let date1904 = if meta.date1904 { "true" } else { "false" };
    let mut workbook_pr_attrs = vec![("date1904".to_string(), date1904.to_string())];
    if is_xlsm || meta.has_vba {
        workbook_pr_attrs.push(("codeName".to_string(), "ThisWorkbook".to_string()));
    }
    writer.write_empty_element("workbookPr", workbook_pr_attrs)?;

    writer.write_start_element("sheets", std::iter::empty::<(&str, &str)>())?;
    for sheet in &meta.sheets {
        let mut attrs = vec![
            ("name".to_string(), sheet.name.clone()),
            ("sheetId".to_string(), sheet.sheet_id.to_string()),
            ("r:id".to_string(), sheet.rel_id.clone()),
        ];

        match sheet.state {
            SheetVisibility::Visible => {}
            SheetVisibility::Hidden => attrs.push(("state".to_string(), "hidden".to_string())),
            SheetVisibility::VeryHidden => {
                attrs.push(("state".to_string(), "veryHidden".to_string()))
            }
        }

        writer.write_empty_element("sheet", attrs)?;
    }
    writer.write_end_element("sheets")?;

    if !defined_names.is_empty() {
        writer.write_start_element("definedNames", std::iter::empty::<(&str, &str)>())?;
        for defined_name in defined_names {
            let mut attrs = vec![("name".to_string(), defined_name.name.clone())];
            if let Some(sheet_index) = defined_name.sheet_index {
                attrs.push(("localSheetId".to_string(), sheet_index.to_string()));
            }
            if defined_name.hidden {
                attrs.push(("hidden".to_string(), "1".to_string()));
            }
            writer.write_text_element("definedName", attrs, &defined_name.formula)?;
        }
        writer.write_end_element("definedNames")?;
    }

    writer.write_end_element("workbook")?;
    Ok(())
}

fn filtered_defined_names<'a>(meta: &'a WorkbookMeta) -> Vec<&'a xlsbye_core::types::DefinedName> {
    meta.defined_names
        .iter()
        .filter(|defined_name| !defined_name.name.starts_with("_xlfn."))
        .filter(|defined_name| !defined_name.formula.trim().is_empty())
        .filter(|defined_name| match defined_name.sheet_index {
            Some(sheet_index) => (sheet_index as usize) < meta.sheets.len(),
            None => true,
        })
        .collect()
}