use crate::base::read::counting::Counting;
use crate::base::read::io::entry::ZipEntryReader;
use crate::error::Result;
use crate::error::ZipError;
use crate::spec::data_descriptor::{CombinedDataDescriptor, DataDescriptor, Zip64DataDescriptor};
#[cfg(feature = "tokio")]
use crate::tokio::read::stream::Ready as TokioReady;
use futures_lite::io::AsyncBufRead;
use futures_lite::io::AsyncReadExt;
use super::io::entry::WithEntry;
use super::io::entry::WithoutEntry;
use crate::spec::header::HeaderId;
#[cfg(feature = "tokio")]
use tokio_util::compat::TokioAsyncReadCompatExt;
pub struct Ready<R>(R);
pub struct Reading<'a, R, E>(ZipEntryReader<'a, R, E>, Option<Suffix>);
#[derive(Copy, Clone, Debug)]
enum Suffix {
DataDescriptor,
Zip64DataDescriptor,
}
#[derive(Clone)]
pub struct ZipFileReader<S>(S);
impl<'a, R> ZipFileReader<Ready<Counting<R>>>
where
R: AsyncBufRead + Unpin + 'a,
{
pub fn new(reader: R) -> Self {
Self(Ready(Counting::new(reader)))
}
pub async fn next_without_entry(mut self) -> Result<Option<ZipFileReader<Reading<'a, Counting<R>, WithoutEntry>>>> {
let file_offset = self.0 .0.bytes_read();
let entry = match crate::base::read::lfh(&mut self.0 .0, file_offset).await? {
Some(entry) => entry,
None => return Ok(None),
};
let length = if entry.data_descriptor { u64::MAX } else { entry.compressed_size };
let reader = ZipEntryReader::new_with_owned(self.0 .0, entry.compression, length);
let suffix = if entry.data_descriptor {
if entry.extra_fields.iter().any(|ef| ef.header_id() == HeaderId::ZIP64_EXTENDED_INFORMATION_EXTRA_FIELD) {
Some(Suffix::Zip64DataDescriptor)
} else {
Some(Suffix::DataDescriptor)
}
} else {
None
};
Ok(Some(ZipFileReader(Reading(reader, suffix))))
}
pub async fn next_with_entry(mut self) -> Result<Option<ZipFileReader<Reading<'a, Counting<R>, WithEntry<'a>>>>> {
let file_offset = self.0 .0.bytes_read();
let entry = match crate::base::read::lfh(&mut self.0 .0, file_offset).await? {
Some(entry) => entry,
None => return Ok(None),
};
let length = if entry.data_descriptor { u64::MAX } else { entry.compressed_size };
let reader = ZipEntryReader::new_with_owned(self.0 .0, entry.compression, length);
let suffix = if entry.data_descriptor {
if entry.extra_fields.iter().any(|ef| ef.header_id() == HeaderId::ZIP64_EXTENDED_INFORMATION_EXTRA_FIELD) {
Some(Suffix::Zip64DataDescriptor)
} else {
Some(Suffix::DataDescriptor)
}
} else {
None
};
Ok(Some(ZipFileReader(Reading(reader.into_with_entry_owned(entry), suffix))))
}
pub async fn into_inner(self) -> R {
self.0 .0.into_inner()
}
pub fn offset(&self) -> u64 {
self.0 .0.bytes_read()
}
}
#[cfg(feature = "tokio")]
impl<R> ZipFileReader<TokioReady<R>>
where
R: tokio::io::AsyncBufRead + Unpin,
{
pub fn with_tokio(reader: R) -> ZipFileReader<TokioReady<R>> {
Self(Ready(reader.compat()))
}
}
type Next<R> = (Option<CombinedDataDescriptor>, ZipFileReader<Ready<R>>);
impl<'a, R, E> ZipFileReader<Reading<'a, R, E>>
where
R: AsyncBufRead + Unpin,
{
pub fn reader(&self) -> &ZipEntryReader<'a, R, E> {
&self.0 .0
}
pub fn reader_mut(&mut self) -> &mut ZipEntryReader<'a, R, E> {
&mut self.0 .0
}
pub async fn done(mut self) -> Result<Next<R>> {
if self.0 .0.read(&mut [0; 1]).await? != 0 {
return Err(ZipError::EOFNotReached);
}
let mut inner = self.0 .0.into_inner();
let data_descriptor = match self.0 .1 {
Some(Suffix::DataDescriptor) => {
Some(CombinedDataDescriptor::from(DataDescriptor::from_reader(&mut inner).await?))
}
Some(Suffix::Zip64DataDescriptor) => {
Some(CombinedDataDescriptor::from(Zip64DataDescriptor::from_reader(&mut inner).await?))
}
None => None,
};
let reader = ZipFileReader(Ready(inner));
Ok((data_descriptor, reader))
}
pub async fn skip(mut self) -> Result<Next<R>> {
while self.0 .0.read(&mut [0; 2048]).await? != 0 {}
let mut inner = self.0 .0.into_inner();
let data_descriptor = match self.0 .1 {
Some(Suffix::DataDescriptor) => {
Some(CombinedDataDescriptor::from(DataDescriptor::from_reader(&mut inner).await?))
}
Some(Suffix::Zip64DataDescriptor) => {
Some(CombinedDataDescriptor::from(Zip64DataDescriptor::from_reader(&mut inner).await?))
}
None => None,
};
let reader = ZipFileReader(Ready(inner));
Ok((data_descriptor, reader))
}
}