1pub mod builder;
5
6use std::ops::Deref;
7
8use futures_lite::io::{AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt, SeekFrom};
9
10use crate::entry::builder::ZipEntryBuilder;
11use crate::error::{Result, ZipError};
12use crate::spec::{
13 attribute::AttributeCompatibility,
14 consts::LFH_SIGNATURE,
15 header::{ExtraField, LocalFileHeader},
16 Compression,
17};
18use crate::{string::ZipString, ZipDateTime};
19
20#[derive(Clone, Debug)]
26pub struct ZipEntry {
27 pub(crate) filename: ZipString,
28 pub(crate) compression: Compression,
29 #[cfg(any(
30 feature = "deflate",
31 feature = "bzip2",
32 feature = "zstd",
33 feature = "lzma",
34 feature = "xz",
35 feature = "deflate64"
36 ))]
37 pub(crate) compression_level: async_compression::Level,
38 pub(crate) crc32: u32,
39 pub(crate) uncompressed_size: u64,
40 pub(crate) compressed_size: u64,
41 pub(crate) attribute_compatibility: AttributeCompatibility,
42 pub(crate) last_modification_date: ZipDateTime,
43 pub(crate) internal_file_attribute: u16,
44 pub(crate) external_file_attribute: u32,
45 pub(crate) extra_fields: Vec<ExtraField>,
46 pub(crate) comment: ZipString,
47 pub(crate) data_descriptor: bool,
48 pub(crate) file_offset: u64,
49}
50
51impl From<ZipEntryBuilder> for ZipEntry {
52 fn from(builder: ZipEntryBuilder) -> Self {
53 builder.0
54 }
55}
56
57impl ZipEntry {
58 pub(crate) fn new(filename: ZipString, compression: Compression) -> Self {
59 ZipEntry {
60 filename,
61 compression,
62 #[cfg(any(
63 feature = "deflate",
64 feature = "bzip2",
65 feature = "zstd",
66 feature = "lzma",
67 feature = "xz",
68 feature = "deflate64"
69 ))]
70 compression_level: async_compression::Level::Default,
71 crc32: 0,
72 uncompressed_size: 0,
73 compressed_size: 0,
74 attribute_compatibility: AttributeCompatibility::Unix,
75 last_modification_date: ZipDateTime::default_for_write(),
76 internal_file_attribute: 0,
77 external_file_attribute: 0,
78 extra_fields: Vec::new(),
79 comment: String::new().into(),
80 data_descriptor: false,
81 file_offset: 0,
82 }
83 }
84
85 pub fn filename(&self) -> &ZipString {
92 &self.filename
93 }
94
95 pub fn compression(&self) -> Compression {
97 self.compression
98 }
99
100 pub fn crc32(&self) -> u32 {
102 self.crc32
103 }
104
105 pub fn uncompressed_size(&self) -> u64 {
107 self.uncompressed_size
108 }
109
110 pub fn compressed_size(&self) -> u64 {
112 self.compressed_size
113 }
114
115 pub fn attribute_compatibility(&self) -> AttributeCompatibility {
117 self.attribute_compatibility
118 }
119
120 pub fn last_modification_date(&self) -> &ZipDateTime {
122 &self.last_modification_date
123 }
124
125 pub fn internal_file_attribute(&self) -> u16 {
127 self.internal_file_attribute
128 }
129
130 pub fn external_file_attribute(&self) -> u32 {
132 self.external_file_attribute
133 }
134
135 pub fn extra_fields(&self) -> &[ExtraField] {
137 &self.extra_fields
138 }
139
140 pub fn comment(&self) -> &ZipString {
142 &self.comment
143 }
144
145 pub fn unix_permissions(&self) -> Option<u16> {
150 if !matches!(self.attribute_compatibility, AttributeCompatibility::Unix) {
151 return None;
152 }
153
154 Some(((self.external_file_attribute) >> 16) as u16)
155 }
156
157 pub fn dir(&self) -> Result<bool> {
159 Ok(self.filename.as_str()?.ends_with('/'))
160 }
161
162 pub fn data_descriptor(&self) -> bool {
164 self.data_descriptor
165 }
166
167 pub fn file_offset(&self) -> u64 {
169 self.file_offset
170 }
171}
172
173#[derive(Clone)]
178pub struct StoredZipEntry {
179 pub(crate) entry: ZipEntry,
180 pub(crate) file_offset: u64,
182 pub(crate) header_size: u64,
183}
184
185impl StoredZipEntry {
186 pub fn header_offset(&self) -> u64 {
188 self.file_offset
189 }
190
191 pub fn header_size(&self) -> u64 {
196 self.header_size
197 }
198
199 pub(crate) async fn seek_to_data_offset<R: AsyncRead + AsyncSeek + Unpin>(&self, mut reader: &mut R) -> Result<()> {
201 reader.seek(SeekFrom::Start(self.file_offset)).await?;
203
204 let signature = {
206 let mut buffer = [0; 4];
207 reader.read_exact(&mut buffer).await?;
208 u32::from_le_bytes(buffer)
209 };
210
211 match signature {
212 LFH_SIGNATURE => (),
213 actual => return Err(ZipError::UnexpectedHeaderError(actual, LFH_SIGNATURE)),
214 };
215
216 let header = LocalFileHeader::from_reader(&mut reader).await?;
218 let trailing_size = (header.file_name_length as i64) + (header.extra_field_length as i64);
219 reader.seek(SeekFrom::Current(trailing_size)).await?;
220
221 Ok(())
222 }
223}
224
225impl Deref for StoredZipEntry {
226 type Target = ZipEntry;
227
228 fn deref(&self) -> &Self::Target {
229 &self.entry
230 }
231}