zip_core/structs/
mod.rs

1//! `zip-core::structs` contains common zip records/structs enum, according to the standart file <https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT>
2//!
3//! They are optimized for Rust usage, e.g. `enums` with valid Compression
4//! Methods or structs that don't contain "length"-fields, as that can be
5//! determined via Vec::len()
6
7pub struct VersionMadeBy {
8    pub compatibility_information: CompatibleFileAttributeInformation,
9    pub version: ZipVersion,
10}
11
12/// Part of version made by
13///
14/// see [4.4.2.2](https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT)
15#[derive(Debug, Clone, PartialEq)]
16pub enum CompatibleFileAttributeInformation {
17    /// MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)
18    MsDosAndOs2,
19    Amiga,
20    OpenVms,
21    Unix,
22    VmCms,
23    AtariSt,
24    Os2,
25    Macintosh,
26    ZSystem,
27    CpM,
28    WindowsNtfs,
29    Mvs,
30    Vse,
31    AcornRisc,
32    VFat,
33    AlternateMvs,
34    BeOs,
35    Tandem,
36    Os400,
37    OsX,
38}
39
40impl TryFrom<u8> for CompatibleFileAttributeInformation {
41    type Error = u8;
42
43    /// in case of an unknown number, we return the number itself
44    /// Note: an unknown number might be a incompatiblity in the zip or just
45    /// that this crate hasn't been updated to the latest APPNOTE.TXT
46    fn try_from(value: u8) -> Result<Self, Self::Error> {
47        use CompatibleFileAttributeInformation::*;
48        match value {
49            0 => Ok(MsDosAndOs2),
50            1 => Ok(Amiga),
51            2 => Ok(OpenVms),
52            3 => Ok(Unix),
53            4 => Ok(VmCms),
54            5 => Ok(AtariSt),
55            6 => Ok(Os2),
56            7 => Ok(Macintosh),
57            8 => Ok(ZSystem),
58            9 => Ok(CpM),
59            10 => Ok(WindowsNtfs),
60            11 => Ok(Mvs),
61            12 => Ok(Vse),
62            13 => Ok(AcornRisc),
63            14 => Ok(VFat),
64            15 => Ok(AlternateMvs),
65            16 => Ok(BeOs),
66            17 => Ok(Tandem),
67            18 => Ok(Os400),
68            19 => Ok(OsX),
69            other => Err(other),
70        }
71    }
72}
73
74#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
75pub struct ZipVersion {
76    pub major_version: u8,
77    pub minor_version: u8,
78}
79
80/// stored in u8 in 4.4.2.2
81impl From<u8> for ZipVersion {
82    fn from(value: u8) -> Self {
83        let major_version = value / 10;
84        let minor_version = value.rem_euclid(10);
85        Self {
86            major_version,
87            minor_version,
88        }
89    }
90}
91
92/// stored in u18 in 4.4.3.2
93impl From<u16> for ZipVersion {
94    fn from(value: u16) -> Self {
95        let version = value.to_le_bytes();
96        Self {
97            major_version: version[0],
98            minor_version: version[1],
99        }
100    }
101}
102
103impl ZipVersion {
104    pub fn new(major_version: u8, minor_version: u8) -> Self {
105        Self {
106            major_version,
107            minor_version,
108        }
109    }
110}
111
112/// see [4.4.3.2](https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT)
113#[derive(Debug, Clone, PartialEq)]
114pub enum MinimumFeatureVersion {
115    Default,
116    VolumeLabel,
117    Folder,
118    CompressedUsingDeflate,
119    EncryptedUsingPkWare,
120    CompressedUsingDeflate64,
121    CompressedUsingPkwareDclImplode,
122    PatchDataSet,
123    UsesZip64FormatExtentions,
124    CompressedUsingBzip2,
125    EncryptedUsingDes,
126    EncryptedUsing3Des,
127    EncryptedUsingOriginalRc2,
128    EncryptedUsingRc4,
129    EncryptedUsingAes,
130    EncryptedUsingCorrectedRc2,
131    EncryptedUsingCorrectedRc2_64,
132    EncryptedUsingNonOaepKeyWrapping,
133    CentralDirectoryEncryption,
134    CompressedUsingLzma,
135    CompressedUsingPpmd,
136    EncryptedUsingBlowfish,
137    EncryptedUsingTwofish,
138}
139
140impl From<MinimumFeatureVersion> for ZipVersion {
141    fn from(value: MinimumFeatureVersion) -> Self {
142        use MinimumFeatureVersion::*;
143        match value {
144            Default => ZipVersion::new(1, 0),
145            VolumeLabel => ZipVersion::new(1, 1),
146            Folder => ZipVersion::new(2, 0),
147            CompressedUsingDeflate => ZipVersion::new(2, 0),
148            EncryptedUsingPkWare => ZipVersion::new(2, 0),
149            CompressedUsingDeflate64 => ZipVersion::new(2, 1),
150            CompressedUsingPkwareDclImplode => ZipVersion::new(2, 5),
151            PatchDataSet => ZipVersion::new(2, 7),
152            UsesZip64FormatExtentions => ZipVersion::new(4, 5),
153            CompressedUsingBzip2 => ZipVersion::new(4, 7),
154            EncryptedUsingDes => ZipVersion::new(5, 0),
155            EncryptedUsing3Des => ZipVersion::new(5, 0),
156            EncryptedUsingOriginalRc2 => ZipVersion::new(5, 0),
157            EncryptedUsingRc4 => ZipVersion::new(5, 0),
158            EncryptedUsingAes => ZipVersion::new(5, 1),
159            EncryptedUsingCorrectedRc2 => ZipVersion::new(5, 1),
160            EncryptedUsingCorrectedRc2_64 => ZipVersion::new(5, 2),
161            EncryptedUsingNonOaepKeyWrapping => ZipVersion::new(6, 1),
162            CentralDirectoryEncryption => ZipVersion::new(6, 2),
163            CompressedUsingLzma => ZipVersion::new(6, 3),
164            CompressedUsingPpmd => ZipVersion::new(6, 3),
165            EncryptedUsingBlowfish => ZipVersion::new(6, 3),
166            EncryptedUsingTwofish => ZipVersion::new(6, 3),
167        }
168    }
169}
170
171/// see [4.4.5](https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT)
172#[derive(Debug, Clone, PartialEq)]
173pub enum CompressionMethod {
174    Stored,
175    Shrunk,
176    ReducedWithFactor1,
177    ReducedWithFactor2,
178    ReducedWithFactor3,
179    ReducedWithFactor4,
180    Imploded,
181    Deflated,
182    EnhancedDeflatingUsingDeflate64,
183    PkWareDataCompressionLIbraryImploding,
184    Bzip2,
185    Lzma,
186    IbmZOsCmpsc,
187    /// Ibm Terse new, for old see PkWareDataCompressionLIbraryImploding
188    IbmTerse,
189    IbmLz77z,
190    Zstdandart,
191    Mp3,
192    Xz,
193    Jpeg,
194    WavPack,
195    PpmdV1R1,
196    Aex,
197}
198
199impl CompressionMethod {
200    pub const fn is_deprecated(&self) -> bool {
201        use CompressionMethod::*;
202        matches!(
203            self,
204            Shrunk | ReducedWithFactor1 | ReducedWithFactor2 | ReducedWithFactor3 | ReducedWithFactor4 | Imploded
205        )
206    }
207}
208
209impl TryFrom<u16> for CompressionMethod {
210    type Error = u16;
211
212    /// in case of an unknown number, we return the number itself
213    /// Note: an unknown number might be a incompatiblity in the zip or just
214    /// that this crate hasn't been updated to the latest APPNOTE.TXT
215    fn try_from(value: u16) -> Result<Self, Self::Error> {
216        use CompressionMethod::*;
217        match value {
218            0 => Ok(Stored),
219            1 => Ok(Shrunk),
220            2 => Ok(ReducedWithFactor1),
221            3 => Ok(ReducedWithFactor2),
222            4 => Ok(ReducedWithFactor3),
223            5 => Ok(ReducedWithFactor4),
224            6 => Ok(Imploded),
225            8 => Ok(Deflated),
226            9 => Ok(EnhancedDeflatingUsingDeflate64),
227            10 => Ok(PkWareDataCompressionLIbraryImploding),
228            12 => Ok(Bzip2),
229            14 => Ok(Lzma),
230            16 => Ok(IbmZOsCmpsc),
231            18 => Ok(IbmTerse),
232            19 => Ok(IbmLz77z),
233            93 => Ok(Zstdandart),
234            94 => Ok(Mp3),
235            95 => Ok(Xz),
236            96 => Ok(Jpeg),
237            97 => Ok(WavPack),
238            98 => Ok(PpmdV1R1),
239            99 => Ok(Aex),
240            other => Err(other),
241        }
242    }
243}
244
245#[cfg(test)]
246mod tests {
247    use super::*;
248
249    #[test]
250    fn order_zip_version() {
251        let v1_0 = ZipVersion::new(1, 0);
252        let v1_1 = ZipVersion::new(1, 1);
253        let v2_0 = ZipVersion::new(2, 0);
254        let v2_1 = ZipVersion::new(2, 1);
255        assert_eq!(v1_0, v1_0);
256        assert_ne!(v1_0, v1_1);
257        assert_ne!(v1_1, v2_1);
258
259        assert!(v1_1 > v1_0);
260        assert!(v2_0 > v1_0);
261        assert!(v2_0 > v1_1);
262        assert!(v2_1 > v2_0);
263        assert!(v2_0 <= v2_0);
264        assert!(v2_0 < v2_1);
265        assert!(v1_1 < v2_1);
266        assert!(v1_0 < v2_1);
267        assert_eq!(v1_0.max(v1_1), v1_1);
268    }
269
270    #[test]
271    fn parse_zip_version() {
272        assert_eq!(ZipVersion::from(30u8), ZipVersion::new(3, 0));
273        assert_eq!(ZipVersion::from(19u8), ZipVersion::new(1, 9));
274        assert_eq!(ZipVersion::from(123u8), ZipVersion::new(12, 3));
275        assert_eq!(ZipVersion::from(1u16), ZipVersion::new(1, 0));
276        assert_eq!(ZipVersion::from(258u16), ZipVersion::new(2, 1));
277        assert_eq!(ZipVersion::from(259u16), ZipVersion::new(3, 1));
278    }
279
280    #[test]
281    fn parse_compatible_file_attribute_infromation() {
282        use CompatibleFileAttributeInformation::*;
283        assert_eq!(CompatibleFileAttributeInformation::try_from(0).unwrap(), MsDosAndOs2);
284        assert_eq!(CompatibleFileAttributeInformation::try_from(19).unwrap(), OsX);
285        assert_eq!(CompatibleFileAttributeInformation::try_from(123), Err(123));
286
287        // just check it doesn't panic
288        assert!(CompatibleFileAttributeInformation::try_from(1).is_ok());
289        assert!(CompatibleFileAttributeInformation::try_from(2).is_ok());
290        assert!(CompatibleFileAttributeInformation::try_from(3).is_ok());
291        assert!(CompatibleFileAttributeInformation::try_from(4).is_ok());
292        assert!(CompatibleFileAttributeInformation::try_from(5).is_ok());
293        assert!(CompatibleFileAttributeInformation::try_from(6).is_ok());
294        assert!(CompatibleFileAttributeInformation::try_from(7).is_ok());
295        assert!(CompatibleFileAttributeInformation::try_from(8).is_ok());
296        assert!(CompatibleFileAttributeInformation::try_from(9).is_ok());
297        assert!(CompatibleFileAttributeInformation::try_from(10).is_ok());
298        assert!(CompatibleFileAttributeInformation::try_from(11).is_ok());
299        assert!(CompatibleFileAttributeInformation::try_from(12).is_ok());
300        assert!(CompatibleFileAttributeInformation::try_from(13).is_ok());
301        assert!(CompatibleFileAttributeInformation::try_from(14).is_ok());
302        assert!(CompatibleFileAttributeInformation::try_from(15).is_ok());
303        assert!(CompatibleFileAttributeInformation::try_from(16).is_ok());
304        assert!(CompatibleFileAttributeInformation::try_from(17).is_ok());
305        assert!(CompatibleFileAttributeInformation::try_from(18).is_ok());
306        assert!(CompatibleFileAttributeInformation::try_from(20).is_err());
307        assert!(CompatibleFileAttributeInformation::try_from(21).is_err());
308        assert!(CompatibleFileAttributeInformation::try_from(22).is_err());
309        assert!(CompatibleFileAttributeInformation::try_from(23).is_err());
310        assert!(CompatibleFileAttributeInformation::try_from(55).is_err());
311        assert!(CompatibleFileAttributeInformation::try_from(200).is_err());
312        assert!(CompatibleFileAttributeInformation::try_from(255).is_err());
313    }
314
315    #[test]
316    fn parse_minimum_feature_version() {
317        use MinimumFeatureVersion::*;
318        assert_eq!(ZipVersion::from(Default), ZipVersion::new(1, 0));
319        assert_eq!(ZipVersion::from(CompressedUsingDeflate64), ZipVersion::new(2, 1));
320        assert_eq!(ZipVersion::from(UsesZip64FormatExtentions), ZipVersion::new(4, 5));
321        assert_eq!(ZipVersion::from(EncryptedUsingDes), ZipVersion::new(5, 0));
322        assert_eq!(ZipVersion::from(CentralDirectoryEncryption), ZipVersion::new(6, 2));
323        assert_eq!(ZipVersion::from(CompressedUsingLzma), ZipVersion::new(6, 3));
324    }
325
326    #[test]
327    fn parse_compression_method() {
328        use CompressionMethod::*;
329        assert_eq!(CompressionMethod::try_from(0).unwrap(), Stored);
330        assert_eq!(CompressionMethod::try_from(9).unwrap(), EnhancedDeflatingUsingDeflate64);
331        assert_eq!(CompressionMethod::try_from(15), Err(15));
332        assert_eq!(CompressionMethod::try_from(93).unwrap(), Zstdandart);
333        assert_eq!(CompressionMethod::try_from(200), Err(200));
334
335        // just check it doesn't panic
336        assert!(CompressionMethod::try_from(1).is_ok());
337        assert!(CompressionMethod::try_from(2).is_ok());
338        assert!(CompressionMethod::try_from(3).is_ok());
339        assert!(CompressionMethod::try_from(4).is_ok());
340        assert!(CompressionMethod::try_from(5).is_ok());
341        assert!(CompressionMethod::try_from(6).is_ok());
342        assert!(CompressionMethod::try_from(7).is_err());
343        assert!(CompressionMethod::try_from(8).is_ok());
344        assert!(CompressionMethod::try_from(9).is_ok());
345        assert!(CompressionMethod::try_from(10).is_ok());
346        assert!(CompressionMethod::try_from(11).is_err());
347        assert!(CompressionMethod::try_from(12).is_ok());
348        assert!(CompressionMethod::try_from(13).is_err());
349        assert!(CompressionMethod::try_from(14).is_ok());
350        assert!(CompressionMethod::try_from(16).is_ok());
351        assert!(CompressionMethod::try_from(17).is_err());
352        assert!(CompressionMethod::try_from(18).is_ok());
353        assert!(CompressionMethod::try_from(19).is_ok());
354        assert!(CompressionMethod::try_from(20).is_err());
355        assert!(CompressionMethod::try_from(21).is_err());
356        assert!(CompressionMethod::try_from(22).is_err());
357        assert!(CompressionMethod::try_from(92).is_err());
358        assert!(CompressionMethod::try_from(93).is_ok());
359        assert!(CompressionMethod::try_from(94).is_ok());
360        assert!(CompressionMethod::try_from(95).is_ok());
361        assert!(CompressionMethod::try_from(96).is_ok());
362        assert!(CompressionMethod::try_from(97).is_ok());
363        assert!(CompressionMethod::try_from(98).is_ok());
364        assert!(CompressionMethod::try_from(99).is_ok());
365        assert!(CompressionMethod::try_from(100).is_err());
366        assert!(CompressionMethod::try_from(200).is_err());
367        assert!(CompressionMethod::try_from(255).is_err());
368
369        assert!(!Stored.is_deprecated());
370        assert!(ReducedWithFactor3.is_deprecated());
371    }
372}