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