mod bookviews;
mod defined_names;
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 zip::ZipArchive;
use crate::api::relationship::Rel;
use crate::file::{XlsxFileType, XlsxFileWriter};
use crate::result::{WorkSheetError, WorkbookError};
use crate::WorkbookResult;
use crate::xml::common::{XmlnsAttrs};
use crate::xml::extension::ExtensionList;
use crate::xml::io::Io;
use crate::xml::style::StyleSheet;
use crate::xml::workbook::bookviews::BookViews;
use crate::xml::workbook::defined_names::DefinedNames;
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename="workbook")]
pub(crate) struct Workbook {
#[serde(flatten)]
xmlns_attrs: XmlnsAttrs,
#[serde(rename = "fileVersion")]
file_version: FileVersion,
#[serde(rename = "fileSharing", skip_serializing_if = "Option::is_none")]
pub(crate) file_sharing: Option<FileSharing>,
#[serde(rename = "workbookPr")]
workbook_pr: WorkbookPr,
#[serde(rename(serialize = "xr:revisionPtr", deserialize = "revisionPtr"), skip_serializing_if = "Option::is_none")]
xr_revision_ptr: Option<XrRevisionPtr>,
#[serde(rename = "bookViews")]
pub(crate) book_views: BookViews,
#[serde(rename = "sheets")]
pub(crate) sheets: Sheets,
#[serde(rename = "definedNames", default, skip_serializing_if = "DefinedNames::is_empty")]
pub(crate) defined_names: DefinedNames,
#[serde(rename = "calcPr", skip_serializing_if = "Option::is_none")]
calc_pr: Option<CalcPr>,
#[serde(rename = "extLst", skip_serializing_if = "Option::is_none")]
ext_lst: Option<ExtensionList>,
}
unsafe impl Sync for Workbook {}
unsafe impl Send for Workbook {}
impl Workbook {
pub(crate) fn next_sheet_id(&self) -> u32 {
let max_sheet_id = self.sheets.sheets.iter().max_by_key(|s| { s.sheet_id }).unwrap().sheet_id;
1 + max_sheet_id
}
pub(crate) fn add_worksheet(&mut self, id: u32, r_id: u32) -> WorkbookResult<String> {
let name = format!("Sheet{id}");
if let Some(_) = self.sheets.sheets.iter().find(|s| { s.name == name }) {
return Err(WorkbookError::SheetError(WorkSheetError::DuplicatedSheets));
}
self.sheets.sheets.push(Sheet::by_name(r_id, id, &name));
Ok(name)
}
pub(crate) fn add_worksheet_v2(&mut self, r_id: u32, default_name: Option<&str>) -> WorkbookResult<(u32, String)> {
let id = self.next_sheet_id();
let name = if let Some(default_name) = default_name { default_name.to_string() } else { format!("Sheet{id}") };
if let Some(_) = self.sheets.sheets.iter().find(|s| { s.name == name }) {
return Err(WorkbookError::SheetError(WorkSheetError::DuplicatedSheets));
}
self.sheets.sheets.push(Sheet::by_name(r_id, id, &name));
Ok((id, name))
}
pub(crate) fn add_worksheet_by_name(&mut self, id: u32, r_id: u32, name: &str) -> WorkbookResult<()> {
if let Some(_) = self.sheets.sheets.iter().find(|s| { s.name == name }) {
return Err(WorkbookError::SheetError(WorkSheetError::DuplicatedSheets));
}
self.sheets.sheets.push(Sheet::by_name(r_id, id, &name));
Ok(())
}
}
#[derive(Debug, Deserialize, Serialize)]
struct FileVersion {
#[serde(rename = "@appName")]
app_name: String,
#[serde(rename = "@lastEdited", skip_serializing_if = "Option::is_none")]
last_edited: Option<u32>,
#[serde(rename = "@lowestEdited", skip_serializing_if = "Option::is_none")]
lowest_edited: Option<u32>,
#[serde(rename = "@rupBuild", skip_serializing_if = "Option::is_none")]
rup_build: Option<String>,
}
impl Default for FileVersion {
fn default() -> Self {
FileVersion {
app_name: "xl".to_string(),
last_edited: None,
lowest_edited: None,
rup_build: Some(String::from("14420")),
}
}
}
#[derive(Debug, Deserialize, Serialize)]
pub(crate) struct FileSharing {
#[serde(rename = "@readOnlyRecommended")]
pub(crate) read_only_recommended: u8,
}
impl Default for FileSharing {
fn default() -> Self {
Self {
read_only_recommended: 0,
}
}
}
#[derive(Debug, Deserialize, Serialize, Default)]
struct WorkbookPr {
#[serde(rename = "@filterPrivacy", skip_serializing_if = "Option::is_none")]
filter_privacy: Option<u32>,
#[serde(rename = "@defaultThemeVersion", skip_serializing_if = "Option::is_none")]
default_theme_version: Option<String>,
}
#[derive(Debug, Deserialize, Serialize)]
pub(crate) struct Sheets {
#[serde(rename = "sheet")]
pub(crate) sheets: Vec<Sheet>
}
impl Default for Sheets {
fn default() -> Self {
Sheets {
sheets: vec![],
}
}
}
#[derive(Debug, Deserialize, Serialize)]
pub(crate) struct Sheet {
#[serde(rename = "@name")]
pub(crate) name: String,
#[serde(rename = "@sheetId")]
pub(crate) sheet_id: u32,
#[serde(rename(serialize = "@r:id", deserialize = "@id"))]
pub(crate) r_id: Rel,
#[serde(rename = "@state", skip_serializing_if = "Option::is_none")]
pub(crate) state: Option<String>,
}
impl Default for Sheet {
fn default() -> Sheet {
Sheet {
name: format!("sheet1"),
sheet_id: 1,
r_id: Rel::from_id(1), state: None,
}
}
}
impl Sheet {
pub(crate) fn by_id(r_id: u32, id: u32) -> Sheet {
Sheet {
name: format!("Sheet{id}"),
sheet_id: id,
r_id: Rel::from_id(r_id), state: None,
}
}
pub(crate) fn by_name(r_id: u32, id: u32, name: &str) -> Sheet {
Sheet {
name: String::from(name),
sheet_id: id,
r_id: Rel::from_id(r_id), state: None,
}
}
pub(crate) fn change_id(&mut self, id: u32) {
self.sheet_id = id;
self.r_id = Rel::from_id(id); }
}
#[derive(Debug, Deserialize, Serialize)]
struct CalcPr {
#[serde(rename = "@calcId", default, skip_serializing_if = "String::is_empty")]
calc_id: String,
}
impl Default for CalcPr {
fn default() -> Self {
CalcPr {
calc_id: String::from("162913"),
}
}
}
#[derive(Debug, Deserialize, Serialize)]
struct XrRevisionPtr {
#[serde(rename = "@revIDLastSave", skip_serializing_if = "Option::is_none")]
rev_id_last_save: Option<u32>,
#[serde(rename = "@documentId", skip_serializing_if = "Option::is_none")]
document_id: Option<String>,
#[serde(rename(serialize = "@xr6:coauthVersionLast", deserialize = "@coauthVersionLast"), skip_serializing_if = "Option::is_none")]
xr6_coauth_version_last: Option<u32>,
#[serde(rename(serialize = "@xr6:coauthVersionMax", deserialize = "@coauthVersionMax"), skip_serializing_if = "Option::is_none")]
xr6_coauth_version_max: Option<u32>,
#[serde(rename(serialize = "@xr10:uidLastSave", deserialize = "@uidLastSave"), skip_serializing_if = "Option::is_none")]
xr10_uid_last_save: Option<String>,
}
impl Default for Workbook {
fn default() -> Self {
Workbook {
xmlns_attrs: XmlnsAttrs::workbook_default(),
file_version: Default::default(),
file_sharing: None,
workbook_pr: Default::default(),
xr_revision_ptr: None,
book_views: Default::default(),
sheets: Default::default(),
defined_names: Default::default(),
calc_pr: Default::default(),
ext_lst: Some(Default::default()),
}
}
}
impl Workbook {
pub(crate) fn from_file(file: &mut ZipFile) -> Workbook {
let mut xml = String::new();
file.read_to_string(&mut xml).unwrap();
de::from_str(&xml).unwrap_or_default()
}
}
impl Io<Workbook> for Workbook {
fn save<P: AsRef<Path>>(& self, file_path: P) {
let xml = se::to_string_with_root("workbook", &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::WorkbookFile).unwrap();
file.write_all(xml.as_ref()).unwrap();
}
}