docx_rust/
rels.rs

1//! Relationship item
2//!
3//! The corresponding ZIP item is `/_rels/.rels` (package-relationship) or
4//! `/word/_rels/document.xml.rels` (part-relationship).
5
6use hard_xml::{XmlRead, XmlResult, XmlWrite, XmlWriter};
7use std::borrow::Cow;
8use std::io::Write;
9
10use crate::__string_enum;
11use crate::schema::SCHEMA_RELATIONSHIPS;
12
13#[derive(Debug, Default, XmlRead, Clone)]
14#[xml(tag = "Relationships")]
15pub struct Relationships<'a> {
16    #[xml(child = "Relationship")]
17    pub relationships: Vec<Relationship<'a>>,
18}
19
20impl<'a> XmlWrite for Relationships<'a> {
21    fn to_writer<W: Write>(&self, writer: &mut XmlWriter<W>) -> XmlResult<()> {
22        let Relationships { relationships } = self;
23
24        log::debug!("[Relationships] Started writing.");
25        let _ = write!(writer.inner, "{}", crate::schema::SCHEMA_XML);
26
27        writer.write_element_start("Relationships")?;
28
29        writer.write_attribute("xmlns", SCHEMA_RELATIONSHIPS)?;
30
31        if relationships.is_empty() {
32            writer.write_element_end_empty()?;
33        } else {
34            writer.write_element_end_open()?;
35            for ele in relationships {
36                ele.to_writer(writer)?;
37            }
38            writer.write_element_end_close("Relationships")?;
39        }
40
41        log::debug!("[Relationships] Finished writing.");
42
43        Ok(())
44    }
45}
46
47impl<'a> Relationships<'a> {
48    pub fn add_rel(&mut self, schema: &'a str, target: &'a str) {
49        let has = self.relationships.iter().find(|r| r.target == target);
50        if has.is_none() {
51            let ids: Vec<_> = self
52                .relationships
53                .iter()
54                .map(|r| r.id.to_string())
55                .collect();
56
57            let len = self.relationships.len();
58
59            let mut available = false;
60            let mut id = len;
61            while !available {
62                id += 1;
63                let idstr = format!("rId{}", id);
64                available = !ids.contains(&idstr);
65            }
66
67            //hack
68            //let target = target.replace("jpeg","png");
69            self.relationships.push(Relationship {
70                id: format!("rId{}", id).into(),
71                target: target.into(),
72                ty: schema.into(),
73                target_mode: None,
74            });
75        }
76    }
77
78    pub fn add_rel_with_target_mode(
79        &mut self,
80        schema: &'a str,
81        target: &'a str,
82        target_mode: Option<&'a str>,
83    ) {
84        let has = self.relationships.iter().find(|r| r.target == target);
85        if has.is_none() {
86            let ids: Vec<_> = self
87                .relationships
88                .iter()
89                .map(|r| r.id.to_string())
90                .collect();
91
92            let len = self.relationships.len();
93
94            let mut available = false;
95            let mut id = len;
96            while !available {
97                id += 1;
98                let idstr = format!("rId{}", id);
99                available = !ids.contains(&idstr);
100            }
101
102            //hack
103            //let target = target.replace("jpeg","png");
104            self.relationships.push(Relationship {
105                id: format!("rId{}", id).into(),
106                target: target.into(),
107                ty: schema.into(),
108                target_mode: TargetMode::from_str(target_mode),
109            });
110        }
111    }
112
113    pub fn get_target(&self, id: &str) -> Option<&str> {
114        self.relationships
115            .iter()
116            .find(|r| r.id == id)
117            .map(|r| &*r.target)
118    }
119}
120
121#[derive(Debug, Clone, PartialEq)]
122#[cfg_attr(test, derive(Eq))]
123pub enum TargetMode {
124    Internal,
125    External,
126}
127
128__string_enum! {
129    TargetMode {
130        Internal = "Internal",
131        External = "External",
132    }
133}
134
135impl From<&str> for TargetMode {
136    fn from(value: &str) -> Self {
137        match value {
138            "External" => TargetMode::External,
139            _ => TargetMode::Internal,
140        }
141    }
142}
143
144impl TargetMode {
145    fn from_str(option_str: Option<&str>) -> Option<Self> {
146        match option_str {
147            Some(s) => Some(s.into()),
148            None => None,
149        }
150    }
151}
152
153#[derive(Debug, Default, XmlRead, XmlWrite, Clone)]
154#[xml(tag = "Relationship")]
155pub struct Relationship<'a> {
156    #[xml(attr = "Id")]
157    pub id: Cow<'a, str>,
158    #[xml(attr = "Target")]
159    pub target: Cow<'a, str>,
160    #[xml(attr = "Type")]
161    pub ty: Cow<'a, str>,
162    #[xml(attr = "TargetMode")]
163    pub target_mode: Option<TargetMode>,
164}