Skip to main content

compression/
aa_entry_stream.rs

1use crate::{
2    aa_byte_stream::ArchiveFlags, aa_header::Timespec, ffi, util, CompressionError, Result,
3};
4use std::ffi::{c_void, CStr};
5use std::ptr::{null, NonNull};
6
7/// Wraps AppleArchive entry message identifiers.
8#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
9#[repr(u32)]
10pub enum EntryMessage {
11    /// Wraps the `SearchPruneDir` variant of `EntryMessage`.
12    SearchPruneDir = 10,
13    /// Wraps the `SearchExclude` variant of `EntryMessage`.
14    SearchExclude = 11,
15    /// Wraps the `SearchFail` variant of `EntryMessage`.
16    SearchFail = 12,
17    /// Wraps the `ExtractBegin` variant of `EntryMessage`.
18    ExtractBegin = 20,
19    /// Wraps the `ExtractEnd` variant of `EntryMessage`.
20    ExtractEnd = 21,
21    /// Wraps the `ExtractFail` variant of `EntryMessage`.
22    ExtractFail = 22,
23    /// Wraps the `ExtractAttributes` variant of `EntryMessage`.
24    ExtractAttributes = 23,
25    /// Wraps the `ExtractXat` variant of `EntryMessage`.
26    ExtractXat = 24,
27    /// Wraps the `ExtractAcl` variant of `EntryMessage`.
28    ExtractAcl = 25,
29    /// Wraps the `EncodeScanning` variant of `EntryMessage`.
30    EncodeScanning = 30,
31    /// Wraps the `EncodeWriting` variant of `EntryMessage`.
32    EncodeWriting = 31,
33    /// Wraps the `ConvertExclude` variant of `EntryMessage`.
34    ConvertExclude = 40,
35    /// Wraps the `ProcessExclude` variant of `EntryMessage`.
36    ProcessExclude = 50,
37    /// Wraps the `DecodeReading` variant of `EntryMessage`.
38    DecodeReading = 60,
39}
40
41impl EntryMessage {
42    pub(crate) const fn from_raw(raw: u32) -> Option<Self> {
43        match raw {
44            x if x == Self::SearchPruneDir as u32 => Some(Self::SearchPruneDir),
45            x if x == Self::SearchExclude as u32 => Some(Self::SearchExclude),
46            x if x == Self::SearchFail as u32 => Some(Self::SearchFail),
47            x if x == Self::ExtractBegin as u32 => Some(Self::ExtractBegin),
48            x if x == Self::ExtractEnd as u32 => Some(Self::ExtractEnd),
49            x if x == Self::ExtractFail as u32 => Some(Self::ExtractFail),
50            x if x == Self::ExtractAttributes as u32 => Some(Self::ExtractAttributes),
51            x if x == Self::ExtractXat as u32 => Some(Self::ExtractXat),
52            x if x == Self::ExtractAcl as u32 => Some(Self::ExtractAcl),
53            x if x == Self::EncodeScanning as u32 => Some(Self::EncodeScanning),
54            x if x == Self::EncodeWriting as u32 => Some(Self::EncodeWriting),
55            x if x == Self::ConvertExclude as u32 => Some(Self::ConvertExclude),
56            x if x == Self::ProcessExclude as u32 => Some(Self::ProcessExclude),
57            x if x == Self::DecodeReading as u32 => Some(Self::DecodeReading),
58            _ => None,
59        }
60    }
61}
62
63/// Wraps AppleArchive entry attribute values.
64#[repr(C)]
65#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
66pub struct EntryAttributes {
67    /// Wraps the `bits` field of `EntryAttributes`.
68    pub bits: u32,
69    /// Wraps the `uid` field of `EntryAttributes`.
70    pub uid: u32,
71    /// Wraps the `gid` field of `EntryAttributes`.
72    pub gid: u32,
73    /// Wraps the `flags` field of `EntryAttributes`.
74    pub flags: u32,
75    /// Wraps the `mode` field of `EntryAttributes`.
76    pub mode: u32,
77    /// Wraps the `backup_time` field of `EntryAttributes`.
78    pub backup_time: Timespec,
79    /// Wraps the `creation_time` field of `EntryAttributes`.
80    pub creation_time: Timespec,
81    /// Wraps the `modification_time` field of `EntryAttributes`.
82    pub modification_time: Timespec,
83}
84
85impl EntryAttributes {
86    /// Wraps the `UID_BIT` AppleArchive entry attribute bit.
87    pub const UID_BIT: u32 = 1 << 0;
88    /// Wraps the `GID_BIT` AppleArchive entry attribute bit.
89    pub const GID_BIT: u32 = 1 << 1;
90    /// Wraps the `FLAGS_BIT` AppleArchive entry attribute bit.
91    pub const FLAGS_BIT: u32 = 1 << 2;
92    /// Wraps the `MODE_BIT` AppleArchive entry attribute bit.
93    pub const MODE_BIT: u32 = 1 << 3;
94    /// Wraps the `BACKUP_TIME_BIT` AppleArchive entry attribute bit.
95    pub const BACKUP_TIME_BIT: u32 = 1 << 4;
96    /// Wraps the `CREATION_TIME_BIT` AppleArchive entry attribute bit.
97    pub const CREATION_TIME_BIT: u32 = 1 << 5;
98    /// Wraps the `MODIFICATION_TIME_BIT` AppleArchive entry attribute bit.
99    pub const MODIFICATION_TIME_BIT: u32 = 1 << 6;
100
101    /// Wraps `AAPathListCreateWithDirectoryContents`.
102    pub const fn has_uid(self) -> bool {
103        self.bits & Self::UID_BIT != 0
104    }
105
106    /// Wraps `AAPathListCreateWithDirectoryContents`.
107    pub const fn has_gid(self) -> bool {
108        self.bits & Self::GID_BIT != 0
109    }
110
111    /// Wraps `AAPathListCreateWithDirectoryContents`.
112    pub const fn has_flags(self) -> bool {
113        self.bits & Self::FLAGS_BIT != 0
114    }
115
116    /// Wraps `AAPathListCreateWithDirectoryContents`.
117    pub const fn has_mode(self) -> bool {
118        self.bits & Self::MODE_BIT != 0
119    }
120}
121
122/// Wraps an `AAPathList` handle.
123#[derive(Debug)]
124pub struct PathList {
125    handle: NonNull<c_void>,
126}
127
128impl PathList {
129    /// Wraps `END_NODE`.
130    pub const END_NODE: u64 = u64::MAX;
131
132    /// Wraps `AAPathListCreateWithDirectoryContents`.
133    pub fn from_directory_contents(
134        dir: &str,
135        path: Option<&str>,
136        flags: ArchiveFlags,
137        n_threads: i32,
138    ) -> Result<Self> {
139        let dir = util::cstring("dir", dir)?;
140        let path_cstring = path.map(|value| util::cstring("path", value)).transpose()?;
141        let handle = unsafe {
142            ffi::aa_entry_stream::compression_rs_aa_path_list_create_with_directory_contents(
143                dir.as_ptr(),
144                path_cstring.as_ref().map_or(null(), |value| value.as_ptr()),
145                flags.bits(),
146                n_threads,
147            )
148        };
149        Ok(Self {
150            handle: util::nonnull_handle(handle, "AAPathListCreateWithDirectoryContents")?,
151        })
152    }
153
154    /// Wraps `AAPathListCreateWithPath`.
155    pub fn from_path(dir: &str, path: &str) -> Result<Self> {
156        let dir = util::cstring("dir", dir)?;
157        let path = util::cstring("path", path)?;
158        let handle = unsafe {
159            ffi::aa_entry_stream::compression_rs_aa_path_list_create_with_path(
160                dir.as_ptr(),
161                path.as_ptr(),
162            )
163        };
164        Ok(Self {
165            handle: util::nonnull_handle(handle, "AAPathListCreateWithPath")?,
166        })
167    }
168
169    pub(crate) fn as_ptr(&self) -> *mut c_void {
170        self.handle.as_ptr()
171    }
172
173    /// Wraps `AAPathListNodeGetPath`.
174    pub fn first_node(&self) -> Option<u64> {
175        let node =
176            unsafe { ffi::aa_entry_stream::compression_rs_aa_path_list_node_first(self.as_ptr()) };
177        (node != Self::END_NODE).then_some(node)
178    }
179
180    /// Wraps `AAPathListNodeGetPath`.
181    pub fn next_node(&self, node: u64) -> Option<u64> {
182        let next = unsafe {
183            ffi::aa_entry_stream::compression_rs_aa_path_list_node_next(self.as_ptr(), node)
184        };
185        (next != Self::END_NODE).then_some(next)
186    }
187
188    /// Wraps `AAPathListNodeGetPath`.
189    pub fn node_path(&self, node: u64) -> Result<String> {
190        let mut length = 0_usize;
191        let status = unsafe {
192            ffi::aa_entry_stream::compression_rs_aa_path_list_node_get_path(
193                self.as_ptr(),
194                node,
195                0,
196                std::ptr::null_mut(),
197                &mut length,
198            )
199        };
200        util::status_result("AAPathListNodeGetPath", status)?;
201
202        let mut buffer = vec![0_i8; length.saturating_add(1)];
203        let status = unsafe {
204            ffi::aa_entry_stream::compression_rs_aa_path_list_node_get_path(
205                self.as_ptr(),
206                node,
207                buffer.len(),
208                buffer.as_mut_ptr(),
209                &mut length,
210            )
211        };
212        util::status_result("AAPathListNodeGetPath", status)?;
213
214        let value = unsafe { CStr::from_ptr(buffer.as_ptr()) }
215            .to_str()
216            .map_err(|_| CompressionError::Utf8Error {
217                operation: "AAPathListNodeGetPath",
218            })?;
219        Ok(value.to_string())
220    }
221
222    /// Wraps iterative `AAPathListNode*` traversal.
223    pub fn paths(&self) -> Result<Vec<String>> {
224        let mut paths = Vec::new();
225        let mut node = self.first_node();
226        while let Some(current) = node {
227            paths.push(self.node_path(current)?);
228            node = self.next_node(current);
229        }
230        Ok(paths)
231    }
232}
233
234impl Drop for PathList {
235    fn drop(&mut self) {
236        unsafe { ffi::aa_entry_stream::compression_rs_aa_path_list_release(self.as_ptr()) };
237    }
238}