Skip to main content

compression/
aa_field_key.rs

1use crate::{ffi, util, CompressionError, Result};
2use std::ffi::{c_void, CStr};
3use std::fmt;
4use std::ptr::NonNull;
5
6/// Wraps an `AAFieldKey` value.
7#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
8pub struct FieldKey(u32);
9
10fn parse_field_key(value: &str) -> Result<FieldKey> {
11    if value.len() != 3 || !value.bytes().all(|byte| byte.is_ascii() && byte != 0) {
12        return Err(CompressionError::InvalidFieldKey {
13            key: value.to_string(),
14        });
15    }
16
17    let upper = value.to_ascii_uppercase();
18    let bytes = upper.as_bytes();
19    Ok(FieldKey::from_bytes([bytes[0], bytes[1], bytes[2]]))
20}
21
22impl FieldKey {
23    /// Wraps the `ACL` AppleArchive field key.
24    pub const ACL: Self = Self::from_bytes(*b"ACL");
25    /// Wraps the `BTM` AppleArchive field key.
26    pub const BTM: Self = Self::from_bytes(*b"BTM");
27    /// Wraps the `CKS` AppleArchive field key.
28    pub const CKS: Self = Self::from_bytes(*b"CKS");
29    /// Wraps the `CLC` AppleArchive field key.
30    pub const CLC: Self = Self::from_bytes(*b"CLC");
31    /// Wraps the `CTM` AppleArchive field key.
32    pub const CTM: Self = Self::from_bytes(*b"CTM");
33    /// Wraps the `DAT` AppleArchive field key.
34    pub const DAT: Self = Self::from_bytes(*b"DAT");
35    /// Wraps the `DEV` AppleArchive field key.
36    pub const DEV: Self = Self::from_bytes(*b"DEV");
37    /// Wraps the `DE2` AppleArchive field key.
38    pub const DE2: Self = Self::from_bytes(*b"DE2");
39    /// Wraps the `DUZ` AppleArchive field key.
40    pub const DUZ: Self = Self::from_bytes(*b"DUZ");
41    /// Wraps the `FLG` AppleArchive field key.
42    pub const FLG: Self = Self::from_bytes(*b"FLG");
43    /// Wraps the `GID` AppleArchive field key.
44    pub const GID: Self = Self::from_bytes(*b"GID");
45    /// Wraps the `GIN` AppleArchive field key.
46    pub const GIN: Self = Self::from_bytes(*b"GIN");
47    /// Wraps the `HLC` AppleArchive field key.
48    pub const HLC: Self = Self::from_bytes(*b"HLC");
49    /// Wraps the `IDX` AppleArchive field key.
50    pub const IDX: Self = Self::from_bytes(*b"IDX");
51    /// Wraps the `IDZ` AppleArchive field key.
52    pub const IDZ: Self = Self::from_bytes(*b"IDZ");
53    /// Wraps the `INO` AppleArchive field key.
54    pub const INO: Self = Self::from_bytes(*b"INO");
55    /// Wraps the `LNK` AppleArchive field key.
56    pub const LNK: Self = Self::from_bytes(*b"LNK");
57    /// Wraps the `MOD` AppleArchive field key.
58    pub const MOD: Self = Self::from_bytes(*b"MOD");
59    /// Wraps the `MTM` AppleArchive field key.
60    pub const MTM: Self = Self::from_bytes(*b"MTM");
61    /// Wraps the `NLK` AppleArchive field key.
62    pub const NLK: Self = Self::from_bytes(*b"NLK");
63    /// Wraps the `PAT` AppleArchive field key.
64    pub const PAT: Self = Self::from_bytes(*b"PAT");
65    /// Wraps the `SH1` AppleArchive field key.
66    pub const SH1: Self = Self::from_bytes(*b"SH1");
67    /// Wraps the `SH2` AppleArchive field key.
68    pub const SH2: Self = Self::from_bytes(*b"SH2");
69    /// Wraps the `SH3` AppleArchive field key.
70    pub const SH3: Self = Self::from_bytes(*b"SH3");
71    /// Wraps the `SH5` AppleArchive field key.
72    pub const SH5: Self = Self::from_bytes(*b"SH5");
73    /// Wraps the `SIZ` AppleArchive field key.
74    pub const SIZ: Self = Self::from_bytes(*b"SIZ");
75    /// Wraps the `SLC` AppleArchive field key.
76    pub const SLC: Self = Self::from_bytes(*b"SLC");
77    /// Wraps the `TYP` AppleArchive field key.
78    pub const TYP: Self = Self::from_bytes(*b"TYP");
79    /// Wraps the `UID` AppleArchive field key.
80    pub const UID: Self = Self::from_bytes(*b"UID");
81    /// Wraps the `UIN` AppleArchive field key.
82    pub const UIN: Self = Self::from_bytes(*b"UIN");
83    /// Wraps the `XAT` AppleArchive field key.
84    pub const XAT: Self = Self::from_bytes(*b"XAT");
85    /// Wraps the `YAF` AppleArchive field key.
86    pub const YAF: Self = Self::from_bytes(*b"YAF");
87
88    /// Wraps three-byte `AAFieldKey` construction.
89    pub const fn from_bytes(bytes: [u8; 3]) -> Self {
90        Self(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], 0]))
91    }
92
93    /// Wraps string parsing for `AAFieldKey` values.
94    pub fn parse(value: &str) -> Result<Self> {
95        parse_field_key(value)
96    }
97
98    pub(crate) const fn from_raw(raw: u32) -> Self {
99        Self(raw)
100    }
101
102    /// Wraps raw `AAFieldKey` values.
103    pub const fn raw(self) -> u32 {
104        self.0
105    }
106
107    /// Wraps three-byte `AAFieldKey` values.
108    pub const fn as_bytes(self) -> [u8; 3] {
109        let [a, b, c, _] = self.0.to_le_bytes();
110        [a, b, c]
111    }
112
113    /// Wraps string conversion for `AAFieldKey` values.
114    pub fn as_string(self) -> String {
115        String::from_utf8_lossy(&self.as_bytes()).into_owned()
116    }
117}
118
119impl fmt::Debug for FieldKey {
120    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121        write!(f, "FieldKey({self})")
122    }
123}
124
125impl std::str::FromStr for FieldKey {
126    type Err = CompressionError;
127
128    fn from_str(value: &str) -> Result<Self> {
129        parse_field_key(value)
130    }
131}
132
133impl fmt::Display for FieldKey {
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135        f.write_str(&self.as_string())
136    }
137}
138
139impl TryFrom<&str> for FieldKey {
140    type Error = CompressionError;
141
142    fn try_from(value: &str) -> Result<Self> {
143        parse_field_key(value)
144    }
145}
146
147/// Wraps an `AAFieldKeySet` handle.
148#[derive(Debug)]
149pub struct FieldKeySet {
150    handle: NonNull<c_void>,
151}
152
153impl FieldKeySet {
154    /// Wraps `AAFieldKeySetCreate`.
155    pub fn new() -> Result<Self> {
156        let handle = unsafe { ffi::aa_field_key::compression_rs_aa_field_key_set_create() };
157        Ok(Self {
158            handle: util::nonnull_handle(handle, "AAFieldKeySetCreate")?,
159        })
160    }
161
162    /// Wraps `AAFieldKeySetCreateWithString`.
163    pub fn from_csv(value: &str) -> Result<Self> {
164        let value = util::cstring("value", value)?;
165        let handle = unsafe {
166            ffi::aa_field_key::compression_rs_aa_field_key_set_create_with_string(value.as_ptr())
167        };
168        Ok(Self {
169            handle: util::nonnull_handle(handle, "AAFieldKeySetCreateWithString")?,
170        })
171    }
172
173    pub(crate) fn as_ptr(&self) -> *mut c_void {
174        self.handle.as_ptr()
175    }
176
177    /// Wraps `AAFieldKeySetClear`.
178    pub fn clear(&mut self) -> Result<()> {
179        let status =
180            unsafe { ffi::aa_field_key::compression_rs_aa_field_key_set_clear(self.as_ptr()) };
181        util::status_result("AAFieldKeySetClear", status)
182    }
183
184    /// Wraps `AAFieldKeySetContainsKey`.
185    pub fn contains(&self, key: FieldKey) -> Result<bool> {
186        match unsafe {
187            ffi::aa_field_key::compression_rs_aa_field_key_set_contains_key(
188                self.as_ptr(),
189                key.raw(),
190            )
191        } {
192            value if value < 0 => Err(CompressionError::OperationFailed {
193                operation: "AAFieldKeySetContainsKey",
194                code: value,
195            }),
196            0 => Ok(false),
197            _ => Ok(true),
198        }
199    }
200
201    /// Wraps `AAFieldKeySetInsertKey`.
202    pub fn insert(&mut self, key: FieldKey) -> Result<()> {
203        let status = unsafe {
204            ffi::aa_field_key::compression_rs_aa_field_key_set_insert_key(self.as_ptr(), key.raw())
205        };
206        util::status_result("AAFieldKeySetInsertKey", status)
207    }
208
209    /// Wraps `AAFieldKeySetRemoveKey`.
210    pub fn remove(&mut self, key: FieldKey) -> Result<()> {
211        let status = unsafe {
212            ffi::aa_field_key::compression_rs_aa_field_key_set_remove_key(self.as_ptr(), key.raw())
213        };
214        util::status_result("AAFieldKeySetRemoveKey", status)
215    }
216
217    /// Wraps `AAFieldKeySetInsertKeySet`.
218    pub fn insert_set(&mut self, other: &Self) -> Result<()> {
219        let status = unsafe {
220            ffi::aa_field_key::compression_rs_aa_field_key_set_insert_key_set(
221                self.as_ptr(),
222                other.as_ptr(),
223            )
224        };
225        util::status_result("AAFieldKeySetInsertKeySet", status)
226    }
227
228    /// Wraps `AAFieldKeySetRemoveKeySet`.
229    pub fn remove_set(&mut self, other: &Self) -> Result<()> {
230        let status = unsafe {
231            ffi::aa_field_key::compression_rs_aa_field_key_set_remove_key_set(
232                self.as_ptr(),
233                other.as_ptr(),
234            )
235        };
236        util::status_result("AAFieldKeySetRemoveKeySet", status)
237    }
238
239    /// Wraps `AAFieldKeySetSelectKeySet`.
240    pub fn select_set(&mut self, other: &Self) -> Result<()> {
241        let status = unsafe {
242            ffi::aa_field_key::compression_rs_aa_field_key_set_select_key_set(
243                self.as_ptr(),
244                other.as_ptr(),
245            )
246        };
247        util::status_result("AAFieldKeySetSelectKeySet", status)
248    }
249
250    /// Wraps `AAFieldKeySetGetKey`.
251    pub fn len(&self) -> u32 {
252        unsafe { ffi::aa_field_key::compression_rs_aa_field_key_set_get_key_count(self.as_ptr()) }
253    }
254
255    /// Wraps `AAFieldKeySetGetKey`.
256    pub fn is_empty(&self) -> bool {
257        self.len() == 0
258    }
259
260    /// Wraps `AAFieldKeySetGetKey`.
261    pub fn key(&self, index: u32) -> Result<FieldKey> {
262        if index >= self.len() {
263            return Err(CompressionError::OperationFailed {
264                operation: "AAFieldKeySetGetKey",
265                code: -1,
266            });
267        }
268
269        Ok(FieldKey::from_raw(unsafe {
270            ffi::aa_field_key::compression_rs_aa_field_key_set_get_key(self.as_ptr(), index)
271        }))
272    }
273
274    /// Wraps `AAFieldKeySetSerialize`.
275    pub fn serialize(&self) -> Result<String> {
276        let capacity = (self.len() as usize)
277            .saturating_mul(4)
278            .saturating_add(1)
279            .max(1);
280        let mut buffer = vec![0_i8; capacity];
281        let status = unsafe {
282            ffi::aa_field_key::compression_rs_aa_field_key_set_serialize(
283                self.as_ptr(),
284                buffer.len(),
285                buffer.as_mut_ptr(),
286            )
287        };
288        util::status_result("AAFieldKeySetSerialize", status)?;
289
290        let value = unsafe { CStr::from_ptr(buffer.as_ptr()) }
291            .to_str()
292            .map_err(|_| CompressionError::Utf8Error {
293                operation: "AAFieldKeySetSerialize",
294            })?;
295        Ok(value.to_string())
296    }
297}
298
299impl Clone for FieldKeySet {
300    fn clone(&self) -> Self {
301        let handle =
302            unsafe { ffi::aa_field_key::compression_rs_aa_field_key_set_clone(self.as_ptr()) };
303        Self {
304            handle: util::nonnull_handle(handle, "AAFieldKeySetClone")
305                .expect("AAFieldKeySetClone returned null"),
306        }
307    }
308}
309
310impl Drop for FieldKeySet {
311    fn drop(&mut self) {
312        unsafe { ffi::aa_field_key::compression_rs_aa_field_key_set_release(self.as_ptr()) };
313    }
314}