mod rel_type;
mod rel;
use std::collections::HashMap;
use std::fs::File;
use serde::{Deserialize, Serialize};
use std::io::Read;
use std::path::Path;
use quick_xml::{de, se};
use zip::read::ZipFile;
use zip::ZipArchive;
use crate::api::relationship::Rel;
use crate::file::{XlsxFileType, XlsxFileWriter};
use crate::xml::relationships::rel::RelationShip;
use crate::xml::relationships::rel_type::RelType;
use crate::xml::workbook::Workbook;
#[derive(Debug, Clone, Deserialize, Serialize)]
pub(crate) struct Relationships {
#[serde(rename = "@xmlns")]
xmlns: String,
#[serde(rename = "Relationship", default)]
relationship: Vec<RelationShip>,
#[serde(skip)]
targets: Targets,
}
#[derive(Debug, Clone, Default)]
struct Targets {
target: HashMap<String, Vec<u32>>,
}
impl Targets {
fn add_target(&mut self, rel_type: &RelType, name: &str) {
let name = Path::new(name).file_stem().unwrap().to_str().unwrap();
let id = name.chars().filter(|&c| c >= '0' && c <= '9').collect::<String>().parse().unwrap_or(1);
let mut target = self.target.get_mut(rel_type.get_type());
let mut vec = Vec::new();
let ids = target.get_or_insert(&mut vec);
ids.push(id);
}
fn next_target(&mut self, rel_type: RelType, id: u32) -> String {
match rel_type {
RelType::Worksheets => format!("worksheets/sheet{id}.xml"),
RelType::Theme => format!("theme/theme{id}.xml"),
RelType::Styles => String::from("styles.xml"),
RelType::Images => format!("../media/image{id}.png"),
RelType::Drawings => format!("../drawings/drawing{id}.xml"),
RelType::Hyperlinks => { "".to_string() }
RelType::MetaData => "metadata.xml".to_string(),
RelType::CalcChain => "calcChain.xml".to_string(),
RelType::Table => format!("../tables/table{id}.xml"),
RelType::Chart => format!("../charts/chart{id}.xml"),
RelType::SharedStrings => { "".to_string() }
RelType::PrinterSettings => { "".to_string() }
RelType::VmlDrawing => { "".to_string() }
RelType::Comments => { "".to_string() }
RelType::Unknown => { "".to_string() }
}
}
}
unsafe impl Sync for Relationships {}
unsafe impl Send for Relationships {}
impl Default for Relationships {
fn default() -> Self {
Relationships {
xmlns: "http://schemas.openxmlformats.org/package/2006/relationships".to_string(),
relationship: vec![],
targets: Default::default(),
}
}
}
impl Relationships {
pub(crate) fn next_id(&self) -> u32 {
1 + self.relationship.len() as u32
}
pub(crate) fn add_worksheet_v2(&mut self) -> (u32, u32) {
let r_id = self.next_id();
let sheet_target_id: u32 = 1 + self.relationship
.iter()
.filter(|r| r.rel_type == RelType::Worksheets)
.map(|r| r.target[16..r.target.len() - 4].parse().unwrap_or(0))
.max()
.unwrap_or(0);
let rel = RelationShip::new(r_id, RelType::Worksheets, &format!("worksheets/sheet{sheet_target_id}.xml"), None);
self.relationship.push(rel);
(r_id, sheet_target_id)
}
pub(crate) fn next_sheet_target_id(&self) -> u32 {
1 + self.relationship.iter()
.filter(|r| r.rel_type == RelType::Worksheets)
.map(|r| r.target.chars().filter(|&c| c >= '0' && c <= '9').collect::<String>().parse().unwrap_or(0))
.max()
.unwrap_or(0)
}
pub(crate) fn get_drawings_rids(&self) -> Vec<u32> {
let binding = self.get_target_by_type(RelType::Drawings);
let rids: Vec<u32> = binding.iter()
.map(|s|s.chars().filter(|&c| c >= '0' && c <= '9').collect::<String>().parse().unwrap())
.collect();
rids
}
pub(crate) fn get_vml_drawing_rid(&self) -> Option<u32> {
self.get_rid_by_type(RelType::VmlDrawing).first().copied()
}
pub(crate) fn get_target(&self, r_id: &Rel) -> (&String, u32) {
let target = self.relationship.iter()
.find(|r| r.id == *r_id)
.map(|r| &r.target)
.unwrap();
let target_id: u32 = target[16..target.len() - 4].parse().unwrap();
(target, target_id)
}
fn get_rid_by_type(&self, rel_type: RelType) -> Vec<u32> {
self.relationship
.iter()
.filter(|r| r.rel_type == rel_type)
.map(|r| r.id.get_id())
.collect()
}
fn get_target_by_type(&self, rel_type: RelType) -> Vec<String> {
self.relationship
.iter()
.filter(|r| r.rel_type == rel_type)
.map(|r| r.target.clone())
.collect()
}
fn exist_type(&self, rel_type: RelType) -> bool {
self.relationship
.iter()
.filter(|r| r.rel_type == rel_type)
.count() > 0
}
pub(crate) fn add_worksheet(&mut self, id: u32) -> (u32, String) {
let r_id = self.next_id();
let target = self.targets.next_target(RelType::Worksheets, id);
let rel = RelationShip::new(r_id, RelType::Worksheets, &target, None);
self.relationship.push(rel);
(r_id, target.clone())
}
pub(crate) fn add_image(&mut self, id: u32, image_extension: &str) -> u32 {
let r_id = self.next_id();
self.relationship.push(RelationShip::new_image(r_id, id, image_extension));
r_id
}
pub(crate) fn add_hyperlink(&mut self, target: &str) -> u32 {
let r_id = self.next_id();
self.relationship.push(RelationShip::new_hyperlink(r_id, target));
r_id
}
pub(crate) fn add_drawings(&mut self, id: u32) -> u32 {
let r = self.relationship
.iter()
.find(|r| r.rel_type == RelType::Drawings);
if let Some(r) = r {
return r.id.get_id();
}
let r_id = self.next_id();
self.relationship.push(RelationShip::new_drawing(r_id, id));
r_id
}
pub(crate) fn get_or_add_metadata(&mut self) -> u32 {
let r_id = self.get_rid_by_type(RelType::MetaData);
if r_id.is_empty() {
let r_id = self.next_id();
self.relationship.push(RelationShip::new_metadata(r_id));
return r_id;
}
return r_id[0]
}
}
impl Relationships {
pub(crate) async fn save_async<P: AsRef<Path>>(&self, file_path: P, rel_type: XlsxFileType) {
self.save(file_path, rel_type)
}
pub(crate) fn save<P: AsRef<Path>>(&self, file_path: P, rel_type: XlsxFileType) {
let xml = se::to_string_with_root("Relationships", &self).unwrap();
let xml = format!("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n{}", xml);
let mut file = XlsxFileWriter::from_path(file_path, rel_type).unwrap();
file.write_all(xml.as_ref()).unwrap();
}
}