1use std::ops::Deref;
5
6use futures_lite::io::{AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt, SeekFrom};
7
8use crate::error::{Result, ZipError};
9use crate::spec::{
10 attribute::AttributeCompatibility,
11 consts::LFH_SIGNATURE,
12 header::{ExtraField, LocalFileHeader},
13 Compression,
14};
15use crate::{string::ZipString, ZipDateTime};
16
17#[derive(Clone, Debug)]
19pub struct ZipEntry {
20 pub(crate) filename: ZipString,
21 pub(crate) compression: Compression,
22 #[cfg(any(
23 feature = "deflate",
24 feature = "bzip2",
25 feature = "zstd",
26 feature = "lzma",
27 feature = "xz",
28 feature = "deflate64"
29 ))]
30 pub(crate) compression_level: async_compression::Level,
31 pub(crate) crc32: u32,
32 pub(crate) uncompressed_size: u64,
33 pub(crate) compressed_size: u64,
34 pub(crate) attribute_compatibility: AttributeCompatibility,
35 pub(crate) last_modification_date: ZipDateTime,
36 pub(crate) internal_file_attribute: u16,
37 pub(crate) external_file_attribute: u32,
38 pub(crate) extra_fields: Vec<ExtraField>,
39 pub(crate) comment: ZipString,
40 pub(crate) data_descriptor: bool,
41 pub(crate) file_offset: u64,
42}
43
44impl ZipEntry {
45 pub fn filename(&self) -> &ZipString {
52 &self.filename
53 }
54
55 pub fn compression(&self) -> Compression {
57 self.compression
58 }
59
60 pub fn crc32(&self) -> u32 {
62 self.crc32
63 }
64
65 pub fn uncompressed_size(&self) -> u64 {
67 self.uncompressed_size
68 }
69
70 pub fn compressed_size(&self) -> u64 {
72 self.compressed_size
73 }
74
75 pub fn attribute_compatibility(&self) -> AttributeCompatibility {
77 self.attribute_compatibility
78 }
79
80 pub fn last_modification_date(&self) -> &ZipDateTime {
82 &self.last_modification_date
83 }
84
85 pub fn internal_file_attribute(&self) -> u16 {
87 self.internal_file_attribute
88 }
89
90 pub fn external_file_attribute(&self) -> u32 {
92 self.external_file_attribute
93 }
94
95 pub fn extra_fields(&self) -> &[ExtraField] {
97 &self.extra_fields
98 }
99
100 pub fn comment(&self) -> &ZipString {
102 &self.comment
103 }
104
105 pub fn unix_permissions(&self) -> Option<u16> {
110 if !matches!(self.attribute_compatibility, AttributeCompatibility::Unix) {
111 return None;
112 }
113
114 Some(((self.external_file_attribute) >> 16) as u16)
115 }
116
117 pub fn dir(&self) -> Result<bool> {
119 Ok(self.filename.as_str()?.ends_with('/'))
120 }
121
122 pub fn data_descriptor(&self) -> bool {
124 self.data_descriptor
125 }
126
127 pub fn file_offset(&self) -> u64 {
129 self.file_offset
130 }
131}
132
133#[derive(Clone)]
138pub struct StoredZipEntry {
139 pub(crate) entry: ZipEntry,
140 pub(crate) file_offset: u64,
142 pub(crate) header_size: u64,
143}
144
145impl StoredZipEntry {
146 pub fn header_offset(&self) -> u64 {
148 self.file_offset
149 }
150
151 pub fn header_size(&self) -> u64 {
156 self.header_size
157 }
158
159 pub(crate) async fn seek_to_data_offset<R: AsyncRead + AsyncSeek + Unpin>(&self, mut reader: &mut R) -> Result<()> {
161 reader.seek(SeekFrom::Start(self.file_offset)).await?;
163
164 let signature = {
166 let mut buffer = [0; 4];
167 reader.read_exact(&mut buffer).await?;
168 u32::from_le_bytes(buffer)
169 };
170
171 match signature {
172 LFH_SIGNATURE => (),
173 actual => return Err(ZipError::UnexpectedHeaderError(actual, LFH_SIGNATURE)),
174 };
175
176 let header = LocalFileHeader::from_reader(&mut reader).await?;
178 let trailing_size = (header.file_name_length as i64) + (header.extra_field_length as i64);
179 reader.seek(SeekFrom::Current(trailing_size)).await?;
180
181 Ok(())
182 }
183}
184
185impl Deref for StoredZipEntry {
186 type Target = ZipEntry;
187
188 fn deref(&self) -> &Self::Target {
189 &self.entry
190 }
191}