mod colors; mod metadata; mod shared_strings; mod styles; mod tables; mod util; mod workbook; mod worksheets; use std::{ collections::HashMap, fs, io::{BufReader, Read}, }; use roxmltree::Node; use ironcalc_base::{ model::Model, types::{Metadata, Workbook, WorkbookSettings}, }; use crate::error::XlsxError; use shared_strings::read_shared_strings; use metadata::load_metadata; use styles::load_styles; use util::get_attribute; use workbook::load_workbook; use worksheets::{load_sheets, Relationship}; fn load_relationships( archive: &mut zip::ZipArchive, ) -> Result, XlsxError> { let mut file = archive.by_name("xl/_rels/workbook.xml.rels")?; let mut text = String::new(); file.read_to_string(&mut text)?; let doc = roxmltree::Document::parse(&text)?; let nodes: Vec = doc .descendants() .filter(|n| n.has_tag_name("Relationship")) .collect(); let mut rels = HashMap::new(); for node in nodes { rels.insert( get_attribute(&node, "Id")?.to_string(), Relationship { rel_type: get_attribute(&node, "Type")?.to_string(), target: get_attribute(&node, "Target")?.to_string(), }, ); } Ok(rels) } fn load_xlsx_from_reader( name: String, reader: R, locale: &str, tz: &str, ) -> Result { let mut archive = zip::ZipArchive::new(reader)?; let mut shared_strings = read_shared_strings(&mut archive)?; let workbook = load_workbook(&mut archive)?; let rels = load_relationships(&mut archive)?; let mut tables = HashMap::new(); let worksheets = load_sheets( &mut archive, &rels, &workbook, &mut tables, &mut shared_strings, )?; let styles = load_styles(&mut archive)?; let metadata = match load_metadata(&mut archive) { Ok(metadata) => metadata, Err(_) => { // In case there is no metadata, add some Metadata { application: "Unknown application".to_string(), app_version: "".to_string(), creator: "".to_string(), last_modified_by: "".to_string(), created: "".to_string(), last_modified: "".to_string(), } } }; Ok(Workbook { shared_strings, defined_names: workbook.defined_names, worksheets, styles, name, settings: WorkbookSettings { tz: tz.to_string(), locale: locale.to_string(), }, metadata, tables, }) } // Public methods /// Imports a file from disk into an internal representation pub fn load_from_excel(file_name: &str, locale: &str, tz: &str) -> Result { let file_path = std::path::Path::new(file_name); let file = fs::File::open(file_path)?; let reader = BufReader::new(file); let name = file_path .file_stem() .ok_or_else(|| XlsxError::IO("Could not extract workbook name".to_string()))? .to_string_lossy() .to_string(); load_xlsx_from_reader(name, reader, locale, tz) } pub fn load_model_from_xlsx(file_name: &str, locale: &str, tz: &str) -> Result { let workbook = load_from_excel(file_name, locale, tz)?; Model::from_workbook(workbook).map_err(XlsxError::Workbook) }