use crate::base::read::io::{compressed::CompressedReader, hashed::HashedReader, owned::OwnedReader};
use crate::entry::ZipEntry;
use crate::error::{Result, ZipError};
use crate::spec::Compression;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_lite::io::{AsyncBufRead, AsyncRead, AsyncReadExt, Take};
use pin_project::pin_project;
pub struct WithEntry<'a>(OwnedEntry<'a>);
pub struct WithoutEntry;
#[pin_project]
pub struct ZipEntryReader<'a, R, E> {
#[pin]
reader: HashedReader<CompressedReader<Take<OwnedReader<'a, R>>>>,
entry: E,
}
impl<'a, R> ZipEntryReader<'a, R, WithoutEntry>
where
R: AsyncBufRead + Unpin,
{
pub(crate) fn new_with_owned(reader: R, compression: Compression, size: u64) -> Self {
let reader = HashedReader::new(CompressedReader::new(OwnedReader::Owned(reader).take(size), compression));
Self { reader, entry: WithoutEntry }
}
pub(crate) fn new_with_borrow(reader: &'a mut R, compression: Compression, size: u64) -> Self {
let reader = HashedReader::new(CompressedReader::new(OwnedReader::Borrow(reader).take(size), compression));
Self { reader, entry: WithoutEntry }
}
pub(crate) fn into_with_entry(self, entry: &'a ZipEntry) -> ZipEntryReader<'a, R, WithEntry<'a>> {
ZipEntryReader { reader: self.reader, entry: WithEntry(OwnedEntry::Borrow(entry)) }
}
pub(crate) fn into_with_entry_owned(self, entry: ZipEntry) -> ZipEntryReader<'a, R, WithEntry<'a>> {
ZipEntryReader { reader: self.reader, entry: WithEntry(OwnedEntry::Owned(entry)) }
}
}
impl<'a, R, E> AsyncRead for ZipEntryReader<'a, R, E>
where
R: AsyncBufRead + Unpin,
{
fn poll_read(self: Pin<&mut Self>, c: &mut Context<'_>, b: &mut [u8]) -> Poll<std::io::Result<usize>> {
self.project().reader.poll_read(c, b)
}
}
impl<'a, R, E> ZipEntryReader<'a, R, E>
where
R: AsyncBufRead + Unpin,
{
pub fn compute_hash(&mut self) -> u32 {
self.reader.swap_and_compute_hash()
}
pub(crate) fn into_inner(self) -> R {
self.reader.into_inner().into_inner().into_inner().owned_into_inner()
}
}
impl<R> ZipEntryReader<'_, R, WithEntry<'_>>
where
R: AsyncBufRead + Unpin,
{
pub fn entry(&self) -> &'_ ZipEntry {
self.entry.0.entry()
}
pub async fn read_to_end_checked(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
let read = self.read_to_end(buf).await?;
if self.compute_hash() == self.entry.0.entry().crc32() {
Ok(read)
} else {
Err(ZipError::CRC32CheckError)
}
}
pub async fn read_to_string_checked(&mut self, buf: &mut String) -> Result<usize> {
let read = self.read_to_string(buf).await?;
if self.compute_hash() == self.entry.0.entry().crc32() {
Ok(read)
} else {
Err(ZipError::CRC32CheckError)
}
}
}
enum OwnedEntry<'a> {
Owned(ZipEntry),
Borrow(&'a ZipEntry),
}
impl<'a> OwnedEntry<'a> {
pub fn entry(&self) -> &'_ ZipEntry {
match self {
OwnedEntry::Owned(entry) => entry,
OwnedEntry::Borrow(entry) => entry,
}
}
}