edit-xlsx 0.4.4

A quick and easy-to-use Rust library for Excel file editing.
Documentation
use std::fs::File;
use std::io;
use std::io::Read;
use std::path::Path;
use quick_xml::{de, se};
use serde::{Deserialize, Serialize};
use zip::read::ZipFile;
use crate::file::{XlsxFileType, XlsxFileWriter};
use crate::xml::extension::{AddExtension, ExtensionList, ExtensionType};
use crate::xml::io::Io;

#[derive(Debug, Deserialize, Serialize)]
pub(crate) struct Metadata {
    #[serde(rename = "@xmlns")]
    xmlns: String,
    #[serde(rename(serialize = "@xmlns:xda", deserialize = "@xmlns:xda"), default, skip_serializing_if = "String::is_empty")]
    xmlns_xda: String,
    #[serde(rename = "metadataTypes", skip_serializing_if = "Option::is_none")]
    metadata_types: Option<MetadataTypes>,
    #[serde(rename = "futureMetadata", skip_serializing_if = "Option::is_none")]
    future_metadata: Option<FutureMetadata>,
    #[serde(rename = "cellMetadata", skip_serializing_if = "Option::is_none")]
    cell_metadata: Option<CellMetadata>,
}

impl Default for Metadata {
    fn default() -> Self {
        Self {
            xmlns: "http://schemas.openxmlformats.org/spreadsheetml/2006/main".to_string(),
            xmlns_xda: "http://schemas.microsoft.com/office/spreadsheetml/2017/dynamicarray".to_string(),
            metadata_types: Some(Default::default()),
            future_metadata: Default::default(),
            cell_metadata: Some(Default::default()),
        }
    }
}

impl AddExtension for Metadata {
    fn add_extension(&mut self, e: ExtensionType) {
        let mut future_metadata = self.future_metadata.take();
        if let None = future_metadata {
            future_metadata = Some(FutureMetadata::default());
        }
        let mut future_metadata = future_metadata.unwrap();
        if future_metadata.bk.is_empty() {
            future_metadata.bk.push(Bk::default());
        }
        let extension_list = &mut future_metadata.bk[0].ext_lst;
        extension_list.add_extension(e);
        self.future_metadata = Some(future_metadata)
    }
}

#[derive(Debug, Deserialize, Serialize)]
struct MetadataTypes {
    #[serde(rename = "@count", default)]
    count: u32,
    #[serde(rename = "metadataType", skip_serializing_if = "Vec::is_empty")]
    metadata_type: Vec<MetadataType>,
}
impl Default for MetadataTypes {
    fn default() -> Self {
        Self {
            count: 1,
            metadata_type: vec![MetadataType::default()],
        }
    }
}
#[derive(Debug, Deserialize, Serialize)]
struct MetadataType {
    #[serde(rename = "@name")]
    name: String,
    #[serde(rename = "@minSupportedVersion")]
    min_supported_version: u32,
    #[serde(rename = "@copy")]
    copy: u8,
    #[serde(rename = "@pasteAll")]
    paste_all: u8,
    #[serde(rename = "@pasteValues")]
    paste_values: u8,
    #[serde(rename = "@merge")]
    merge: u8,
    #[serde(rename = "@splitFirst")]
    split_first: u8,
    #[serde(rename = "@rowColShift")]
    row_col_shift: u8,
    #[serde(rename = "@clearFormats")]
    clear_formats: u8,
    #[serde(rename = "@clearComments")]
    clear_comments: u8,
    #[serde(rename = "@assign")]
    assign: u8,
    #[serde(rename = "@coerce")]
    coerce: u8,
    #[serde(rename = "@cellMeta", skip_serializing_if = "Option::is_none")]
    cell_meta: Option<u8>,
}
impl Default for MetadataType {
    fn default() -> Self {
        Self {
            name: "XLDAPR".to_string(),
            min_supported_version: 120000,
            copy: 1,
            paste_all: 1,
            paste_values: 1,
            merge: 1,
            split_first: 1,
            row_col_shift: 1,
            clear_formats: 1,
            clear_comments: 1,
            assign: 1,
            coerce: 1,
            cell_meta: Some(1),
        }
    }
}
#[derive(Debug, Deserialize, Serialize)]
struct FutureMetadata {
    #[serde(rename = "@name", default)]
    name: String,
    #[serde(rename = "@count", default)]
    count: u32,
    #[serde(rename = "bk", skip_serializing_if = "Vec::is_empty")]
    bk: Vec<Bk>
}
impl Default for FutureMetadata {
    fn default() -> Self {
        Self {
            name: "XLDAPR".to_string(),
            count: 1,
            bk: vec![Default::default()],
        }
    }
}
#[derive(Debug, Deserialize, Serialize)]
struct CellMetadata {
    #[serde(rename = "@count", default)]
    count: u32,
    #[serde(rename = "bk", skip_serializing_if = "Vec::is_empty")]
    bk: Vec<Bk>
}

impl Default for CellMetadata {
    fn default() -> Self {
        Self {
            count: 1,
            bk: vec![Bk::default_rc()],
        }
    }
}

#[derive(Debug, Deserialize, Serialize, Default)]
struct Bk {
    #[serde(rename = "extLst", default, skip_serializing_if = "ExtensionList::is_empty")]
    ext_lst: ExtensionList,
    #[serde(rename = "rc", default, skip_serializing_if = "Vec::is_empty")]
    rc: Vec<Rc>
}

impl Bk {
    fn default_rc() -> Bk {
        Bk {
            ext_lst: Default::default(),
            rc: vec![Default::default()],
        }
    }
}

#[derive(Debug, Deserialize, Serialize)]
struct Rc {
    #[serde(rename = "@t")]
    t: u8,
    #[serde(rename = "@v")]
    v: u8,
}
impl Default for Rc {
    fn default() -> Self {
        Self {
            t: 1,
            v: 0,
        }
    }
}

impl Metadata {
    pub(crate) fn from_file(file: &File) -> Metadata {
        let mut xml = String::new();
        let mut archive = zip::ZipArchive::new(file).unwrap();
        let file_path = "xl/sharedStrings.xml";
        let metadata = match archive.by_name(&file_path) {
            Ok(mut file) => {
                file.read_to_string(&mut xml).unwrap();
                de::from_str(&xml).unwrap()
            }
            Err(_) => {
                 Metadata::default()
            }
        };
        metadata
    }
}

impl Io<Metadata> for Metadata {
    fn save<P: AsRef<Path>>(& self, file_path: P) {
        let xml = se::to_string_with_root("metadata", &self).unwrap();
        let xml = format!("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n{}", xml);
        let mut file = XlsxFileWriter::from_path(file_path, XlsxFileType::MetaData).unwrap();
        file.write_all(xml.as_ref()).unwrap();
    }
}