karo 0.1.2

Spreadsheet export
Documentation
#[cfg(test)]
mod tests;

use crate::{Result, XmlWritable, XmlWriter};
use indexmap::{indexmap, IndexMap};

pub(crate) const APP_PACKAGE: &str =
    "application/vnd.openxmlformats-package.";
pub(crate) const APP_DOCUMENT: &str =
    "application/vnd.openxmlformats-officedocument.";
pub(crate) const APP_MSEXCEL: &str = "application/vnd.ms-excel.";

pub(crate) struct ContentTypes {
    pub defaults: IndexMap<String, String>,
    pub overrides: IndexMap<String, String>,
}

impl Default for ContentTypes {
    fn default() -> Self {
        let defaults = indexmap! {
            "rels".to_string() =>
                format!("{}relationships+xml", APP_PACKAGE),
            "xml".to_string() => "application/xml".to_string(),
        };
        let overrides = indexmap! {
            "/docProps/app.xml".to_string() =>
                format!("{}extended-properties+xml", APP_DOCUMENT),
            "/docProps/core.xml".to_string() =>
                format!("{}core-properties+xml", APP_PACKAGE),
            "/xl/styles.xml".to_string() =>
                format!("{}spreadsheetml.styles+xml", APP_DOCUMENT),
            "/xl/theme/theme1.xml".to_string() =>
                format!("{}theme+xml", APP_DOCUMENT),
        };

        ContentTypes {
            defaults,
            overrides,
        }
    }
}

impl ContentTypes {
    pub fn add_override(
        &mut self,
        key: impl Into<String>,
        value: impl Into<String>,
    ) {
        self.overrides.insert(key.into(), value.into());
    }

    pub fn add_default(
        &mut self,
        key: impl Into<String>,
        value: impl Into<String>,
    ) {
        self.defaults.insert(key.into(), value.into());
    }

    pub fn add_worksheet_name(&mut self, name: impl Into<String>) {
        self.add_override(
            name,
            format!("{}spreadsheetml.worksheet+xml", APP_DOCUMENT),
        );
    }

    pub fn add_shared_strings(&mut self) {
        self.add_override(
            "/xl/sharedStrings.xml",
            format!("{}spreadsheetml.sharedStrings+xml", APP_DOCUMENT),
        );
    }

    pub fn add_custom_properties(&mut self) {
        self.add_override(
            "/docProps/custom.xml",
            format!("{}custom-properties+xml", APP_DOCUMENT),
        );
    }

    pub fn add_calc_chain(&mut self) {
        self.add_override(
            "/xl/calcChain.xml",
            format!("{}spreadsheetml.calcChain+xml", APP_DOCUMENT),
        );
    }
}

impl XmlWritable for ContentTypes {
    fn write_xml<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
        let tag = "Types";
        let attrs = indexmap! {
            "xmlns" =>
                "http://schemas.openxmlformats.org/package/2006/content-types",
        };
        w.start_tag_with_attrs(tag, attrs)?;
        self.write_defaults(w)?;
        self.write_overrides(w)?;
        w.end_tag(tag)?;
        Ok(())
    }
}

impl ContentTypes {
    fn write_defaults<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
        for (k, v) in self.defaults.iter() {
            let attrs = indexmap! {
              "Extension" => k.as_str(),
              "ContentType" => v.as_str(),
            };
            w.empty_tag_with_attrs("Default", attrs)?;
        }

        Ok(())
    }

    fn write_overrides<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
        for (k, v) in self.overrides.iter() {
            let attrs = indexmap! {
              "PartName" => k.as_str(),
              "ContentType" => v.as_str(),
            };
            w.empty_tag_with_attrs("Override", attrs)?;
        }
        Ok(())
    }
}