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