use crate::writer::{Result, XmlWriter};
use std::io::Write;
use xlsbye_core::types::{ParsedTable, RangeRef};
use xlsbye_core::xml_names::SPREADSHEET_NS;
pub fn write_table(writer: impl Write, table: &ParsedTable) -> Result<()> {
let mut writer = XmlWriter::new(writer);
writer.write_xml_declaration()?;
let mut attrs = vec![
("id".to_string(), table.id.to_string()),
("name".to_string(), table.name.clone()),
("displayName".to_string(), table.display_name.clone()),
("ref".to_string(), range_ref_to_a1(&table.range)),
(
"totalsRowShown".to_string(),
if table.totals_row_count > 0 {
"1".to_string()
} else {
"0".to_string()
},
),
];
if table.header_row_count != 1 {
attrs.push((
"headerRowCount".to_string(),
table.header_row_count.to_string(),
));
}
writer.write_start_element_with_ns("table", [("", SPREADSHEET_NS)], attrs)?;
if table.has_auto_filter {
writer.write_empty_element("autoFilter", [("ref", range_ref_to_a1(&table.range))])?;
}
writer.write_start_element(
"tableColumns",
[("count", table.columns.len().to_string())],
)?;
for column in &table.columns {
writer.write_empty_element(
"tableColumn",
[
("id", column.id.to_string()),
("name", column.name.clone()),
],
)?;
}
writer.write_end_element("tableColumns")?;
if let Some(style_name) = &table.style_name {
writer.write_empty_element(
"tableStyleInfo",
[
("name", style_name.clone()),
("showFirstColumn", "0".to_string()),
("showLastColumn", "0".to_string()),
("showRowStripes", "1".to_string()),
("showColumnStripes", "0".to_string()),
],
)?;
}
writer.write_end_element("table")?;
Ok(())
}
fn range_ref_to_a1(range: &RangeRef) -> String {
format!(
"{}:{}",
cell_ref_to_a1(range.first_row, range.first_col),
cell_ref_to_a1(range.last_row, range.last_col)
)
}
fn cell_ref_to_a1(row: u32, col: u32) -> String {
format!("{}{}", col_to_name(col), row)
}
fn col_to_name(mut col: u32) -> String {
let mut letters = Vec::new();
while col > 0 {
let rem = ((col - 1) % 26) as u8;
letters.push((b'A' + rem) as char);
col = (col - 1) / 26;
}
letters.iter().rev().collect()
}
#[cfg(test)]
mod tests {
use super::*;
use xlsbye_core::types::TableColumn;
#[test]
fn writes_table_xml() {
let table = ParsedTable {
id: 1,
name: "Table1".to_string(),
display_name: "Table1".to_string(),
range: RangeRef {
first_row: 1,
last_row: 10,
first_col: 1,
last_col: 4,
},
columns: vec![
TableColumn {
id: 1,
name: "Header1".to_string(),
},
TableColumn {
id: 2,
name: "Header2".to_string(),
},
],
has_auto_filter: true,
style_name: Some("TableStyleMedium2".to_string()),
header_row_count: 1,
totals_row_count: 0,
};
let mut out = Vec::new();
write_table(&mut out, &table).expect("table xml should be written");
let xml = String::from_utf8(out).expect("utf-8 xml");
assert!(xml.contains("<table xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" id=\"1\" name=\"Table1\" displayName=\"Table1\" ref=\"A1:D10\" totalsRowShown=\"0\""));
assert!(xml.contains("<autoFilter ref=\"A1:D10\"/>"));
assert!(xml.contains("<tableColumns count=\"2\">"));
assert!(xml.contains("<tableColumn id=\"1\" name=\"Header1\"/>"));
assert!(xml.contains("<tableStyleInfo name=\"TableStyleMedium2\""));
}
}