use std::io::Write;
use std::path::Path;
use crate::entry::{EntryInfo, EntryKind};
use crate::error::Error;
pub struct SevenZAdapter {
entries: Vec<SevenZEntry>,
}
struct SevenZEntry {
info: EntryInfo,
data: Vec<u8>,
}
impl SevenZAdapter {
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
let path = path.as_ref();
let entries = Self::decompress_all(path)?;
Ok(Self { entries })
}
pub fn from_bytes(data: &[u8]) -> Result<Self, Error> {
let mut temp = tempfile::NamedTempFile::new()?;
temp.write_all(data)?;
temp.flush()?;
Self::open(temp.path())
}
fn decompress_all(path: &Path) -> Result<Vec<SevenZEntry>, Error> {
let mut entries = Vec::new();
let mut archive = sevenz_rust::SevenZReader::open(path, sevenz_rust::Password::empty())
.map_err(|e| Error::Io(std::io::Error::other(format!("7z open error: {}", e))))?;
archive
.for_each_entries(|entry, reader| {
let name = entry.name().to_string();
let kind = if entry.is_directory() {
EntryKind::Directory
} else {
EntryKind::File
};
let mut data = Vec::new();
if matches!(kind, EntryKind::File) {
reader.read_to_end(&mut data)?;
}
let info = EntryInfo {
name,
size: data.len() as u64,
kind,
mode: None, };
entries.push(SevenZEntry { info, data });
Ok(true)
})
.map_err(|e| Error::Io(std::io::Error::other(format!("7z read error: {}", e))))?;
Ok(entries)
}
pub fn entries_metadata(&self) -> Vec<EntryInfo> {
self.entries.iter().map(|e| e.info.clone()).collect()
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn for_each<F>(&self, mut callback: F) -> Result<(), Error>
where
F: FnMut(&EntryInfo, Option<&[u8]>) -> Result<bool, Error>,
{
for entry in &self.entries {
let data = if matches!(entry.info.kind, EntryKind::File) {
Some(entry.data.as_slice())
} else {
None
};
if !callback(&entry.info, data)? {
break;
}
}
Ok(())
}
}