docx_rs/reader/
rels.rs

1use super::*;
2use crate::reader::{FromXML, ReaderError};
3use std::str::FromStr;
4use std::{
5    collections::{BTreeMap, BTreeSet},
6    io::Read,
7    path::{Path, PathBuf},
8};
9use xml::reader::{EventReader, XmlEvent};
10
11pub type ReadRels = BTreeMap<String, BTreeSet<(RId, PathBuf, Option<String>)>>;
12
13impl FromXML for Rels {
14    fn from_xml<R: Read>(reader: R) -> Result<Self, ReaderError> {
15        let parser = EventReader::new(reader);
16        let mut s = Self::default();
17        let mut depth = 0;
18        for e in parser {
19            match e {
20                Ok(XmlEvent::StartElement { attributes, .. }) => {
21                    if depth == 1 {
22                        let mut rel_type = "".to_owned();
23                        let mut target = "".to_owned();
24                        for attr in attributes {
25                            let name: &str = &attr.name.local_name;
26                            if name == "Type" {
27                                rel_type = attr.value.clone();
28                            } else if name == "Target" {
29                                target = attr.value.clone();
30                            }
31                        }
32                        s = s.add_rel(rel_type, target);
33                    }
34                    depth += 1;
35                }
36                Ok(XmlEvent::EndElement { .. }) => {
37                    depth -= 1;
38                }
39                Err(_) => return Err(ReaderError::XMLReadError),
40                _ => {}
41            }
42        }
43        Ok(s)
44    }
45}
46
47pub fn find_rels_filename(main_path: impl AsRef<Path>) -> Result<PathBuf, ReaderError> {
48    let path = main_path.as_ref();
49    let dir = path
50        .parent()
51        .ok_or(ReaderError::DocumentRelsNotFoundError)?;
52    let base = path
53        .file_stem()
54        .ok_or(ReaderError::DocumentRelsNotFoundError)?;
55    Ok(Path::new(dir)
56        .join("_rels")
57        .join(base)
58        .with_extension("xml.rels"))
59}
60
61pub fn read_rels_xml<R: Read>(reader: R, dir: impl AsRef<Path>) -> Result<ReadRels, ReaderError> {
62    let mut parser = EventReader::new(reader);
63    let mut rels: BTreeMap<String, BTreeSet<(RId, PathBuf, Option<String>)>> = BTreeMap::new();
64
65    loop {
66        let e = parser.next();
67        match e {
68            Ok(XmlEvent::StartElement {
69                attributes, name, ..
70            }) => {
71                let e = XMLElement::from_str(&name.local_name).unwrap();
72                if let XMLElement::Relationship = e {
73                    let mut rel_type = "".to_owned();
74                    let mut rid = "".to_owned();
75                    let mut target_mode = None;
76                    let mut target_string = "".to_owned();
77                    for a in attributes {
78                        let local_name = &a.name.local_name;
79                        if local_name == "Type" {
80                            rel_type = a.value.to_owned();
81                        } else if local_name == "Target" {
82                            // target_str = Path::new(dir.as_ref()).join(a.value);
83                            target_string = a.value.to_owned();
84                        } else if local_name == "Id" {
85                            rid = a.value.to_owned();
86                        } else if local_name == "TargetMode" {
87                            target_mode = Some(a.value.to_owned());
88                        }
89                    }
90
91                    let target = if !rel_type.ends_with("hyperlink") {
92                        Path::new(dir.as_ref()).join(target_string)
93                    } else {
94                        Path::new("").join(target_string)
95                    };
96
97                    let current = rels.remove(&rel_type);
98                    if let Some(mut paths) = current {
99                        paths.insert((rid, target, target_mode));
100                        rels.insert(rel_type, paths);
101                    } else {
102                        let s: BTreeSet<(RId, PathBuf, Option<String>)> =
103                            vec![(rid, target, target_mode)].into_iter().collect();
104                        rels.insert(rel_type, s);
105                    }
106                    continue;
107                }
108            }
109            Ok(XmlEvent::EndElement { name, .. }) => {
110                let e = XMLElement::from_str(&name.local_name).unwrap();
111                if let XMLElement::Relationships = e {
112                    break;
113                }
114            }
115            Err(_) => return Err(ReaderError::XMLReadError),
116            _ => {}
117        }
118    }
119    Ok(rels)
120}
121
122#[cfg(test)]
123mod tests {
124
125    use super::*;
126    #[cfg(test)]
127    use pretty_assertions::assert_eq;
128
129    #[test]
130    fn test_from_xml() {
131        let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
132<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
133  <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml" />
134</Relationships>"#;
135        let c = Rels::from_xml(xml.as_bytes()).unwrap();
136        let rels =
137            vec![
138        (
139            "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"
140                .to_owned(),
141            "rId1".to_owned(),
142            "docProps/core.xml".to_owned(),
143        )];
144        assert_eq!(Rels { rels }, c);
145    }
146}