1use crate::error::{IoSnafu, JarError, Result};
2use snafu::ResultExt;
3use std::collections::HashMap;
4use std::fs::File;
5use std::io::{BufReader, Read};
6use std::path::Path;
7use zip::ZipArchive;
8
9#[derive(Debug)]
11pub struct JarFile {
12 zip_file: ZipArchive<BufReader<File>>,
13 file_name_index: HashMap<String, usize>,
14}
15
16impl JarFile {
17 pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
19 let file = File::open(path.as_ref()).context(IoSnafu {
20 path: path.as_ref().to_path_buf(),
21 })?;
22 let reader = BufReader::new(file);
23 let mut zip_file = ZipArchive::new(reader)?;
24
25 let mut file_name_index = HashMap::with_capacity(zip_file.len());
26 for i in 0..zip_file.len() {
27 let file = zip_file.by_index(i)?;
28 if file.is_file() {
29 let name = file.name().to_string();
30 file_name_index.insert(name, i);
31 }
32 }
33
34 Ok(JarFile {
35 zip_file,
36 file_name_index,
37 })
38 }
39
40 pub fn file_names(&self) -> Vec<String> {
42 self.file_name_index.keys().cloned().collect()
43 }
44
45 pub fn content_by_name(&mut self, name: &str) -> Result<Vec<u8>> {
47 let index = self
48 .file_name_index
49 .get(name)
50 .ok_or_else(|| JarError::EntryNotFound {
51 entry_name: name.to_string(),
52 })?;
53 let mut file = self.zip_file.by_index(*index)?;
54 let mut buffer = Vec::with_capacity(file.size() as usize);
55 file.read_to_end(&mut buffer).map_err(|e| JarError::Io {
56 source: e,
57 path: name.into(),
58 })?;
59 Ok(buffer)
60 }
61}