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#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
9#[repr(u32)]
10pub enum EntryMessage {
11 SearchPruneDir = 10,
13 SearchExclude = 11,
15 SearchFail = 12,
17 ExtractBegin = 20,
19 ExtractEnd = 21,
21 ExtractFail = 22,
23 ExtractAttributes = 23,
25 ExtractXat = 24,
27 ExtractAcl = 25,
29 EncodeScanning = 30,
31 EncodeWriting = 31,
33 ConvertExclude = 40,
35 ProcessExclude = 50,
37 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#[repr(C)]
65#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
66pub struct EntryAttributes {
67 pub bits: u32,
69 pub uid: u32,
71 pub gid: u32,
73 pub flags: u32,
75 pub mode: u32,
77 pub backup_time: Timespec,
79 pub creation_time: Timespec,
81 pub modification_time: Timespec,
83}
84
85impl EntryAttributes {
86 pub const UID_BIT: u32 = 1 << 0;
88 pub const GID_BIT: u32 = 1 << 1;
90 pub const FLAGS_BIT: u32 = 1 << 2;
92 pub const MODE_BIT: u32 = 1 << 3;
94 pub const BACKUP_TIME_BIT: u32 = 1 << 4;
96 pub const CREATION_TIME_BIT: u32 = 1 << 5;
98 pub const MODIFICATION_TIME_BIT: u32 = 1 << 6;
100
101 pub const fn has_uid(self) -> bool {
103 self.bits & Self::UID_BIT != 0
104 }
105
106 pub const fn has_gid(self) -> bool {
108 self.bits & Self::GID_BIT != 0
109 }
110
111 pub const fn has_flags(self) -> bool {
113 self.bits & Self::FLAGS_BIT != 0
114 }
115
116 pub const fn has_mode(self) -> bool {
118 self.bits & Self::MODE_BIT != 0
119 }
120}
121
122#[derive(Debug)]
124pub struct PathList {
125 handle: NonNull<c_void>,
126}
127
128impl PathList {
129 pub const END_NODE: u64 = u64::MAX;
131
132 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 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 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 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 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 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}