use log::warn;
use serde::{Deserialize, Deserializer};
use crate::docx::error::Result;
use crate::docx::model::RelId;
use crate::docx::parse::serde_xml::from_xml;
#[derive(Clone, Debug)]
pub struct Relationship {
pub id: RelId,
pub rel_type: RelationshipType,
pub target: String,
pub target_mode: TargetMode,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RelationshipType {
OfficeDocument,
Styles,
Numbering,
Settings,
FontTable,
Theme,
Header,
Footer,
Footnotes,
Endnotes,
Font,
Image,
Hyperlink,
Comments,
CoreProperties,
ExtendedProperties,
CustomProperties,
CustomXml,
WebSettings,
StylesWithEffects,
GlossaryDocument,
Unknown(String),
}
impl RelationshipType {
fn from_uri(uri: &str) -> Self {
if uri.ends_with("/officeDocument") || uri.ends_with("/document") {
Self::OfficeDocument
} else if uri.ends_with("/styles") {
Self::Styles
} else if uri.ends_with("/numbering") {
Self::Numbering
} else if uri.ends_with("/settings") {
Self::Settings
} else if uri.ends_with("/fontTable") {
Self::FontTable
} else if uri.ends_with("/theme") {
Self::Theme
} else if uri.ends_with("/header") {
Self::Header
} else if uri.ends_with("/footer") {
Self::Footer
} else if uri.ends_with("/footnotes") {
Self::Footnotes
} else if uri.ends_with("/endnotes") {
Self::Endnotes
} else if uri.ends_with("/font") {
Self::Font
} else if uri.ends_with("/image") {
Self::Image
} else if uri.ends_with("/hyperlink") {
Self::Hyperlink
} else if uri.ends_with("/comments") {
Self::Comments
} else if uri.ends_with("/core-properties") {
Self::CoreProperties
} else if uri.ends_with("/extended-properties") {
Self::ExtendedProperties
} else if uri.ends_with("/custom-properties") {
Self::CustomProperties
} else if uri.ends_with("/customXml") {
Self::CustomXml
} else if uri.ends_with("/webSettings") {
Self::WebSettings
} else if uri.ends_with("/stylesWithEffects") {
Self::StylesWithEffects
} else if uri.ends_with("/glossaryDocument") {
Self::GlossaryDocument
} else {
warn!("unknown relationship type: {}", uri);
Self::Unknown(uri.to_string())
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum TargetMode {
#[default]
Internal,
External,
}
impl<'de> Deserialize<'de> for TargetMode {
fn deserialize<D: Deserializer<'de>>(d: D) -> std::result::Result<Self, D::Error> {
let s = String::deserialize(d)?;
Ok(if s.eq_ignore_ascii_case("external") {
TargetMode::External
} else {
TargetMode::Internal
})
}
}
#[derive(Clone, Debug, Default)]
pub struct Relationships {
pub(crate) rels: Vec<Relationship>,
}
impl Relationships {
pub fn parse(data: &[u8]) -> Result<Self> {
from_xml::<RelationshipsXml>(data).map(Into::into)
}
pub fn find_by_type(&self, rel_type: &RelationshipType) -> Option<&Relationship> {
self.rels.iter().find(|r| &r.rel_type == rel_type)
}
pub fn filter_by_type(&self, rel_type: &RelationshipType) -> Vec<&Relationship> {
self.rels
.iter()
.filter(|r| &r.rel_type == rel_type)
.collect()
}
pub fn find_by_id(&self, id: &str) -> Option<&Relationship> {
self.rels.iter().find(|r| r.id.as_str() == id)
}
pub fn all(&self) -> &[Relationship] {
&self.rels
}
}
#[derive(Deserialize, Default)]
struct RelationshipsXml {
#[serde(rename = "Relationship", default)]
rels: Vec<RelationshipXml>,
}
#[derive(Deserialize)]
struct RelationshipXml {
#[serde(rename = "@Id")]
id: String,
#[serde(rename = "@Type")]
rel_type: String,
#[serde(rename = "@Target")]
target: String,
#[serde(rename = "@TargetMode", default)]
target_mode: TargetMode,
}
impl From<RelationshipsXml> for Relationships {
fn from(x: RelationshipsXml) -> Self {
Self {
rels: x.rels.into_iter().map(Relationship::from).collect(),
}
}
}
impl From<RelationshipXml> for Relationship {
fn from(r: RelationshipXml) -> Self {
Self {
id: RelId::new(r.id),
rel_type: RelationshipType::from_uri(&r.rel_type),
target: r.target,
target_mode: r.target_mode,
}
}
}