1use std::cell::RefCell;
2use std::fs;
3use std::io::{self, Cursor, ErrorKind, Read, Seek, SeekFrom};
4use std::path::Path;
5
6use zip_::ZipArchive;
7
8use crate::index::Index;
9use crate::store::Store;
10use crate::{Entries, Entry};
11
12pub struct ZipFs<T: Read + Seek> {
19 inner: RefCell<T>,
20 index: Option<Index<()>>,
21}
22
23pub struct ZipFsFile {
25 inner: Cursor<Box<[u8]>>,
26}
27
28impl Read for ZipFsFile {
29 #[inline]
30 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
31 self.inner.read(buf)
32 }
33}
34
35impl Seek for ZipFsFile {
36 #[inline]
37 fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
38 self.inner.seek(pos)
39 }
40}
41
42impl ZipFs<fs::File> {
43 pub fn open<P: AsRef<Path>>(path: P) -> io::Result<Self> {
44 let file = fs::OpenOptions::new()
45 .read(true)
46 .write(false)
47 .create(false)
48 .open(path)?;
49 Ok(Self::new(file))
50 }
51}
52
53impl<T: Read + Seek> ZipFs<T> {
54 pub fn new(inner: T) -> Self {
55 Self {
56 inner: RefCell::new(inner),
57 index: None,
58 }
59 }
60
61 pub fn index(mut self) -> io::Result<Self> {
66 let mut index = Index::new();
67 let mut file = self.inner.borrow_mut();
68 file.seek(SeekFrom::Start(0))?;
69 let mut archive = ZipArchive::new(&mut *file)?;
70 for i in 0..archive.len() {
71 let file = archive.by_index(i)?;
72 let path = file.sanitized_name();
73
74 index.insert(path, ());
75 }
76 self.index = Some(index);
77 drop(file);
78 Ok(self)
79 }
80}
81
82impl<T: Read + Seek> Store for ZipFs<T> {
83 type File = ZipFsFile;
84 fn open_path(&self, path: &Path) -> io::Result<Self::File> {
85 let mut file = self.inner.borrow_mut();
86 file.seek(SeekFrom::Start(0))?;
87
88 let mut archive = ZipArchive::new(&mut *file)?;
89 let name = path.to_str().ok_or(io::Error::new(
90 ErrorKind::Other,
91 "Utf8 path conversion error.",
92 ));
93 let mut file = archive.by_name(name?)?;
94
95 let mut v = Vec::new();
96 file.read_to_end(&mut v)?;
97 Ok(ZipFsFile {
98 inner: Cursor::new(v.into()),
99 })
100 }
101
102 fn entries_path(&self, path: &Path) -> io::Result<Entries> {
103 if let Some(ref idx) = self.index {
104 Ok(Entries::new(idx.entries(path).map(|ent| {
105 let name = ent.name.to_os_string();
106 let kind = ent.kind;
107 Ok(Entry { name, kind })
108 })))
109 } else {
110 panic!("You have to call the `Zip::index` method on this zip archive before you can list its entries.")
111 }
112 }
113}