use rc_zip::{
error::Error,
fsm::{ArchiveFsm, FsmResult},
parse::Archive,
};
use rc_zip::{fsm::EntryFsm, parse::Entry};
use tracing::trace;
use crate::entry_reader::EntryReader;
use crate::streaming_entry_reader::StreamingEntryReader;
use std::{io::Read, ops::Deref};
pub trait ReadZipWithSize {
type File: HasCursor;
fn read_zip_with_size(&self, size: u64) -> Result<ArchiveHandle<'_, Self::File>, Error>;
}
pub trait ReadZip {
type File: HasCursor;
fn read_zip(&self) -> Result<ArchiveHandle<'_, Self::File>, Error>;
}
impl<F> ReadZipWithSize for F
where
F: HasCursor,
{
type File = F;
fn read_zip_with_size(&self, size: u64) -> Result<ArchiveHandle<'_, F>, Error> {
struct CursorState<'a, F: HasCursor + 'a> {
cursor: <F as HasCursor>::Cursor<'a>,
offset: u64,
}
let mut cstate: Option<CursorState<'_, F>> = None;
let mut fsm = ArchiveFsm::new(size);
loop {
if let Some(offset) = fsm.wants_read() {
trace!(%offset, "read_zip_with_size: wants_read, space len = {}", fsm.space().len());
let mut cstate_next = match cstate.take() {
Some(cstate) => {
if cstate.offset == offset {
cstate
} else {
CursorState {
cursor: self.cursor_at(offset),
offset,
}
}
}
None => CursorState {
cursor: self.cursor_at(offset),
offset,
},
};
match cstate_next.cursor.read(fsm.space()) {
Ok(read_bytes) => {
cstate_next.offset += read_bytes as u64;
cstate = Some(cstate_next);
trace!(%read_bytes, "read_zip_with_size: read");
if read_bytes == 0 {
return Err(Error::IO(std::io::ErrorKind::UnexpectedEof.into()));
}
fsm.fill(read_bytes);
}
Err(err) => return Err(Error::IO(err)),
}
}
fsm = match fsm.process()? {
FsmResult::Done(archive) => {
trace!("read_zip_with_size: done");
return Ok(ArchiveHandle {
file: self,
archive,
});
}
FsmResult::Continue(fsm) => fsm,
}
}
}
}
impl ReadZip for &[u8] {
type File = Self;
fn read_zip(&self) -> Result<ArchiveHandle<'_, Self::File>, Error> {
self.read_zip_with_size(self.len() as u64)
}
}
impl ReadZip for Vec<u8> {
type File = Self;
fn read_zip(&self) -> Result<ArchiveHandle<'_, Self::File>, Error> {
self.read_zip_with_size(self.len() as u64)
}
}
pub struct ArchiveHandle<'a, F>
where
F: HasCursor,
{
file: &'a F,
archive: Archive,
}
impl<F> Deref for ArchiveHandle<'_, F>
where
F: HasCursor,
{
type Target = Archive;
fn deref(&self) -> &Self::Target {
&self.archive
}
}
impl<F> ArchiveHandle<'_, F>
where
F: HasCursor,
{
pub fn entries(&self) -> impl Iterator<Item = EntryHandle<'_, F>> {
self.archive.entries().map(move |entry| EntryHandle {
file: self.file,
entry,
})
}
pub fn by_name<N: AsRef<str>>(&self, name: N) -> Option<EntryHandle<'_, F>> {
self.archive
.entries()
.find(|&x| x.name == name.as_ref())
.map(|entry| EntryHandle {
file: self.file,
entry,
})
}
}
pub struct EntryHandle<'a, F> {
file: &'a F,
entry: &'a Entry,
}
impl<F> Deref for EntryHandle<'_, F> {
type Target = Entry;
fn deref(&self) -> &Self::Target {
self.entry
}
}
impl<'a, F> EntryHandle<'a, F>
where
F: HasCursor,
{
pub fn reader(&self) -> impl Read + 'a {
EntryReader::new(self.entry, self.file.cursor_at(self.entry.header_offset))
}
pub fn bytes(&self) -> std::io::Result<Vec<u8>> {
let mut v = Vec::new();
self.reader().read_to_end(&mut v)?;
Ok(v)
}
}
pub trait HasCursor {
type Cursor<'a>: Read + 'a
where
Self: 'a;
fn cursor_at(&self, offset: u64) -> Self::Cursor<'_>;
}
impl HasCursor for &[u8] {
type Cursor<'a> = &'a [u8]
where
Self: 'a;
fn cursor_at(&self, offset: u64) -> Self::Cursor<'_> {
&self[offset.try_into().unwrap()..]
}
}
impl HasCursor for Vec<u8> {
type Cursor<'a> = &'a [u8]
where
Self: 'a;
fn cursor_at(&self, offset: u64) -> Self::Cursor<'_> {
&self[offset.try_into().unwrap()..]
}
}
#[cfg(feature = "file")]
impl HasCursor for std::fs::File {
type Cursor<'a> = positioned_io::Cursor<&'a std::fs::File>
where
Self: 'a;
fn cursor_at(&self, offset: u64) -> Self::Cursor<'_> {
positioned_io::Cursor::new_pos(self, offset)
}
}
#[cfg(feature = "file")]
impl ReadZip for std::fs::File {
type File = Self;
fn read_zip(&self) -> Result<ArchiveHandle<'_, Self>, Error> {
let size = self.metadata()?.len();
self.read_zip_with_size(size)
}
}
pub trait ReadZipStreaming<R>
where
R: Read,
{
fn stream_zip_entries_throwing_caution_to_the_wind(
self,
) -> Result<StreamingEntryReader<R>, Error>;
}
impl<R> ReadZipStreaming<R> for R
where
R: Read,
{
fn stream_zip_entries_throwing_caution_to_the_wind(
mut self,
) -> Result<StreamingEntryReader<Self>, Error> {
let mut fsm = EntryFsm::new(None, None);
loop {
if fsm.wants_read() {
let n = self.read(fsm.space())?;
trace!("read {} bytes into buf for first zip entry", n);
fsm.fill(n);
}
if let Some(entry) = fsm.process_till_header()? {
let entry = entry.clone();
return Ok(StreamingEntryReader::new(fsm, entry, self));
}
}
}
}