epub_parser/
zip_handler.rs

1use crate::epub::Error;
2use std::fs::File;
3use std::io::Read;
4use std::path::Path;
5use zip::ZipArchive;
6
7pub struct ZipHandler {
8    archive: ZipArchive<File>,
9}
10
11impl ZipHandler {
12    pub fn new(path: &Path) -> Result<Self, Error> {
13        let file = File::open(path)?;
14        let archive = ZipArchive::new(file)?;
15        Ok(ZipHandler { archive })
16    }
17
18    pub fn get_opf_path(&mut self) -> Result<String, Error> {
19        let container_content = self.read_file("META-INF/container.xml")?;
20
21        let mut reader = quick_xml::Reader::from_str(&container_content);
22        let mut opf_path = String::new();
23
24        let mut buf = Vec::new();
25
26        loop {
27            match reader.read_event_into(&mut buf) {
28                Ok(quick_xml::events::Event::Start(ref e)) => {
29                    let name = String::from_utf8_lossy(e.name().as_ref()).to_string();
30
31                    if name == "rootfile" || name.ends_with(":rootfile") {
32                        for attr_result in e.attributes() {
33                            if let Ok(attr) = attr_result {
34                                let attr_name =
35                                    String::from_utf8_lossy(attr.key.as_ref()).to_string();
36
37                                if attr_name == "full-path" || attr_name.ends_with(":full-path") {
38                                    opf_path = attr
39                                        .decode_and_unescape_value(reader.decoder())?
40                                        .to_string();
41                                    break;
42                                }
43                            }
44                        }
45                        if !opf_path.is_empty() {
46                            break;
47                        }
48                    }
49                }
50                Ok(quick_xml::events::Event::Empty(ref e)) => {
51                    let name = String::from_utf8_lossy(e.name().as_ref()).to_string();
52
53                    if name == "rootfile" || name.ends_with(":rootfile") {
54                        for attr_result in e.attributes() {
55                            if let Ok(attr) = attr_result {
56                                let attr_name =
57                                    String::from_utf8_lossy(attr.key.as_ref()).to_string();
58
59                                if attr_name == "full-path" || attr_name.ends_with(":full-path") {
60                                    opf_path = attr
61                                        .decode_and_unescape_value(reader.decoder())?
62                                        .to_string();
63                                    break;
64                                }
65                            }
66                        }
67                        if !opf_path.is_empty() {
68                            break;
69                        }
70                    }
71                }
72                Ok(quick_xml::events::Event::Eof) => break,
73                Err(e) => return Err(Error::XmlError(e.to_string())),
74                _ => {}
75            }
76            buf.clear();
77        }
78
79        if opf_path.is_empty() {
80            return Err(Error::MissingOpf);
81        }
82
83        Ok(opf_path)
84    }
85
86    pub fn read_file(&mut self, path: &str) -> Result<String, Error> {
87        let mut file = self.archive.by_name(path)?;
88        let mut content = String::new();
89        file.read_to_string(&mut content)?;
90        Ok(content)
91    }
92
93    pub fn read_file_as_bytes(&mut self, path: &str) -> Result<Vec<u8>, Error> {
94        let mut file = self.archive.by_name(path)?;
95        let mut content = Vec::new();
96        file.read_to_end(&mut content)?;
97        Ok(content)
98    }
99}