compression-rs 0.2.0

Safe Rust bindings for Apple's Compression and AppleArchive APIs on macOS
Documentation
use crate::{
    aa_byte_stream::{ArchiveFlags, ByteStream},
    aa_entry_stream::PathList,
    aa_field_key::{FieldKey, FieldKeySet},
    aa_header::Header,
    ffi, util, CompressionError, Result,
};
use std::ffi::c_void;
use std::ptr::NonNull;

#[allow(dead_code)]
#[derive(Debug)]
enum ArchiveStreamUpstream {
    Byte(Box<ByteStream>),
    Archive(Box<ArchiveStream>),
}

#[derive(Debug)]
pub struct ArchiveStream {
    handle: NonNull<c_void>,
    _upstream: Option<ArchiveStreamUpstream>,
    closed: bool,
}

impl ArchiveStream {
    pub fn extract_output(dir: &str, flags: ArchiveFlags, n_threads: i32) -> Result<Self> {
        let dir = util::cstring("dir", dir)?;
        let handle = unsafe {
            ffi::aa_archive_stream::compression_rs_aa_extract_archive_output_stream_open(
                dir.as_ptr(),
                flags.bits(),
                n_threads,
            )
        };
        Ok(Self {
            handle: util::nonnull_handle(handle, "AAExtractArchiveOutputStreamOpen")?,
            _upstream: None,
            closed: false,
        })
    }

    pub fn encode_output(stream: ByteStream, flags: ArchiveFlags, n_threads: i32) -> Result<Self> {
        let handle = unsafe {
            ffi::aa_archive_stream::compression_rs_aa_encode_archive_output_stream_open(
                stream.as_ptr(),
                flags.bits(),
                n_threads,
            )
        };
        Ok(Self {
            handle: util::nonnull_handle(handle, "AAEncodeArchiveOutputStreamOpen")?,
            _upstream: Some(ArchiveStreamUpstream::Byte(Box::new(stream))),
            closed: false,
        })
    }

    pub fn decode_input(stream: ByteStream, flags: ArchiveFlags, n_threads: i32) -> Result<Self> {
        let handle = unsafe {
            ffi::aa_archive_stream::compression_rs_aa_decode_archive_input_stream_open(
                stream.as_ptr(),
                flags.bits(),
                n_threads,
            )
        };
        Ok(Self {
            handle: util::nonnull_handle(handle, "AADecodeArchiveInputStreamOpen")?,
            _upstream: Some(ArchiveStreamUpstream::Byte(Box::new(stream))),
            closed: false,
        })
    }

    pub fn convert_output(
        stream: ArchiveStream,
        insert_key_set: &FieldKeySet,
        remove_key_set: &FieldKeySet,
        flags: ArchiveFlags,
        n_threads: i32,
    ) -> Result<Self> {
        let handle = unsafe {
            ffi::aa_archive_stream::compression_rs_aa_convert_archive_output_stream_open(
                stream.as_ptr(),
                insert_key_set.as_ptr(),
                remove_key_set.as_ptr(),
                flags.bits(),
                n_threads,
            )
        };
        Ok(Self {
            handle: util::nonnull_handle(handle, "AAConvertArchiveOutputStreamOpen")?,
            _upstream: Some(ArchiveStreamUpstream::Archive(Box::new(stream))),
            closed: false,
        })
    }

    pub(crate) fn as_ptr(&self) -> *mut c_void {
        self.handle.as_ptr()
    }

    fn ensure_open(&self) -> Result<()> {
        if self.closed {
            Err(CompressionError::Closed {
                resource: "archive stream",
            })
        } else {
            Ok(())
        }
    }

    pub fn write_header(&mut self, header: &Header) -> Result<()> {
        self.ensure_open()?;
        let status = unsafe {
            ffi::aa_archive_stream::compression_rs_aa_archive_stream_write_header(
                self.as_ptr(),
                header.as_ptr(),
            )
        };
        util::status_result("AAArchiveStreamWriteHeader", status)
    }

    pub fn write_blob(&mut self, key: FieldKey, buffer: &[u8]) -> Result<()> {
        self.ensure_open()?;
        let status = unsafe {
            ffi::aa_archive_stream::compression_rs_aa_archive_stream_write_blob(
                self.as_ptr(),
                key.raw(),
                buffer.as_ptr(),
                buffer.len(),
            )
        };
        util::status_result("AAArchiveStreamWriteBlob", status)
    }

    pub fn read_header(&mut self) -> Result<Option<Header>> {
        self.ensure_open()?;
        let mut status = 0_i32;
        let handle = unsafe {
            ffi::aa_archive_stream::compression_rs_aa_archive_stream_read_header_new(
                self.as_ptr(),
                &mut status,
            )
        };
        match status {
            1 => Ok(Some(Header::from_handle(
                handle,
                "AAArchiveStreamReadHeader",
            )?)),
            0 => Ok(None),
            code => Err(CompressionError::OperationFailed {
                operation: "AAArchiveStreamReadHeader",
                code,
            }),
        }
    }

    pub fn read_header_into(&mut self, header: &mut Header) -> Result<bool> {
        self.ensure_open()?;
        match unsafe {
            ffi::aa_archive_stream::compression_rs_aa_archive_stream_read_header_into(
                self.as_ptr(),
                header.as_ptr(),
            )
        } {
            1 => Ok(true),
            0 => Ok(false),
            code => Err(CompressionError::OperationFailed {
                operation: "AAArchiveStreamReadHeader",
                code,
            }),
        }
    }

    pub fn read_blob(&mut self, key: FieldKey, buffer: &mut [u8]) -> Result<()> {
        self.ensure_open()?;
        let status = unsafe {
            ffi::aa_archive_stream::compression_rs_aa_archive_stream_read_blob(
                self.as_ptr(),
                key.raw(),
                buffer.as_mut_ptr(),
                buffer.len(),
            )
        };
        util::status_result("AAArchiveStreamReadBlob", status)
    }

    pub fn write_path_list(
        &mut self,
        path_list: &PathList,
        key_set: &FieldKeySet,
        dir: &str,
        flags: ArchiveFlags,
        n_threads: i32,
    ) -> Result<()> {
        self.ensure_open()?;
        let dir = util::cstring("dir", dir)?;
        let status = unsafe {
            ffi::aa_archive_stream::compression_rs_aa_archive_stream_write_path_list(
                self.as_ptr(),
                path_list.as_ptr(),
                key_set.as_ptr(),
                dir.as_ptr(),
                flags.bits(),
                n_threads,
            )
        };
        util::status_result("AAArchiveStreamWritePathList", status)
    }

    pub fn process_into(
        &mut self,
        output: &mut Self,
        flags: ArchiveFlags,
        n_threads: i32,
    ) -> Result<u64> {
        self.ensure_open()?;
        output.ensure_open()?;
        util::off_t_result("AAArchiveStreamProcess", unsafe {
            ffi::aa_archive_stream::compression_rs_aa_archive_stream_process(
                self.as_ptr(),
                output.as_ptr(),
                flags.bits(),
                n_threads,
            )
        })
    }

    pub fn cancel(&mut self) -> Result<()> {
        self.ensure_open()?;
        unsafe { ffi::aa_archive_stream::compression_rs_aa_archive_stream_cancel(self.as_ptr()) };
        Ok(())
    }

    pub fn close(&mut self) -> Result<()> {
        if self.closed {
            return Ok(());
        }
        let status = unsafe {
            ffi::aa_archive_stream::compression_rs_aa_archive_stream_close(self.as_ptr())
        };
        self.closed = true;
        util::status_result("AAArchiveStreamClose", status)
    }
}

impl Drop for ArchiveStream {
    fn drop(&mut self) {
        unsafe { ffi::aa_archive_stream::compression_rs_aa_archive_stream_release(self.as_ptr()) };
    }
}