1use crate::{
2 aa_byte_stream::ArchiveFlags,
3 aa_field_key::{FieldKey, FieldKeySet},
4 ffi, util, CompressionError, Result,
5};
6use std::ffi::{c_void, CStr};
7use std::ptr::{null_mut, NonNull};
8
9#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
10pub enum HashFunction {
11 Crc32 = 1,
12 Sha1 = 2,
13 Sha256 = 3,
14 Sha384 = 4,
15 Sha512 = 5,
16}
17
18impl HashFunction {
19 pub const fn digest_len(self) -> usize {
20 match self {
21 Self::Crc32 => 4,
22 Self::Sha1 => 20,
23 Self::Sha256 => 32,
24 Self::Sha384 => 48,
25 Self::Sha512 => 64,
26 }
27 }
28
29 const fn from_raw(raw: u32) -> Option<Self> {
30 match raw {
31 1 => Some(Self::Crc32),
32 2 => Some(Self::Sha1),
33 3 => Some(Self::Sha256),
34 4 => Some(Self::Sha384),
35 5 => Some(Self::Sha512),
36 _ => None,
37 }
38 }
39
40 const fn as_raw(self) -> u32 {
41 self as u32
42 }
43}
44
45#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
46pub enum EntryType {
47 RegularFile = 'F' as isize,
48 Directory = 'D' as isize,
49 SymbolicLink = 'L' as isize,
50 Fifo = 'P' as isize,
51 CharacterDevice = 'C' as isize,
52 BlockDevice = 'B' as isize,
53 Socket = 'S' as isize,
54 Whiteout = 'W' as isize,
55 Door = 'R' as isize,
56 Port = 'T' as isize,
57 Metadata = 'M' as isize,
58}
59
60impl EntryType {
61 pub fn from_raw(raw: u32) -> Option<Self> {
62 match raw {
63 x if x == Self::RegularFile as u32 => Some(Self::RegularFile),
64 x if x == Self::Directory as u32 => Some(Self::Directory),
65 x if x == Self::SymbolicLink as u32 => Some(Self::SymbolicLink),
66 x if x == Self::Fifo as u32 => Some(Self::Fifo),
67 x if x == Self::CharacterDevice as u32 => Some(Self::CharacterDevice),
68 x if x == Self::BlockDevice as u32 => Some(Self::BlockDevice),
69 x if x == Self::Socket as u32 => Some(Self::Socket),
70 x if x == Self::Whiteout as u32 => Some(Self::Whiteout),
71 x if x == Self::Door as u32 => Some(Self::Door),
72 x if x == Self::Port as u32 => Some(Self::Port),
73 x if x == Self::Metadata as u32 => Some(Self::Metadata),
74 _ => None,
75 }
76 }
77}
78
79#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
80pub enum FieldType {
81 Flag = 0,
82 UInt = 1,
83 String = 2,
84 Hash = 3,
85 Timespec = 4,
86 Blob = 5,
87}
88
89impl FieldType {
90 fn from_raw(raw: i32) -> Option<Self> {
91 match raw {
92 0 => Some(Self::Flag),
93 1 => Some(Self::UInt),
94 2 => Some(Self::String),
95 3 => Some(Self::Hash),
96 4 => Some(Self::Timespec),
97 5 => Some(Self::Blob),
98 _ => None,
99 }
100 }
101}
102
103#[repr(C)]
104#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
105pub struct Timespec {
106 pub seconds: i64,
107 pub nanoseconds: i64,
108}
109
110#[derive(Clone, Debug, Eq, PartialEq)]
111pub struct HashValue {
112 pub function: HashFunction,
113 pub bytes: Vec<u8>,
114}
115
116#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
117pub struct BlobDescription {
118 pub size: u64,
119 pub offset: u64,
120}
121
122#[derive(Clone, Debug, Eq, PartialEq)]
123pub enum HeaderFieldValue {
124 Flag,
125 UInt(u64),
126 String(String),
127 Hash(HashValue),
128 Timespec(Timespec),
129 Blob(BlobDescription),
130}
131
132#[derive(Debug)]
133pub struct Header {
134 handle: NonNull<c_void>,
135}
136
137impl Header {
138 pub(crate) fn from_handle(handle: *mut c_void, operation: &'static str) -> Result<Self> {
139 Ok(Self {
140 handle: util::nonnull_handle(handle, operation)?,
141 })
142 }
143
144 pub fn new() -> Result<Self> {
145 let handle = unsafe { ffi::aa_header::compression_rs_aa_header_create() };
146 Self::from_handle(handle, "AAHeaderCreate")
147 }
148
149 pub fn from_encoded_data(data: &[u8]) -> Result<Self> {
150 let handle = unsafe {
151 ffi::aa_header::compression_rs_aa_header_create_with_encoded_data(
152 data.len(),
153 data.as_ptr(),
154 )
155 };
156 Self::from_handle(handle, "AAHeaderCreateWithEncodedData")
157 }
158
159 pub fn from_path(
160 key_set: &FieldKeySet,
161 dir: &str,
162 path: &str,
163 flags: ArchiveFlags,
164 ) -> Result<Self> {
165 let dir = util::cstring("dir", dir)?;
166 let path = util::cstring("path", path)?;
167 let handle = unsafe {
168 ffi::aa_header::compression_rs_aa_header_create_with_path(
169 key_set.as_ptr(),
170 dir.as_ptr(),
171 path.as_ptr(),
172 flags.bits(),
173 )
174 };
175 Self::from_handle(handle, "AAHeaderCreateWithPath")
176 }
177
178 pub(crate) fn as_ptr(&self) -> *mut c_void {
179 self.handle.as_ptr()
180 }
181
182 pub fn assign(&mut self, other: &Self) -> Result<()> {
183 let status = unsafe {
184 ffi::aa_header::compression_rs_aa_header_assign(self.as_ptr(), other.as_ptr())
185 };
186 util::status_result("AAHeaderAssign", status)
187 }
188
189 pub fn field_count(&self) -> u32 {
190 unsafe { ffi::aa_header::compression_rs_aa_header_get_field_count(self.as_ptr()) }
191 }
192
193 pub fn is_empty(&self) -> bool {
194 self.field_count() == 0
195 }
196
197 pub fn key_index(&self, key: FieldKey) -> Result<Option<u32>> {
198 match unsafe {
199 ffi::aa_header::compression_rs_aa_header_get_key_index(self.as_ptr(), key.raw())
200 } {
201 -1 => Ok(None),
202 value if value < -1 => Err(CompressionError::OperationFailed {
203 operation: "AAHeaderGetKeyIndex",
204 code: value,
205 }),
206 value => Ok(Some(value.unsigned_abs())),
207 }
208 }
209
210 pub fn field_type(&self, index: u32) -> Result<FieldType> {
211 let raw = unsafe {
212 ffi::aa_header::compression_rs_aa_header_get_field_type(self.as_ptr(), index)
213 };
214 if raw < 0 {
215 return Err(CompressionError::OperationFailed {
216 operation: "AAHeaderGetFieldType",
217 code: raw,
218 });
219 }
220 FieldType::from_raw(raw).ok_or(CompressionError::OperationFailed {
221 operation: "AAHeaderGetFieldType",
222 code: raw,
223 })
224 }
225
226 pub fn field_key(&self, index: u32) -> Result<FieldKey> {
227 if index >= self.field_count() {
228 return Err(CompressionError::OperationFailed {
229 operation: "AAHeaderGetFieldKey",
230 code: -1,
231 });
232 }
233
234 Ok(FieldKey::from_raw(unsafe {
235 ffi::aa_header::compression_rs_aa_header_get_field_key(self.as_ptr(), index)
236 }))
237 }
238
239 pub fn payload_size(&self) -> u64 {
240 unsafe { ffi::aa_header::compression_rs_aa_header_get_payload_size(self.as_ptr()) }
241 }
242
243 pub fn remove_field(&mut self, index: u32) -> Result<()> {
244 let status =
245 unsafe { ffi::aa_header::compression_rs_aa_header_remove_field(self.as_ptr(), index) };
246 util::status_result("AAHeaderRemoveField", status)
247 }
248
249 pub fn clear(&mut self) -> Result<()> {
250 let status = unsafe { ffi::aa_header::compression_rs_aa_header_clear(self.as_ptr()) };
251 util::status_result("AAHeaderClear", status)
252 }
253
254 pub fn set_field_flag(&mut self, index: u32, key: FieldKey) -> Result<()> {
255 let status = unsafe {
256 ffi::aa_header::compression_rs_aa_header_set_field_flag(self.as_ptr(), index, key.raw())
257 };
258 util::status_result("AAHeaderSetFieldFlag", status)
259 }
260
261 pub fn set_field_uint(&mut self, index: u32, key: FieldKey, value: u64) -> Result<()> {
262 let status = unsafe {
263 ffi::aa_header::compression_rs_aa_header_set_field_uint(
264 self.as_ptr(),
265 index,
266 key.raw(),
267 value,
268 )
269 };
270 util::status_result("AAHeaderSetFieldUInt", status)
271 }
272
273 pub fn set_field_string(&mut self, index: u32, key: FieldKey, value: &str) -> Result<()> {
274 let value = util::cstring("value", value)?;
275 let status = unsafe {
276 ffi::aa_header::compression_rs_aa_header_set_field_string(
277 self.as_ptr(),
278 index,
279 key.raw(),
280 value.as_ptr(),
281 value.as_bytes().len(),
282 )
283 };
284 util::status_result("AAHeaderSetFieldString", status)
285 }
286
287 pub fn set_field_hash(
288 &mut self,
289 index: u32,
290 key: FieldKey,
291 function: HashFunction,
292 value: &[u8],
293 ) -> Result<()> {
294 if value.len() != function.digest_len() {
295 return Err(CompressionError::InvalidHashLength {
296 expected: function.digest_len(),
297 actual: value.len(),
298 });
299 }
300 let status = unsafe {
301 ffi::aa_header::compression_rs_aa_header_set_field_hash(
302 self.as_ptr(),
303 index,
304 key.raw(),
305 function.as_raw(),
306 value.as_ptr(),
307 )
308 };
309 util::status_result("AAHeaderSetFieldHash", status)
310 }
311
312 pub fn set_field_timespec(&mut self, index: u32, key: FieldKey, value: Timespec) -> Result<()> {
313 let status = unsafe {
314 ffi::aa_header::compression_rs_aa_header_set_field_timespec(
315 self.as_ptr(),
316 index,
317 key.raw(),
318 value.seconds,
319 value.nanoseconds,
320 )
321 };
322 util::status_result("AAHeaderSetFieldTimespec", status)
323 }
324
325 pub fn set_field_blob(&mut self, index: u32, key: FieldKey, size: u64) -> Result<()> {
326 let status = unsafe {
327 ffi::aa_header::compression_rs_aa_header_set_field_blob(
328 self.as_ptr(),
329 index,
330 key.raw(),
331 size,
332 )
333 };
334 util::status_result("AAHeaderSetFieldBlob", status)
335 }
336
337 pub fn append_field_flag(&mut self, key: FieldKey) -> Result<()> {
338 self.set_field_flag(u32::MAX, key)
339 }
340
341 pub fn append_field_uint(&mut self, key: FieldKey, value: u64) -> Result<()> {
342 self.set_field_uint(u32::MAX, key, value)
343 }
344
345 pub fn append_field_string(&mut self, key: FieldKey, value: &str) -> Result<()> {
346 self.set_field_string(u32::MAX, key, value)
347 }
348
349 pub fn append_field_hash(
350 &mut self,
351 key: FieldKey,
352 function: HashFunction,
353 value: &[u8],
354 ) -> Result<()> {
355 self.set_field_hash(u32::MAX, key, function, value)
356 }
357
358 pub fn append_field_timespec(&mut self, key: FieldKey, value: Timespec) -> Result<()> {
359 self.set_field_timespec(u32::MAX, key, value)
360 }
361
362 pub fn append_field_blob(&mut self, key: FieldKey, size: u64) -> Result<()> {
363 self.set_field_blob(u32::MAX, key, size)
364 }
365
366 pub fn field_uint(&self, index: u32) -> Result<u64> {
367 let mut value = 0_u64;
368 let status = unsafe {
369 ffi::aa_header::compression_rs_aa_header_get_field_uint(
370 self.as_ptr(),
371 index,
372 &mut value,
373 )
374 };
375 util::status_result("AAHeaderGetFieldUInt", status)?;
376 Ok(value)
377 }
378
379 pub fn field_string(&self, index: u32) -> Result<String> {
380 let mut length = 0_usize;
381 let status = unsafe {
382 ffi::aa_header::compression_rs_aa_header_get_field_string(
383 self.as_ptr(),
384 index,
385 0,
386 null_mut(),
387 &mut length,
388 )
389 };
390 util::status_result("AAHeaderGetFieldString", status)?;
391
392 let mut buffer = vec![0_i8; length.saturating_add(1)];
393 let status = unsafe {
394 ffi::aa_header::compression_rs_aa_header_get_field_string(
395 self.as_ptr(),
396 index,
397 buffer.len(),
398 buffer.as_mut_ptr(),
399 &mut length,
400 )
401 };
402 util::status_result("AAHeaderGetFieldString", status)?;
403
404 let value = unsafe { CStr::from_ptr(buffer.as_ptr()) }
405 .to_str()
406 .map_err(|_| CompressionError::Utf8Error {
407 operation: "AAHeaderGetFieldString",
408 })?;
409 Ok(value.to_string())
410 }
411
412 pub fn field_hash(&self, index: u32) -> Result<HashValue> {
413 let mut function = 0_u32;
414 let mut bytes = vec![0_u8; HashFunction::Sha512.digest_len()];
415 let status = unsafe {
416 ffi::aa_header::compression_rs_aa_header_get_field_hash(
417 self.as_ptr(),
418 index,
419 bytes.len(),
420 &mut function,
421 bytes.as_mut_ptr(),
422 )
423 };
424 util::status_result("AAHeaderGetFieldHash", status)?;
425 let function =
426 HashFunction::from_raw(function).ok_or(CompressionError::OperationFailed {
427 operation: "AAHeaderGetFieldHash",
428 code: -1,
429 })?;
430 bytes.truncate(function.digest_len());
431 Ok(HashValue { function, bytes })
432 }
433
434 pub fn field_timespec(&self, index: u32) -> Result<Timespec> {
435 let mut seconds = 0_i64;
436 let mut nanoseconds = 0_i64;
437 let status = unsafe {
438 ffi::aa_header::compression_rs_aa_header_get_field_timespec(
439 self.as_ptr(),
440 index,
441 &mut seconds,
442 &mut nanoseconds,
443 )
444 };
445 util::status_result("AAHeaderGetFieldTimespec", status)?;
446 Ok(Timespec {
447 seconds,
448 nanoseconds,
449 })
450 }
451
452 pub fn field_blob(&self, index: u32) -> Result<BlobDescription> {
453 let mut size = 0_u64;
454 let mut offset = 0_u64;
455 let status = unsafe {
456 ffi::aa_header::compression_rs_aa_header_get_field_blob(
457 self.as_ptr(),
458 index,
459 &mut size,
460 &mut offset,
461 )
462 };
463 util::status_result("AAHeaderGetFieldBlob", status)?;
464 Ok(BlobDescription { size, offset })
465 }
466
467 pub fn field_value(&self, index: u32) -> Result<HeaderFieldValue> {
468 match self.field_type(index)? {
469 FieldType::Flag => Ok(HeaderFieldValue::Flag),
470 FieldType::UInt => self.field_uint(index).map(HeaderFieldValue::UInt),
471 FieldType::String => self.field_string(index).map(HeaderFieldValue::String),
472 FieldType::Hash => self.field_hash(index).map(HeaderFieldValue::Hash),
473 FieldType::Timespec => self.field_timespec(index).map(HeaderFieldValue::Timespec),
474 FieldType::Blob => self.field_blob(index).map(HeaderFieldValue::Blob),
475 }
476 }
477
478 pub fn uint_with_key(&self, key: FieldKey) -> Result<Option<u64>> {
479 self.key_index(key)?
480 .map(|index| self.field_uint(index))
481 .transpose()
482 }
483
484 pub fn string_with_key(&self, key: FieldKey) -> Result<Option<String>> {
485 self.key_index(key)?
486 .map(|index| self.field_string(index))
487 .transpose()
488 }
489
490 pub fn hash_with_key(&self, key: FieldKey) -> Result<Option<HashValue>> {
491 self.key_index(key)?
492 .map(|index| self.field_hash(index))
493 .transpose()
494 }
495
496 pub fn timespec_with_key(&self, key: FieldKey) -> Result<Option<Timespec>> {
497 self.key_index(key)?
498 .map(|index| self.field_timespec(index))
499 .transpose()
500 }
501
502 pub fn blob_with_key(&self, key: FieldKey) -> Result<Option<BlobDescription>> {
503 self.key_index(key)?
504 .map(|index| self.field_blob(index))
505 .transpose()
506 }
507
508 pub fn value_with_key(&self, key: FieldKey) -> Result<Option<HeaderFieldValue>> {
509 self.key_index(key)?
510 .map(|index| self.field_value(index))
511 .transpose()
512 }
513
514 pub fn entry_type(&self) -> Result<Option<EntryType>> {
515 Ok(self
516 .uint_with_key(FieldKey::TYP)?
517 .and_then(|raw| u32::try_from(raw).ok())
518 .and_then(EntryType::from_raw))
519 }
520
521 pub fn path(&self) -> Result<Option<String>> {
522 self.string_with_key(FieldKey::PAT)
523 }
524
525 pub fn encoded_size(&self) -> usize {
526 unsafe { ffi::aa_header::compression_rs_aa_header_get_encoded_size(self.as_ptr()) }
527 }
528
529 pub fn encoded_data(&self) -> Result<Vec<u8>> {
530 let size = self.encoded_size();
531 let mut data = vec![0_u8; size];
532 if size == 0 {
533 return Ok(data);
534 }
535 let copied = unsafe {
536 ffi::aa_header::compression_rs_aa_header_copy_encoded_data(
537 self.as_ptr(),
538 data.as_mut_ptr(),
539 )
540 };
541 if copied {
542 Ok(data)
543 } else {
544 Err(CompressionError::OperationFailed {
545 operation: "AAHeaderGetEncodedData",
546 code: -1,
547 })
548 }
549 }
550}
551
552impl Clone for Header {
553 fn clone(&self) -> Self {
554 let handle = unsafe { ffi::aa_header::compression_rs_aa_header_clone(self.as_ptr()) };
555 Self::from_handle(handle, "AAHeaderClone").expect("AAHeaderClone returned null")
556 }
557}
558
559impl Drop for Header {
560 fn drop(&mut self) {
561 unsafe { ffi::aa_header::compression_rs_aa_header_release(self.as_ptr()) };
562 }
563}
564
565impl Header {
566 pub(crate) fn from_raw_clone(raw: *mut c_void, operation: &'static str) -> Result<Self> {
567 let handle = unsafe { ffi::aa_header::compression_rs_aa_header_clone_from_raw(raw) };
568 Self::from_handle(handle, operation)
569 }
570
571 pub(crate) fn clone_raw(&self) -> Result<*mut c_void> {
572 let raw = unsafe { ffi::aa_header::compression_rs_aa_header_clone_raw(self.as_ptr()) };
573 if raw.is_null() {
574 Err(CompressionError::NullHandle {
575 operation: "AAHeaderClone",
576 })
577 } else {
578 Ok(raw)
579 }
580 }
581}