Skip to main content

zip/extra_fields/
custom_extra_field.rs

1//! Custom extra field
2
3use crate::result::ZipResult;
4use crate::result::invalid;
5use std::io::Write;
6
7/// A Custom Extra Field
8#[derive(Debug, PartialEq, Eq, Clone)]
9pub struct CustomExtraField {
10    /// If true, this field will be included in the central directory entry but not the local file header.
11    pub(crate) central_only: bool,
12    /// Header ID of the extra field
13    pub header_id: u16,
14    /// Data of the extra field
15    pub data: Box<[u8]>,
16}
17
18impl CustomExtraField {
19    pub(crate) fn new(central_only: bool, header_id: u16, data: Box<[u8]>) -> Self {
20        Self {
21            central_only,
22            header_id,
23            data,
24        }
25    }
26
27    #[allow(unused)] // used for tests
28    pub(crate) fn new_from_raw(central_only: bool, data: &[u8]) -> ZipResult<Self> {
29        if data.len() < 2 {
30            return Err(invalid!("Cannot build a CustomExtraField: no header_id"));
31        }
32        if data.len() < 4 {
33            return Err(invalid!("Cannot build a CustomExtraField: no size"));
34        }
35        let header_id = u16::from_le_bytes([data[0], data[1]]);
36        let size = u16::from_le_bytes([data[2], data[3]]) as usize;
37        if size > (u16::MAX - 4) as usize {
38            return Err(invalid!("Cannot build a CustomExtraField: size too big"));
39        }
40        let data_rest = &data[4..];
41        if size != data_rest.len() {
42            return Err(invalid!("Cannot build a CustomExtraField: incorrect size"));
43        }
44        Ok(Self {
45            central_only,
46            header_id,
47            data: data_rest.to_vec().into_boxed_slice(),
48        })
49    }
50
51    pub(crate) fn full_size(&self, is_local_header: bool) -> usize {
52        if self.central_only && is_local_header {
53            return 0;
54        }
55        let size = self.data.len();
56        size_of::<u16>() + size_of::<u16>() + size
57    }
58
59    pub(crate) fn write<W: Write>(&self, write: &mut W, is_local_header: bool) -> ZipResult<()> {
60        if self.central_only && is_local_header {
61            return Ok(());
62        }
63        write.write_all(&self.header_id.to_le_bytes())?;
64        let size = self.data.len() as u16;
65        write.write_all(&size.to_le_bytes())?;
66        write.write_all(&self.data)?;
67        Ok(())
68    }
69}
70
71#[cfg(feature = "_arbitrary")]
72impl arbitrary::Arbitrary<'_> for CustomExtraField {
73    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
74        Ok(CustomExtraField {
75            central_only: u.arbitrary()?,
76            header_id: u.arbitrary()?,
77            data: u.arbitrary()?,
78        })
79    }
80}