zip7 0.1.0

ZIP unpacking bindings from 7z
Documentation
use std::ffi::CString;
use std::path::Path;
use std::ptr::{self, NonNull};
use std::sync::{Arc, Mutex, OnceLock};

use crate::error::{Result, Zip7Error};
pub use crate::item::Zip7Item;

mod error;
mod item;

static INIT: OnceLock<()> = OnceLock::new();

#[derive(Debug)]
pub struct Handle(NonNull<zip7_sys::Handle>);

unsafe impl Send for Handle {}
unsafe impl Sync for Handle {}

impl Drop for Handle {
    fn drop(&mut self) {
        unsafe { zip7_sys::close_archive(self.0.as_ptr()) };
    }
}

#[derive(Debug)]
pub struct Zip7Archive {
    handle: Arc<Mutex<Handle>>,
    item_index: usize,
    item_count: usize,
}

impl Zip7Archive {
    pub fn new<P>(path: P, password: Option<&[u8]>) -> Result<Self>
    where
        P: AsRef<Path>,
    {
        INIT.get_or_init(|| unsafe { zip7_sys::init() });

        let mut handle = ptr::null_mut();
        let path = CString::new(path.as_ref().to_string_lossy().as_bytes())?;
        let password = if let Some(pw) = password {
            CString::new(pw)?
        } else {
            CString::default()
        };
        let password_ptr = if password.is_empty() {
            ptr::null()
        } else {
            password.as_ptr()
        };
        let res = unsafe { zip7_sys::open_archive(path.as_ptr(), password_ptr, &mut handle) };
        if res != 0 {
            return Err(res.into());
        }
        let handle = NonNull::new(handle).ok_or(Zip7Error::NullHandle)?;

        let item_count = unsafe { zip7_sys::items_count(handle.as_ptr()) } as usize;

        Ok(Self {
            handle: Arc::new(Mutex::new(Handle(handle))),
            item_index: 0,
            item_count,
        })
    }

    pub fn extract(&mut self) -> Result<Vec<Zip7Item>> {
        let code = {
            let handle = self.handle.lock().unwrap();
            let handle_ptr = handle.0.as_ptr();
            unsafe { zip7_sys::extract(handle_ptr) }
        };
        if code != 0 {
            return Err(code.into());
        }
        self.item_index = 0;
        Ok(self.into_iter().filter_map(Into::into).collect())
    }

    pub fn len(&self) -> usize {
        self.item_count
    }

    pub fn is_empty(&self) -> bool {
        self.item_count == 0
    }
}

impl Iterator for &mut Zip7Archive {
    type Item = Zip7Item;

    fn next(&mut self) -> Option<Self::Item> {
        if self.item_index >= self.item_count {
            return None;
        }
        let index = self.item_index;
        self.item_index += 1;

        Some(Zip7Item::new(Arc::clone(&self.handle), index))
    }
}