Skip to main content

compression/
aa_archive_stream.rs

1use crate::{
2    aa_byte_stream::{ArchiveFlags, ByteStream},
3    aa_entry_blob::{EntryAclBlob, EntryXatBlob},
4    aa_entry_stream::{EntryAttributes, EntryMessage, PathList},
5    aa_field_key::{FieldKey, FieldKeySet},
6    aa_header::Header,
7    ffi, util, CompressionError, Result,
8};
9use std::ffi::{c_char, c_void, CStr};
10use std::ptr::NonNull;
11
12#[allow(dead_code)]
13#[derive(Debug)]
14enum ArchiveStreamUpstream {
15    Byte(Box<ByteStream>),
16    Archive(Box<ArchiveStream>),
17}
18
19/// Wraps an `AAArchiveStream` handle.
20#[derive(Debug)]
21pub struct ArchiveStream {
22    handle: NonNull<c_void>,
23    _upstream: Option<ArchiveStreamUpstream>,
24    closed: bool,
25    _message_handler: Option<Box<ArchiveMessageState>>,
26}
27
28impl ArchiveStream {
29    /// Wraps `AAExtractArchiveOutputStreamOpen`.
30    pub fn extract_output(dir: &str, flags: ArchiveFlags, n_threads: i32) -> Result<Self> {
31        let dir = util::cstring("dir", dir)?;
32        let handle = unsafe {
33            ffi::aa_archive_stream::compression_rs_aa_extract_archive_output_stream_open(
34                dir.as_ptr(),
35                flags.bits(),
36                n_threads,
37            )
38        };
39        Ok(Self {
40            handle: util::nonnull_handle(handle, "AAExtractArchiveOutputStreamOpen")?,
41            _upstream: None,
42            closed: false,
43            _message_handler: None,
44        })
45    }
46
47    /// Wraps `AAEncodeArchiveOutputStreamOpen`.
48    pub fn encode_output(stream: ByteStream, flags: ArchiveFlags, n_threads: i32) -> Result<Self> {
49        let handle = unsafe {
50            ffi::aa_archive_stream::compression_rs_aa_encode_archive_output_stream_open(
51                stream.as_ptr(),
52                flags.bits(),
53                n_threads,
54            )
55        };
56        Ok(Self {
57            handle: util::nonnull_handle(handle, "AAEncodeArchiveOutputStreamOpen")?,
58            _upstream: Some(ArchiveStreamUpstream::Byte(Box::new(stream))),
59            closed: false,
60            _message_handler: None,
61        })
62    }
63
64    /// Wraps `AADecodeArchiveInputStreamOpen`.
65    pub fn decode_input(stream: ByteStream, flags: ArchiveFlags, n_threads: i32) -> Result<Self> {
66        let handle = unsafe {
67            ffi::aa_archive_stream::compression_rs_aa_decode_archive_input_stream_open(
68                stream.as_ptr(),
69                flags.bits(),
70                n_threads,
71            )
72        };
73        Ok(Self {
74            handle: util::nonnull_handle(handle, "AADecodeArchiveInputStreamOpen")?,
75            _upstream: Some(ArchiveStreamUpstream::Byte(Box::new(stream))),
76            closed: false,
77            _message_handler: None,
78        })
79    }
80
81    /// Wraps `AAConvertArchiveOutputStreamOpen`.
82    pub fn convert_output(
83        stream: ArchiveStream,
84        insert_key_set: &FieldKeySet,
85        remove_key_set: &FieldKeySet,
86        flags: ArchiveFlags,
87        n_threads: i32,
88    ) -> Result<Self> {
89        let handle = unsafe {
90            ffi::aa_archive_stream::compression_rs_aa_convert_archive_output_stream_open(
91                stream.as_ptr(),
92                insert_key_set.as_ptr(),
93                remove_key_set.as_ptr(),
94                flags.bits(),
95                n_threads,
96            )
97        };
98        Ok(Self {
99            handle: util::nonnull_handle(handle, "AAConvertArchiveOutputStreamOpen")?,
100            _upstream: Some(ArchiveStreamUpstream::Archive(Box::new(stream))),
101            closed: false,
102            _message_handler: None,
103        })
104    }
105
106    pub(crate) fn as_ptr(&self) -> *mut c_void {
107        self.handle.as_ptr()
108    }
109
110    fn ensure_open(&self) -> Result<()> {
111        if self.closed {
112            Err(CompressionError::Closed {
113                resource: "archive stream",
114            })
115        } else {
116            Ok(())
117        }
118    }
119
120    /// Wraps `AAArchiveStreamWriteHeader`.
121    pub fn write_header(&mut self, header: &Header) -> Result<()> {
122        self.ensure_open()?;
123        let status = unsafe {
124            ffi::aa_archive_stream::compression_rs_aa_archive_stream_write_header(
125                self.as_ptr(),
126                header.as_ptr(),
127            )
128        };
129        util::status_result("AAArchiveStreamWriteHeader", status)
130    }
131
132    /// Wraps `AAArchiveStreamWriteBlob`.
133    pub fn write_blob(&mut self, key: FieldKey, buffer: &[u8]) -> Result<()> {
134        self.ensure_open()?;
135        let status = unsafe {
136            ffi::aa_archive_stream::compression_rs_aa_archive_stream_write_blob(
137                self.as_ptr(),
138                key.raw(),
139                buffer.as_ptr(),
140                buffer.len(),
141            )
142        };
143        util::status_result("AAArchiveStreamWriteBlob", status)
144    }
145
146    /// Wraps `AAArchiveStreamReadHeader`.
147    pub fn read_header(&mut self) -> Result<Option<Header>> {
148        self.ensure_open()?;
149        let mut status = 0_i32;
150        let handle = unsafe {
151            ffi::aa_archive_stream::compression_rs_aa_archive_stream_read_header_new(
152                self.as_ptr(),
153                &mut status,
154            )
155        };
156        match status {
157            1 => Ok(Some(Header::from_handle(
158                handle,
159                "AAArchiveStreamReadHeader",
160            )?)),
161            0 => Ok(None),
162            code => Err(CompressionError::OperationFailed {
163                operation: "AAArchiveStreamReadHeader",
164                code,
165            }),
166        }
167    }
168
169    /// Wraps `AAArchiveStreamReadHeader`.
170    pub fn read_header_into(&mut self, header: &mut Header) -> Result<bool> {
171        self.ensure_open()?;
172        match unsafe {
173            ffi::aa_archive_stream::compression_rs_aa_archive_stream_read_header_into(
174                self.as_ptr(),
175                header.as_ptr(),
176            )
177        } {
178            1 => Ok(true),
179            0 => Ok(false),
180            code => Err(CompressionError::OperationFailed {
181                operation: "AAArchiveStreamReadHeader",
182                code,
183            }),
184        }
185    }
186
187    /// Wraps `AAArchiveStreamReadBlob`.
188    pub fn read_blob(&mut self, key: FieldKey, buffer: &mut [u8]) -> Result<()> {
189        self.ensure_open()?;
190        let status = unsafe {
191            ffi::aa_archive_stream::compression_rs_aa_archive_stream_read_blob(
192                self.as_ptr(),
193                key.raw(),
194                buffer.as_mut_ptr(),
195                buffer.len(),
196            )
197        };
198        util::status_result("AAArchiveStreamReadBlob", status)
199    }
200
201    /// Wraps `AAArchiveStreamWritePathList`.
202    pub fn write_path_list(
203        &mut self,
204        path_list: &PathList,
205        key_set: &FieldKeySet,
206        dir: &str,
207        flags: ArchiveFlags,
208        n_threads: i32,
209    ) -> Result<()> {
210        self.ensure_open()?;
211        let dir = util::cstring("dir", dir)?;
212        let status = unsafe {
213            ffi::aa_archive_stream::compression_rs_aa_archive_stream_write_path_list(
214                self.as_ptr(),
215                path_list.as_ptr(),
216                key_set.as_ptr(),
217                dir.as_ptr(),
218                flags.bits(),
219                n_threads,
220            )
221        };
222        util::status_result("AAArchiveStreamWritePathList", status)
223    }
224
225    /// Wraps `AAArchiveStreamProcess`.
226    pub fn process_into(
227        &mut self,
228        output: &mut Self,
229        flags: ArchiveFlags,
230        n_threads: i32,
231    ) -> Result<u64> {
232        self.ensure_open()?;
233        output.ensure_open()?;
234        util::off_t_result("AAArchiveStreamProcess", unsafe {
235            ffi::aa_archive_stream::compression_rs_aa_archive_stream_process(
236                self.as_ptr(),
237                output.as_ptr(),
238                flags.bits(),
239                n_threads,
240            )
241        })
242    }
243
244    /// Wraps `AAArchiveStreamClose`.
245    pub fn cancel(&mut self) -> Result<()> {
246        self.ensure_open()?;
247        unsafe { ffi::aa_archive_stream::compression_rs_aa_archive_stream_cancel(self.as_ptr()) };
248        Ok(())
249    }
250
251    #[deprecated(
252        since = "0.2.2",
253        note = "Use ArchiveStream::cancel; AAArchiveStreamAbort is a deprecated AppleArchive compatibility shim."
254    )]
255    /// Wraps `AAArchiveStreamClose`.
256    pub fn abort(&mut self) -> Result<()> {
257        self.ensure_open()?;
258        unsafe { ffi::aa_archive_stream::compression_rs_aa_archive_stream_abort(self.as_ptr()) };
259        Ok(())
260    }
261
262    /// Wraps `AAArchiveStreamClose`.
263    pub fn close(&mut self) -> Result<()> {
264        if self.closed {
265            return Ok(());
266        }
267        let status = unsafe {
268            ffi::aa_archive_stream::compression_rs_aa_archive_stream_close(self.as_ptr())
269        };
270        self.closed = true;
271        util::status_result("AAArchiveStreamClose", status)
272    }
273}
274
275impl Drop for ArchiveStream {
276    fn drop(&mut self) {
277        unsafe { ffi::aa_archive_stream::compression_rs_aa_archive_stream_release(self.as_ptr()) };
278    }
279}
280
281fn custom_archive_stream_error(operation: &'static str) -> CompressionError {
282    CompressionError::OperationFailed {
283        operation,
284        code: -1,
285    }
286}
287
288fn custom_archive_stream_code(error: &CompressionError) -> i32 {
289    match error {
290        CompressionError::OperationFailed { code, .. } if *code < 0 => *code,
291        _ => -1,
292    }
293}
294
295struct CustomArchiveStreamState {
296    callbacks: Box<dyn CustomArchiveStreamCallbacks>,
297}
298
299/// Wraps callbacks installed by `AACustomArchiveStreamSet*Proc`.
300pub trait CustomArchiveStreamCallbacks {
301    /// Wraps `AAArchiveStreamWriteHeader`.
302    fn write_header(&mut self, _header: &Header) -> Result<()> {
303        Err(custom_archive_stream_error("AAArchiveStreamWriteHeader"))
304    }
305
306    /// Wraps `AAArchiveStreamWriteBlob`.
307    fn write_blob(&mut self, _key: FieldKey, _buffer: &[u8]) -> Result<()> {
308        Err(custom_archive_stream_error("AAArchiveStreamWriteBlob"))
309    }
310
311    /// Wraps `AAArchiveStreamReadHeader`.
312    fn read_header(&mut self) -> Result<Option<Header>> {
313        Err(custom_archive_stream_error("AAArchiveStreamReadHeader"))
314    }
315
316    /// Wraps `AAArchiveStreamReadBlob`.
317    fn read_blob(&mut self, _key: FieldKey, _buffer: &mut [u8]) -> Result<()> {
318        Err(custom_archive_stream_error("AAArchiveStreamReadBlob"))
319    }
320
321    /// Wraps the `cancel` convenience for `CustomArchiveStreamCallbacks`.
322    fn cancel(&mut self) {}
323
324    /// Wraps the `close` convenience for `CustomArchiveStreamCallbacks`.
325    fn close(&mut self) -> Result<()> {
326        Ok(())
327    }
328}
329
330unsafe fn custom_archive_stream_state(
331    arg: *mut c_void,
332) -> Option<&'static mut CustomArchiveStreamState> {
333    if arg.is_null() {
334        None
335    } else {
336        Some(unsafe { &mut *arg.cast::<CustomArchiveStreamState>() })
337    }
338}
339
340unsafe fn custom_archive_stream_slice<'a>(
341    buffer: *const c_void,
342    length: usize,
343) -> Option<&'a [u8]> {
344    if length == 0 {
345        Some(&[])
346    } else if buffer.is_null() {
347        None
348    } else {
349        Some(unsafe { std::slice::from_raw_parts(buffer.cast::<u8>(), length) })
350    }
351}
352
353unsafe fn custom_archive_stream_slice_mut<'a>(
354    buffer: *mut c_void,
355    length: usize,
356) -> Option<&'a mut [u8]> {
357    if length == 0 {
358        Some(&mut [])
359    } else if buffer.is_null() {
360        None
361    } else {
362        Some(unsafe { std::slice::from_raw_parts_mut(buffer.cast::<u8>(), length) })
363    }
364}
365
366/// Run a user archive-stream callback body, converting a panic into the
367/// failure status `-1` instead of letting it unwind across the C ABI (UB).
368fn guard_archive_status(f: impl FnOnce() -> i32) -> i32 {
369    std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)).unwrap_or(-1)
370}
371
372unsafe extern "C" fn custom_archive_stream_write_header(
373    arg: *mut c_void,
374    header: *mut c_void,
375) -> i32 {
376    let Some(state) = (unsafe { custom_archive_stream_state(arg) }) else {
377        return -1;
378    };
379    if header.is_null() {
380        return -1;
381    }
382    match Header::from_raw_clone(header, "AAHeaderClone") {
383        Ok(header) => guard_archive_status(|| match state.callbacks.write_header(&header) {
384            Ok(()) => 0,
385            Err(error) => custom_archive_stream_code(&error),
386        }),
387        Err(error) => custom_archive_stream_code(&error),
388    }
389}
390
391unsafe extern "C" fn custom_archive_stream_write_blob(
392    arg: *mut c_void,
393    key: u32,
394    buffer: *const c_void,
395    length: usize,
396) -> i32 {
397    let Some(state) = (unsafe { custom_archive_stream_state(arg) }) else {
398        return -1;
399    };
400    let Some(buffer) = (unsafe { custom_archive_stream_slice(buffer, length) }) else {
401        return -1;
402    };
403    guard_archive_status(|| match state.callbacks.write_blob(FieldKey::from_raw(key), buffer) {
404        Ok(()) => 0,
405        Err(error) => custom_archive_stream_code(&error),
406    })
407}
408
409unsafe extern "C" fn custom_archive_stream_read_header(
410    arg: *mut c_void,
411    header_out: *mut *mut c_void,
412) -> i32 {
413    let Some(state) = (unsafe { custom_archive_stream_state(arg) }) else {
414        return -1;
415    };
416    let Ok(header) = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
417        state.callbacks.read_header()
418    })) else {
419        return -1;
420    };
421    match header {
422        Ok(Some(header)) => {
423            if header_out.is_null() {
424                return -1;
425            }
426            let raw = match header.clone_raw() {
427                Ok(raw) => raw,
428                Err(error) => return custom_archive_stream_code(&error),
429            };
430            unsafe { *header_out = raw };
431            1
432        }
433        Ok(None) => 0,
434        Err(error) => custom_archive_stream_code(&error),
435    }
436}
437
438unsafe extern "C" fn custom_archive_stream_read_blob(
439    arg: *mut c_void,
440    key: u32,
441    buffer: *mut c_void,
442    length: usize,
443) -> i32 {
444    let Some(state) = (unsafe { custom_archive_stream_state(arg) }) else {
445        return -1;
446    };
447    let Some(buffer) = (unsafe { custom_archive_stream_slice_mut(buffer, length) }) else {
448        return -1;
449    };
450    guard_archive_status(|| match state.callbacks.read_blob(FieldKey::from_raw(key), buffer) {
451        Ok(()) => 0,
452        Err(error) => custom_archive_stream_code(&error),
453    })
454}
455
456unsafe extern "C" fn custom_archive_stream_cancel(arg: *mut c_void) {
457    if let Some(state) = unsafe { custom_archive_stream_state(arg) } {
458        let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| state.callbacks.cancel()));
459    }
460}
461
462unsafe extern "C" fn custom_archive_stream_close(arg: *mut c_void) -> i32 {
463    if arg.is_null() {
464        return 0;
465    }
466    let mut state = unsafe { Box::from_raw(arg.cast::<CustomArchiveStreamState>()) };
467    guard_archive_status(|| match state.callbacks.close() {
468        Ok(()) => 0,
469        Err(error) => custom_archive_stream_code(&error),
470    })
471}
472
473impl ArchiveStream {
474    /// Wraps `AACustomArchiveStreamOpen`.
475    pub fn custom<T: CustomArchiveStreamCallbacks + 'static>(callbacks: T) -> Result<Self> {
476        let handle =
477            unsafe { ffi::aa_archive_stream::compression_rs_aa_custom_archive_stream_open() };
478        let stream = Self {
479            handle: util::nonnull_handle(handle, "AACustomArchiveStreamOpen")?,
480            _upstream: None,
481            closed: false,
482            _message_handler: None,
483        };
484        let state = Box::new(CustomArchiveStreamState {
485            callbacks: Box::new(callbacks),
486        });
487        let data = Box::into_raw(state).cast::<c_void>();
488        unsafe {
489            ffi::aa_archive_stream::compression_rs_aa_custom_archive_stream_set_data(
490                stream.as_ptr(),
491                data,
492            );
493            ffi::aa_archive_stream::compression_rs_aa_custom_archive_stream_set_write_header_proc(
494                stream.as_ptr(),
495                Some(custom_archive_stream_write_header),
496            );
497            ffi::aa_archive_stream::compression_rs_aa_custom_archive_stream_set_write_blob_proc(
498                stream.as_ptr(),
499                Some(custom_archive_stream_write_blob),
500            );
501            ffi::aa_archive_stream::compression_rs_aa_custom_archive_stream_set_read_header_proc(
502                stream.as_ptr(),
503                Some(custom_archive_stream_read_header),
504            );
505            ffi::aa_archive_stream::compression_rs_aa_custom_archive_stream_set_read_blob_proc(
506                stream.as_ptr(),
507                Some(custom_archive_stream_read_blob),
508            );
509            ffi::aa_archive_stream::compression_rs_aa_custom_archive_stream_set_cancel_proc(
510                stream.as_ptr(),
511                Some(custom_archive_stream_cancel),
512            );
513            ffi::aa_archive_stream::compression_rs_aa_custom_archive_stream_set_abort_proc(
514                stream.as_ptr(),
515                Some(custom_archive_stream_cancel),
516            );
517            ffi::aa_archive_stream::compression_rs_aa_custom_archive_stream_set_close_proc(
518                stream.as_ptr(),
519                Some(custom_archive_stream_close),
520            );
521        }
522        Ok(stream)
523    }
524}
525
526/// Wraps payloads delivered through `AAEntryMessageProc`.
527pub enum EntryMessageData {
528    /// Wraps the `None` payload delivered by `AAEntryMessageProc`.
529    None,
530    /// Wraps the `Header` payload delivered by `AAEntryMessageProc`.
531    Header(Header),
532    /// Wraps the `EntryIds` payload delivered by `AAEntryMessageProc`.
533    EntryIds {
534        /// Wraps the `idx` field delivered by `AAEntryMessageProc`.
535        idx: u64,
536        /// Wraps the `idz` field delivered by `AAEntryMessageProc`.
537        idz: u64,
538    },
539    /// Wraps the `Progress` payload delivered by `AAEntryMessageProc`.
540    Progress {
541        /// Wraps the `total` field delivered by `AAEntryMessageProc`.
542        total: u64,
543        /// Wraps the `current` field delivered by `AAEntryMessageProc`.
544        current: u64,
545    },
546    /// Wraps the `Attributes` payload delivered by `AAEntryMessageProc`.
547    Attributes(EntryAttributes),
548    /// Wraps the `Xat` payload delivered by `AAEntryMessageProc`.
549    Xat(EntryXatBlob),
550    /// Wraps the `Acl` payload delivered by `AAEntryMessageProc`.
551    Acl(EntryAclBlob),
552}
553
554impl std::fmt::Debug for EntryMessageData {
555    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
556        match self {
557            Self::None => f.write_str("None"),
558            Self::Header(_) => f.write_str("Header(..)"),
559            Self::EntryIds { idx, idz } => f
560                .debug_struct("EntryIds")
561                .field("idx", idx)
562                .field("idz", idz)
563                .finish(),
564            Self::Progress { total, current } => f
565                .debug_struct("Progress")
566                .field("total", total)
567                .field("current", current)
568                .finish(),
569            Self::Attributes(attributes) => f.debug_tuple("Attributes").field(attributes).finish(),
570            Self::Xat(_) => f.write_str("Xat(..)"),
571            Self::Acl(_) => f.write_str("Acl(..)"),
572        }
573    }
574}
575
576/// Wraps `AAEntryMessageProc` event data.
577#[derive(Debug)]
578pub struct EntryMessageEvent {
579    /// Wraps the `message` field of `EntryMessageEvent`.
580    pub message: EntryMessage,
581    /// Wraps the `path` field of `EntryMessageEvent`.
582    pub path: String,
583    /// Wraps the `data` field of `EntryMessageEvent`.
584    pub data: EntryMessageData,
585}
586
587/// Wraps handlers installed through `AAEntryMessageProc`.
588pub trait EntryMessageHandler {
589    /// Wraps the `handle` convenience for `EntryMessageHandler`.
590    fn handle(&mut self, event: &mut EntryMessageEvent) -> Result<i32>;
591}
592
593impl<F> EntryMessageHandler for F
594where
595    F: FnMut(&mut EntryMessageEvent) -> Result<i32>,
596{
597    fn handle(&mut self, event: &mut EntryMessageEvent) -> Result<i32> {
598        self(event)
599    }
600}
601
602struct ArchiveMessageState {
603    handler: Box<dyn EntryMessageHandler>,
604}
605
606impl std::fmt::Debug for ArchiveMessageState {
607    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
608        f.write_str("ArchiveMessageState(..)")
609    }
610}
611
612fn archive_message_code(error: &CompressionError) -> i32 {
613    match error {
614        CompressionError::OperationFailed { code, .. } if *code < 0 => *code,
615        _ => -1,
616    }
617}
618
619unsafe fn archive_message_state(arg: *mut c_void) -> Option<&'static mut ArchiveMessageState> {
620    if arg.is_null() {
621        None
622    } else {
623        Some(unsafe { &mut *arg.cast::<ArchiveMessageState>() })
624    }
625}
626
627unsafe fn archive_message_pair(data: *mut c_void) -> Option<(u64, u64)> {
628    if data.is_null() {
629        None
630    } else {
631        let values = unsafe { std::slice::from_raw_parts(data.cast::<u64>(), 2) };
632        Some((values[0], values[1]))
633    }
634}
635
636fn archive_message_data(message: EntryMessage, data: *mut c_void) -> Result<EntryMessageData> {
637    match message {
638        EntryMessage::SearchPruneDir
639        | EntryMessage::SearchExclude
640        | EntryMessage::SearchFail
641        | EntryMessage::EncodeScanning => Ok(EntryMessageData::None),
642        EntryMessage::ExtractBegin
643        | EntryMessage::ConvertExclude
644        | EntryMessage::ProcessExclude => {
645            if data.is_null() {
646                Ok(EntryMessageData::None)
647            } else {
648                Ok(EntryMessageData::Header(Header::from_raw_clone(
649                    data,
650                    "AAHeaderClone",
651                )?))
652            }
653        }
654        EntryMessage::ExtractEnd | EntryMessage::ExtractFail => {
655            if let Some((idx, idz)) = unsafe { archive_message_pair(data) } {
656                Ok(EntryMessageData::EntryIds { idx, idz })
657            } else {
658                Ok(EntryMessageData::None)
659            }
660        }
661        EntryMessage::EncodeWriting | EntryMessage::DecodeReading => {
662            if let Some((total, current)) = unsafe { archive_message_pair(data) } {
663                Ok(EntryMessageData::Progress { total, current })
664            } else {
665                Ok(EntryMessageData::None)
666            }
667        }
668        EntryMessage::ExtractAttributes => {
669            if data.is_null() {
670                Ok(EntryMessageData::None)
671            } else {
672                Ok(EntryMessageData::Attributes(unsafe {
673                    *data.cast::<EntryAttributes>()
674                }))
675            }
676        }
677        EntryMessage::ExtractXat => {
678            if data.is_null() {
679                Ok(EntryMessageData::None)
680            } else {
681                Ok(EntryMessageData::Xat(EntryXatBlob::clone_from_raw(data)?))
682            }
683        }
684        EntryMessage::ExtractAcl => {
685            if data.is_null() {
686                Ok(EntryMessageData::None)
687            } else {
688                Ok(EntryMessageData::Acl(EntryAclBlob::clone_from_raw(data)?))
689            }
690        }
691    }
692}
693
694fn archive_message_sync(
695    message: EntryMessage,
696    data_ptr: *mut c_void,
697    data: &EntryMessageData,
698) -> Result<()> {
699    match (message, data) {
700        (EntryMessage::ExtractAttributes, EntryMessageData::Attributes(attributes)) => {
701            if !data_ptr.is_null() {
702                unsafe { *data_ptr.cast::<EntryAttributes>() = *attributes };
703            }
704            Ok(())
705        }
706        (EntryMessage::ExtractXat, EntryMessageData::Xat(xat)) => {
707            if data_ptr.is_null() {
708                Ok(())
709            } else {
710                EntryXatBlob::sync_into_raw(data_ptr, xat)
711            }
712        }
713        (EntryMessage::ExtractAcl, EntryMessageData::Acl(acl)) => {
714            if data_ptr.is_null() {
715                Ok(())
716            } else {
717                EntryAclBlob::sync_into_raw(data_ptr, acl)
718            }
719        }
720        _ => Ok(()),
721    }
722}
723
724unsafe extern "C" fn archive_entry_message_proc(
725    arg: *mut c_void,
726    message_raw: u32,
727    path: *const c_char,
728    data: *mut c_void,
729) -> i32 {
730    let Some(state) = (unsafe { archive_message_state(arg) }) else {
731        return -1;
732    };
733    let Some(message) = EntryMessage::from_raw(message_raw) else {
734        return -1;
735    };
736    if path.is_null() {
737        return -1;
738    }
739    let mut event = match archive_message_data(message, data) {
740        Ok(event_data) => EntryMessageEvent {
741            message,
742            path: unsafe { CStr::from_ptr(path) }
743                .to_string_lossy()
744                .into_owned(),
745            data: event_data,
746        },
747        Err(error) => return archive_message_code(&error),
748    };
749    match state.handler.handle(&mut event) {
750        Ok(code) => match archive_message_sync(message, data, &event.data) {
751            Ok(()) => code,
752            Err(error) => archive_message_code(&error),
753        },
754        Err(error) => archive_message_code(&error),
755    }
756}
757
758impl ArchiveStream {
759    /// Wraps `AAExtractArchiveOutputStreamOpen`.
760    pub fn extract_output_with_messages<T: EntryMessageHandler + 'static>(
761        dir: &str,
762        flags: ArchiveFlags,
763        n_threads: i32,
764        handler: T,
765    ) -> Result<Self> {
766        let dir = util::cstring("dir", dir)?;
767        let mut message_handler = Box::new(ArchiveMessageState {
768            handler: Box::new(handler),
769        });
770        let handle = unsafe {
771            ffi::aa_archive_stream::compression_rs_aa_extract_archive_output_stream_open_with_messages(
772                dir.as_ptr(),
773                flags.bits(),
774                n_threads,
775                std::ptr::addr_of_mut!(*message_handler).cast::<c_void>(),
776                Some(archive_entry_message_proc),
777            )
778        };
779        Ok(Self {
780            handle: util::nonnull_handle(handle, "AAExtractArchiveOutputStreamOpen")?,
781            _upstream: None,
782            closed: false,
783            _message_handler: Some(message_handler),
784        })
785    }
786
787    /// Wraps `AAEncodeArchiveOutputStreamOpen`.
788    pub fn encode_output_with_messages<T: EntryMessageHandler + 'static>(
789        stream: ByteStream,
790        flags: ArchiveFlags,
791        n_threads: i32,
792        handler: T,
793    ) -> Result<Self> {
794        let mut message_handler = Box::new(ArchiveMessageState {
795            handler: Box::new(handler),
796        });
797        let handle = unsafe {
798            ffi::aa_archive_stream::compression_rs_aa_encode_archive_output_stream_open_with_messages(
799                stream.as_ptr(),
800                flags.bits(),
801                n_threads,
802                std::ptr::addr_of_mut!(*message_handler).cast::<c_void>(),
803                Some(archive_entry_message_proc),
804            )
805        };
806        Ok(Self {
807            handle: util::nonnull_handle(handle, "AAEncodeArchiveOutputStreamOpen")?,
808            _upstream: Some(ArchiveStreamUpstream::Byte(Box::new(stream))),
809            closed: false,
810            _message_handler: Some(message_handler),
811        })
812    }
813
814    /// Wraps `AADecodeArchiveInputStreamOpen`.
815    pub fn decode_input_with_messages<T: EntryMessageHandler + 'static>(
816        stream: ByteStream,
817        flags: ArchiveFlags,
818        n_threads: i32,
819        handler: T,
820    ) -> Result<Self> {
821        let mut message_handler = Box::new(ArchiveMessageState {
822            handler: Box::new(handler),
823        });
824        let handle = unsafe {
825            ffi::aa_archive_stream::compression_rs_aa_decode_archive_input_stream_open_with_messages(
826                stream.as_ptr(),
827                flags.bits(),
828                n_threads,
829                std::ptr::addr_of_mut!(*message_handler).cast::<c_void>(),
830                Some(archive_entry_message_proc),
831            )
832        };
833        Ok(Self {
834            handle: util::nonnull_handle(handle, "AADecodeArchiveInputStreamOpen")?,
835            _upstream: Some(ArchiveStreamUpstream::Byte(Box::new(stream))),
836            closed: false,
837            _message_handler: Some(message_handler),
838        })
839    }
840
841    /// Wraps `AAConvertArchiveOutputStreamOpen`.
842    pub fn convert_output_with_messages<T: EntryMessageHandler + 'static>(
843        stream: ArchiveStream,
844        insert_key_set: &FieldKeySet,
845        remove_key_set: &FieldKeySet,
846        flags: ArchiveFlags,
847        n_threads: i32,
848        handler: T,
849    ) -> Result<Self> {
850        let mut message_handler = Box::new(ArchiveMessageState {
851            handler: Box::new(handler),
852        });
853        let handle = unsafe {
854            ffi::aa_archive_stream::compression_rs_aa_convert_archive_output_stream_open_with_messages(
855                stream.as_ptr(),
856                insert_key_set.as_ptr(),
857                remove_key_set.as_ptr(),
858                flags.bits(),
859                n_threads,
860                std::ptr::addr_of_mut!(*message_handler).cast::<c_void>(),
861                Some(archive_entry_message_proc),
862            )
863        };
864        Ok(Self {
865            handle: util::nonnull_handle(handle, "AAConvertArchiveOutputStreamOpen")?,
866            _upstream: Some(ArchiveStreamUpstream::Archive(Box::new(stream))),
867            closed: false,
868            _message_handler: Some(message_handler),
869        })
870    }
871
872    /// Wraps `AAArchiveStreamWritePathList`.
873    pub fn write_path_list_with_messages<T: EntryMessageHandler + 'static>(
874        &mut self,
875        path_list: &PathList,
876        key_set: &FieldKeySet,
877        dir: &str,
878        flags: ArchiveFlags,
879        n_threads: i32,
880        handler: T,
881    ) -> Result<()> {
882        self.ensure_open()?;
883        let dir = util::cstring("dir", dir)?;
884        let mut message_handler = Box::new(ArchiveMessageState {
885            handler: Box::new(handler),
886        });
887        let status = unsafe {
888            ffi::aa_archive_stream::compression_rs_aa_archive_stream_write_path_list_with_messages(
889                self.as_ptr(),
890                path_list.as_ptr(),
891                key_set.as_ptr(),
892                dir.as_ptr(),
893                flags.bits(),
894                n_threads,
895                std::ptr::addr_of_mut!(*message_handler).cast::<c_void>(),
896                Some(archive_entry_message_proc),
897            )
898        };
899        util::status_result("AAArchiveStreamWritePathList", status)
900    }
901
902    /// Wraps `AAArchiveStreamProcess`.
903    pub fn process_into_with_messages<T: EntryMessageHandler + 'static>(
904        &mut self,
905        output: &mut Self,
906        flags: ArchiveFlags,
907        n_threads: i32,
908        handler: T,
909    ) -> Result<u64> {
910        self.ensure_open()?;
911        output.ensure_open()?;
912        let mut message_handler = Box::new(ArchiveMessageState {
913            handler: Box::new(handler),
914        });
915        util::off_t_result("AAArchiveStreamProcess", unsafe {
916            ffi::aa_archive_stream::compression_rs_aa_archive_stream_process_with_messages(
917                self.as_ptr(),
918                output.as_ptr(),
919                flags.bits(),
920                n_threads,
921                std::ptr::addr_of_mut!(*message_handler).cast::<c_void>(),
922                Some(archive_entry_message_proc),
923            )
924        })
925    }
926}