Skip to main content

async_zip/base/read/io/
entry.rs

1// Copyright (c) 2022 Harry [Majored] [hello@majored.pw]
2// MIT License (https://github.com/Majored/rs-async-zip/blob/main/LICENSE)
3
4use crate::base::read::counting::Counting;
5use crate::base::read::io::{compressed::CompressedReader, hashed::HashedReader, owned::OwnedReader};
6use crate::entry::ZipEntry;
7use crate::error::{Result, ZipError};
8use crate::spec::Compression;
9
10use std::pin::Pin;
11use std::task::{Context, Poll};
12
13use futures_lite::io::{AsyncBufRead, AsyncRead, AsyncReadExt, Take};
14use pin_project::pin_project;
15
16/// A type which encodes that [`ZipEntryReader`] has associated entry data.
17pub struct WithEntry<'a>(OwnedEntry<'a>);
18
19/// A type which encodes that [`ZipEntryReader`] has no associated entry data.
20pub struct WithoutEntry;
21
22/// A ZIP entry reader which may implement decompression.
23#[pin_project]
24pub struct ZipEntryReader<'a, R, E> {
25    #[pin]
26    reader: HashedReader<CompressedReader<Counting<Take<OwnedReader<'a, R>>>>>,
27    entry: E,
28}
29
30impl<'a, R> ZipEntryReader<'a, R, WithoutEntry>
31where
32    R: AsyncBufRead + Unpin,
33{
34    /// Constructs a new entry reader from its required parameters (incl. an owned R).
35    pub(crate) fn new_with_owned(reader: R, compression: Compression, size: u64) -> Result<Self> {
36        let reader = HashedReader::new(CompressedReader::new(
37            Counting::new(OwnedReader::Owned(reader).take(size)),
38            compression,
39        )?);
40        Ok(Self { reader, entry: WithoutEntry })
41    }
42
43    /// Constructs a new entry reader from its required parameters (incl. a mutable borrow of an R).
44    pub(crate) fn new_with_borrow(reader: &'a mut R, compression: Compression, size: u64) -> Result<Self> {
45        let reader = HashedReader::new(CompressedReader::new(
46            Counting::new(OwnedReader::Borrow(reader).take(size)),
47            compression,
48        )?);
49        Ok(Self { reader, entry: WithoutEntry })
50    }
51
52    pub(crate) fn into_with_entry(self, entry: &'a ZipEntry) -> ZipEntryReader<'a, R, WithEntry<'a>> {
53        ZipEntryReader { reader: self.reader, entry: WithEntry(OwnedEntry::Borrow(entry)) }
54    }
55
56    pub(crate) fn into_with_entry_owned(self, entry: ZipEntry) -> ZipEntryReader<'a, R, WithEntry<'a>> {
57        ZipEntryReader { reader: self.reader, entry: WithEntry(OwnedEntry::Owned(entry)) }
58    }
59}
60
61impl<'a, R, E> AsyncRead for ZipEntryReader<'a, R, E>
62where
63    R: AsyncBufRead + Unpin,
64{
65    fn poll_read(self: Pin<&mut Self>, c: &mut Context<'_>, b: &mut [u8]) -> Poll<std::io::Result<usize>> {
66        self.project().reader.poll_read(c, b)
67    }
68}
69
70impl<'a, R, E> ZipEntryReader<'a, R, E>
71where
72    R: AsyncBufRead + Unpin,
73{
74    /// Computes and returns the CRC32 hash of bytes read by this reader so far.
75    ///
76    /// This hash should only be computed once EOF has been reached.
77    pub fn compute_hash(&mut self) -> u32 {
78        self.reader.swap_and_compute_hash()
79    }
80
81    /// Return the number of bytes read so far by this reader.
82    pub fn bytes_read(&self) -> u64 {
83        self.reader.inner().inner().bytes_read()
84    }
85
86    /// Consumes this reader and returns the inner value.
87    pub(crate) fn into_inner(self) -> R {
88        self.reader.into_inner().into_inner().into_inner().into_inner().owned_into_inner()
89    }
90}
91
92impl<R> ZipEntryReader<'_, R, WithEntry<'_>>
93where
94    R: AsyncBufRead + Unpin,
95{
96    /// Returns an immutable reference to the associated entry data.
97    pub fn entry(&self) -> &'_ ZipEntry {
98        self.entry.0.entry()
99    }
100
101    /// Reads all bytes until EOF has been reached, appending them to buf, and verifies the CRC32 values.
102    ///
103    /// This is a helper function synonymous to [`AsyncReadExt::read_to_end()`].
104    pub async fn read_to_end_checked(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
105        let read = self.read_to_end(buf).await?;
106
107        if self.compute_hash() == self.entry.0.entry().crc32() {
108            Ok(read)
109        } else {
110            Err(ZipError::CRC32CheckError)
111        }
112    }
113
114    /// Reads all bytes until EOF has been reached, placing them into buf, and verifies the CRC32 values.
115    ///
116    /// This is a helper function synonymous to [`AsyncReadExt::read_to_string()`].
117    pub async fn read_to_string_checked(&mut self, buf: &mut String) -> Result<usize> {
118        let read = self.read_to_string(buf).await?;
119
120        if self.compute_hash() == self.entry.0.entry().crc32() {
121            Ok(read)
122        } else {
123            Err(ZipError::CRC32CheckError)
124        }
125    }
126}
127
128enum OwnedEntry<'a> {
129    Owned(ZipEntry),
130    Borrow(&'a ZipEntry),
131}
132
133impl<'a> OwnedEntry<'a> {
134    pub fn entry(&self) -> &'_ ZipEntry {
135        match self {
136            OwnedEntry::Owned(entry) => entry,
137            OwnedEntry::Borrow(entry) => entry,
138        }
139    }
140}