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