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