use crate::error::ZipError;
use std::{io::SeekFrom, ops::Deref, sync::Arc};
use futures_lite::{AsyncBufRead, AsyncSeek, AsyncSeekExt};
use crate::{base::read1::{file::ZipFileReader, ops::{Ops, SeekOps}, opts::ZipOptions}, error::Result, spec::constructs::{CDR, CEOCDR, LF}};
pub struct ZipArchiveReader<R> {
inner: Arc<ZipArchiveInner>,
reader: R,
}
impl<R: AsyncBufRead + AsyncSeek + Unpin> ZipArchiveReader<R> {
pub async fn open(mut reader: R) -> Result<Self> {
let opts = ZipOptions::default();
let inner = SeekOps::new(&mut reader).open(opts).await?;
Ok(Self::new_with_inner(reader, Arc::new(inner)))
}
pub async fn open_with_options(mut reader: R, opts: ZipOptions) -> Result<Self> {
let inner = SeekOps::new(&mut reader).open(opts).await?;
Ok(Self::new_with_inner(reader, Arc::new(inner)))
}
pub fn new_with_inner(reader: R, inner: Arc<ZipArchiveInner>) -> Self {
Self { reader, inner }
}
pub fn inner(&self) -> &Arc<ZipArchiveInner> {
&self.inner
}
pub async fn file(&mut self, index: usize) -> Result<ZipFileReader<&mut R>> {
let cdr = self.cdr(index).await?;
let lf = self.file_open(index).await?;
let opts = self.inner.options.clone();
ZipFileReader::new(&mut self.reader, lf, Some(cdr), opts)
}
pub async fn file_oneshot(mut self, index: usize) -> Result<ZipFileReader<R>> {
let cdr = self.cdr(index).await?;
let lf = self.file_open(index).await?;
let opts = self.inner.options.clone();
ZipFileReader::new(self.reader, lf, Some(cdr), opts)
}
pub fn into_factory<G: AsyncFn() -> Result<R>>(self, generator: G) -> ZipArchiveFactory<G, R> {
ZipArchiveFactory::new(generator, self)
}
async fn cdr(&mut self, index: usize) -> Result<CDR> {
if let Some(cdr) = self.loaded_cdrs.get(index) {
return Ok(cdr.clone());
}
let offset = self.cdr_offsets.get(index).ok_or(ZipError::EntryIndexOutOfBounds)?;
let offset = crate::base::read1::valid_offset(*offset, self.eor)?;
self.reader.seek(SeekFrom::Start(offset)).await?;
Ops::new(&mut self.reader, &self.inner.options).cdr(true).await
}
async fn file_open(&mut self, index: usize) -> Result<LF> {
let cdr = self.cdr(index).await?;
let eor = self.eor;
SeekOps::new(&mut self.reader).file(cdr, eor, &self.inner.options).await
}
}
impl<R> Deref for ZipArchiveReader<R> {
type Target = ZipArchiveInner;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
#[derive(Clone)]
pub struct ZipArchiveInner {
pub(crate) cdr_offsets: Vec<u64>,
pub(crate) loaded_cdrs: Vec<CDR>,
pub(crate) options: ZipOptions,
pub(crate) ceocdr: CEOCDR,
pub(crate) eor: u64,
}
impl ZipArchiveInner {
pub(crate) fn valid_offset(&self, offset: u64) -> Result<u64> {
if offset > self.eor {
return Err(ZipError::InvalidOffset(offset, self.eor));
}
Ok(offset)
}
pub fn find<'a>(&'a self, file_name: &'a [u8]) -> Result<impl Iterator<Item = usize> + 'a> {
if self.ceocdr.num_entries()? > self.options.max_cd_num_files_load {
return Err(ZipError::CDRsNotLoaded);
}
Ok(self.loaded_cdrs.iter().enumerate().filter_map(move |(i, cdr)| (cdr.insecure_file_name == file_name).then_some(i)))
}
pub fn cdrs(&self) -> &[CDR] {
&self.loaded_cdrs
}
pub fn options(&self) -> &ZipOptions {
&self.options
}
pub fn ceocdr(&self) -> &CEOCDR {
&self.ceocdr
}
}
pub struct ZipArchiveFactory<G, R> {
primary: ZipArchiveReader<R>,
generator: G,
}
impl <R: AsyncBufRead + AsyncSeek + Unpin, G: AsyncFn() -> Result<R>> ZipArchiveFactory<G, R> {
pub fn new(generator: G, primary: ZipArchiveReader<R>) -> Self{
Self { generator, primary }
}
pub fn primary(&self) -> &ZipArchiveReader<R> {
&self.primary
}
pub async fn reader(&self) -> Result<ZipArchiveReader<R>> {
let reader = (self.generator)().await?;
let inner = self.primary.inner().clone();
Ok(ZipArchiveReader { reader, inner })
}
}