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