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_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}