async_zip/base/read/
seek.rs

1// Copyright (c) 2022 Harry [Majored] [hello@majored.pw]
2// MIT License (https://github.com/Majored/rs-async-zip/blob/main/LICENSE)
3
4//! A ZIP reader which acts over a seekable source.
5//!
6//! ### Example
7//! ```no_run
8//! # use async_zip::base::read::seek::ZipFileReader;
9//! # use async_zip::error::Result;
10//! # use futures_lite::io::AsyncReadExt;
11//! # use tokio::fs::File;
12//! # use tokio_util::compat::TokioAsyncReadCompatExt;
13//! # use tokio::io::BufReader;
14//! #
15//! async fn run() -> Result<()> {
16//!     let mut data = BufReader::new(File::open("./foo.zip").await?);
17//!     let mut reader = ZipFileReader::new(data.compat()).await?;
18//!
19//!     let mut data = Vec::new();
20//!     let mut entry = reader.reader_without_entry(0).await?;
21//!     entry.read_to_end(&mut data).await?;
22//!
23//!     // Use data within current scope.
24//!
25//!     Ok(())
26//! }
27//! ```
28
29use crate::base::read::io::entry::ZipEntryReader;
30use crate::error::{Result, ZipError};
31use crate::file::ZipFile;
32
33#[cfg(feature = "tokio")]
34use crate::tokio::read::seek::ZipFileReader as TokioZipFileReader;
35
36use futures_lite::io::{AsyncBufRead, AsyncSeek};
37
38#[cfg(feature = "tokio")]
39use tokio_util::compat::{Compat, TokioAsyncReadCompatExt};
40
41use super::io::entry::{WithEntry, WithoutEntry};
42
43/// A ZIP reader which acts over a seekable source.
44#[derive(Clone)]
45pub struct ZipFileReader<R> {
46    reader: R,
47    file: ZipFile,
48}
49
50impl<R> ZipFileReader<R>
51where
52    R: AsyncBufRead + AsyncSeek + Unpin,
53{
54    /// Constructs a new ZIP reader from a seekable source.
55    pub async fn new(mut reader: R) -> Result<ZipFileReader<R>> {
56        let file = crate::base::read::file(&mut reader).await?;
57        Ok(ZipFileReader::from_raw_parts(reader, file))
58    }
59
60    /// Constructs a ZIP reader from a seekable source and ZIP file information derived from that source.
61    ///
62    /// Providing a [`ZipFile`] that wasn't derived from that source may lead to inaccurate parsing.
63    pub fn from_raw_parts(reader: R, file: ZipFile) -> ZipFileReader<R> {
64        ZipFileReader { reader, file }
65    }
66
67    /// Returns this ZIP file's information.
68    pub fn file(&self) -> &ZipFile {
69        &self.file
70    }
71
72    /// Returns a mutable reference to the inner seekable source.
73    ///
74    /// Swapping the source (eg. via std::mem operations) may lead to inaccurate parsing.
75    pub fn inner_mut(&mut self) -> &mut R {
76        &mut self.reader
77    }
78
79    /// Returns the inner seekable source by consuming self.
80    pub fn into_inner(self) -> R {
81        self.reader
82    }
83
84    /// Returns a new entry reader if the provided index is valid.
85    pub async fn reader_without_entry(&mut self, index: usize) -> Result<ZipEntryReader<'_, R, WithoutEntry>> {
86        let stored_entry = self.file.entries.get(index).ok_or(ZipError::EntryIndexOutOfBounds)?;
87        stored_entry.seek_to_data_offset(&mut self.reader).await?;
88
89        Ok(ZipEntryReader::new_with_borrow(
90            &mut self.reader,
91            stored_entry.entry.compression(),
92            stored_entry.entry.compressed_size(),
93        ))
94    }
95
96    /// Returns a new entry reader if the provided index is valid.
97    pub async fn reader_with_entry(&mut self, index: usize) -> Result<ZipEntryReader<'_, R, WithEntry<'_>>> {
98        let stored_entry = self.file.entries.get(index).ok_or(ZipError::EntryIndexOutOfBounds)?;
99
100        stored_entry.seek_to_data_offset(&mut self.reader).await?;
101
102        let reader = ZipEntryReader::new_with_borrow(
103            &mut self.reader,
104            stored_entry.entry.compression(),
105            stored_entry.entry.compressed_size(),
106        );
107
108        Ok(reader.into_with_entry(stored_entry))
109    }
110
111    /// Returns a new entry reader if the provided index is valid.
112    /// Consumes self
113    pub async fn into_entry<'a>(mut self, index: usize) -> Result<ZipEntryReader<'a, R, WithoutEntry>>
114    where
115        R: 'a,
116    {
117        let stored_entry = self.file.entries.get(index).ok_or(ZipError::EntryIndexOutOfBounds)?;
118
119        stored_entry.seek_to_data_offset(&mut self.reader).await?;
120
121        Ok(ZipEntryReader::new_with_owned(
122            self.reader,
123            stored_entry.entry.compression(),
124            stored_entry.entry.compressed_size(),
125        ))
126    }
127}
128
129#[cfg(feature = "tokio")]
130impl<R> ZipFileReader<Compat<R>>
131where
132    R: tokio::io::AsyncBufRead + tokio::io::AsyncSeek + Unpin,
133{
134    /// Constructs a new tokio-specific ZIP reader from a seekable source.
135    pub async fn with_tokio(reader: R) -> Result<TokioZipFileReader<R>> {
136        let mut reader = reader.compat();
137        let file = crate::base::read::file(&mut reader).await?;
138        Ok(ZipFileReader::from_raw_parts(reader, file))
139    }
140}