easyofd_reader/
zip_util.rs1use easyofd_core::{OfdError, OfdResult};
9use std::io::{Read, Seek};
10use std::path::Path;
11
12pub fn read_entry<R: Read + Seek>(
20 archive: &mut zip::ZipArchive<R>,
21 name: &str,
22) -> OfdResult<Vec<u8>> {
23 let mut file = archive
24 .by_name(name)
25 .map_err(|e| OfdError::Zip(format!("{name}: {e}")))?;
26 let mut buf = Vec::new();
27 file.read_to_end(&mut buf).map_err(OfdError::Io)?;
28 Ok(buf)
29}
30
31pub fn list_entries<R: Read + Seek>(archive: &mut zip::ZipArchive<R>) -> OfdResult<Vec<String>> {
39 let mut names = Vec::new();
40 for i in 0..archive.len() {
41 let file = archive
42 .by_index(i)
43 .map_err(|e| OfdError::Zip(format!("index {i}: {e}")))?;
44 names.push(file.name().to_string());
45 }
46 Ok(names)
47}
48
49pub fn extract_to_dir<R: Read + Seek>(
57 archive: &mut zip::ZipArchive<R>,
58 dest: &Path,
59) -> OfdResult<()> {
60 use std::fs;
61
62 for i in 0..archive.len() {
63 let mut file = archive
64 .by_index(i)
65 .map_err(|e| OfdError::Zip(format!("index {i}: {e}")))?;
66 let outpath = dest.join(file.name());
67
68 if file.name().ends_with('/') {
69 fs::create_dir_all(&outpath).map_err(OfdError::Io)?;
70 } else {
71 if let Some(parent) = outpath.parent() {
72 fs::create_dir_all(parent).map_err(OfdError::Io)?;
73 }
74 let mut outfile = fs::File::create(&outpath).map_err(OfdError::Io)?;
75 std::io::copy(&mut file, &mut outfile).map_err(OfdError::Io)?;
76 }
77 }
78 Ok(())
79}
80
81pub fn extract_bytes_to_dir(data: &[u8], dest: &Path) -> OfdResult<()> {
89 let reader = std::io::Cursor::new(data);
90 let mut archive = zip::ZipArchive::new(reader).map_err(|e| OfdError::Zip(format!("{e}")))?;
91 extract_to_dir(&mut archive, dest)
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97 use std::io::{Cursor, Write};
98
99 fn create_test_zip() -> Vec<u8> {
100 let mut buf = Vec::new();
101 {
102 let writer = Cursor::new(&mut buf);
103 let mut zip = zip::ZipWriter::new(writer);
104 let options = zip::write::SimpleFileOptions::default()
105 .compression_method(zip::CompressionMethod::Stored);
106
107 zip.start_file("OFD.xml", options).unwrap();
108 zip.write_all(b"<OFD/>").unwrap();
109
110 zip.start_file("Doc_0/Document.xml", options).unwrap();
111 zip.write_all(b"<Document/>").unwrap();
112
113 zip.finish().unwrap();
114 }
115 buf
116 }
117
118 #[test]
119 fn test_read_entry() {
120 let data = create_test_zip();
121 let reader = Cursor::new(&data);
122 let mut archive = zip::ZipArchive::new(reader).unwrap();
123 let content = read_entry(&mut archive, "OFD.xml").unwrap();
124 assert_eq!(content, b"<OFD/>");
125 }
126
127 #[test]
128 fn test_read_entry_not_found() {
129 let data = create_test_zip();
130 let reader = Cursor::new(&data);
131 let mut archive = zip::ZipArchive::new(reader).unwrap();
132 let result = read_entry(&mut archive, "nonexistent.xml");
133 assert!(result.is_err());
134 }
135
136 #[test]
137 fn test_list_entries() {
138 let data = create_test_zip();
139 let reader = Cursor::new(&data);
140 let mut archive = zip::ZipArchive::new(reader).unwrap();
141 let entries = list_entries(&mut archive).unwrap();
142 assert_eq!(entries.len(), 2);
143 assert!(entries.contains(&"OFD.xml".to_string()));
144 assert!(entries.contains(&"Doc_0/Document.xml".to_string()));
145 }
146
147 #[test]
148 fn test_extract_to_dir() {
149 let data = create_test_zip();
150 let reader = Cursor::new(&data);
151 let mut archive = zip::ZipArchive::new(reader).unwrap();
152 let dest = std::env::temp_dir().join("easyofd_zip_util_test");
153 let _ = std::fs::remove_dir_all(&dest);
154 std::fs::create_dir_all(&dest).unwrap();
155
156 extract_to_dir(&mut archive, &dest).unwrap();
157
158 assert!(dest.join("OFD.xml").exists());
159 assert!(dest.join("Doc_0/Document.xml").exists());
160
161 let content = std::fs::read_to_string(dest.join("OFD.xml")).unwrap();
162 assert_eq!(content, "<OFD/>");
163
164 let _ = std::fs::remove_dir_all(&dest);
165 }
166
167 #[test]
168 fn test_extract_bytes_to_dir() {
169 let data = create_test_zip();
170 let dest = std::env::temp_dir().join("easyofd_zip_util_bytes_test");
171 let _ = std::fs::remove_dir_all(&dest);
172 std::fs::create_dir_all(&dest).unwrap();
173
174 extract_bytes_to_dir(&data, &dest).unwrap();
175 assert!(dest.join("OFD.xml").exists());
176
177 let _ = std::fs::remove_dir_all(&dest);
178 }
179}