axfive-libzip 0.2.5

Bindings for system libzip
Documentation
use crate::ffi;
use std::borrow::{Borrow, BorrowMut};
use std::ffi::CStr;
use std::fmt;
use std::mem::zeroed;
use std::ops::{Deref, DerefMut};
use std::os::raw::c_int;

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum System {
    Sys(c_int),
    Zlib(c_int),
    Unknown(c_int),
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Zip {
    Changed,
    Close,
    CompressionNotSupported,
    CompressedDataInvalid,
    Crc,
    Deleted,
    EncryptionNotSupported,
    Eof,
    Exists,
    Inconsistent,
    Internal,
    InUse,
    InvalidArgument,
    Memory,
    Multidisk,
    NoSuchFile,
    NoPassword,
    NotZip,
    Open,
    OperationNotSupported,
    ReadOnly,
    Read,
    Remove,
    Rename,
    Seek,
    Tell,
    TempFile,
    Write,
    WrongPassword,
    ZipClosed,
    Zlib,
    Unknown,
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Error {
    pub system: Option<System>,
    pub zip: Option<Zip>,
    message: String,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Error: {}", self.message)
    }
}

impl std::error::Error for Error {}

pub type Result<T> = std::result::Result<T, Error>;

/// A containment structure for a zip_error_t.
/// This can be either Default instantiated for an owned version or borrowed with its From
/// implementation.
#[derive(Debug)]
pub(crate) struct ZipErrorT<T: Borrow<ffi::zip_error_t> + BorrowMut<ffi::zip_error_t>> {
    error: T,
    cleanup: bool,
}

impl Default for ZipErrorT<ffi::zip_error_t> {
    fn default() -> Self {
        unsafe {
            let mut handle: ffi::zip_error_t = zeroed();
            ffi::zip_error_init(&mut handle);
            ZipErrorT {
                error: handle,
                cleanup: true,
            }
        }
    }
}

impl From<c_int> for ZipErrorT<ffi::zip_error_t> {
    fn from(error: c_int) -> Self {
        unsafe {
            let mut handle: ffi::zip_error_t = zeroed();
            ffi::zip_error_init_with_code(&mut handle, error);
            ZipErrorT {
                error: handle,
                cleanup: true,
            }
        }
    }
}

impl<'a> From<&'a mut ffi::zip_error_t> for ZipErrorT<&'a mut ffi::zip_error_t> {
    fn from(error: &'a mut ffi::zip_error_t) -> Self {
        ZipErrorT {
            error,
            cleanup: false,
        }
    }
}

impl<T> ZipErrorT<T>
where
    T: Borrow<ffi::zip_error_t> + BorrowMut<ffi::zip_error_t>,
{
    pub fn system(&self) -> Option<System> {
        let system = unsafe { ffi::zip_error_system_type(self.deref()) };
        match system as _ {
            ffi::ZIP_ET_NONE => None,
            _ => {
                let code = unsafe { ffi::zip_error_code_system(self.deref()) };
                Some(match system as _ {
                    ffi::ZIP_ET_SYS => System::Sys(code),
                    ffi::ZIP_ET_ZLIB => System::Zlib(code),
                    _ => System::Unknown(code),
                })
            }
        }
    }
    pub fn zip(&self) -> Option<Zip> {
        let code = unsafe { ffi::zip_error_code_zip(self.deref()) };
        Some(match code as _ {
            ffi::ZIP_ER_CHANGED => Zip::Changed,
            ffi::ZIP_ER_CLOSE => Zip::Close,
            ffi::ZIP_ER_COMPNOTSUPP => Zip::CompressionNotSupported,
            ffi::ZIP_ER_COMPRESSED_DATA => Zip::CompressedDataInvalid,
            ffi::ZIP_ER_CRC => Zip::Crc,
            ffi::ZIP_ER_DELETED => Zip::Deleted,
            ffi::ZIP_ER_ENCRNOTSUPP => Zip::EncryptionNotSupported,
            ffi::ZIP_ER_EOF => Zip::Eof,
            ffi::ZIP_ER_EXISTS => Zip::Exists,
            ffi::ZIP_ER_INCONS => Zip::Inconsistent,
            ffi::ZIP_ER_INTERNAL => Zip::Internal,
            ffi::ZIP_ER_INUSE => Zip::InUse,
            ffi::ZIP_ER_INVAL => Zip::InvalidArgument,
            ffi::ZIP_ER_MEMORY => Zip::Memory,
            ffi::ZIP_ER_MULTIDISK => Zip::Multidisk,
            ffi::ZIP_ER_NOENT => Zip::NoSuchFile,
            ffi::ZIP_ER_NOPASSWD => Zip::NoPassword,
            ffi::ZIP_ER_NOZIP => Zip::NotZip,
            ffi::ZIP_ER_OK => return None,
            ffi::ZIP_ER_OPEN => Zip::Open,
            ffi::ZIP_ER_OPNOTSUPP => Zip::OperationNotSupported,
            ffi::ZIP_ER_RDONLY => Zip::ReadOnly,
            ffi::ZIP_ER_READ => Zip::Read,
            ffi::ZIP_ER_REMOVE => Zip::Remove,
            ffi::ZIP_ER_RENAME => Zip::Rename,
            ffi::ZIP_ER_SEEK => Zip::Seek,
            ffi::ZIP_ER_TELL => Zip::Tell,
            ffi::ZIP_ER_TMPOPEN => Zip::TempFile,
            ffi::ZIP_ER_WRITE => Zip::Write,
            ffi::ZIP_ER_WRONGPASSWD => Zip::WrongPassword,
            ffi::ZIP_ER_ZIPCLOSED => Zip::ZipClosed,
            ffi::ZIP_ER_ZLIB => Zip::Zlib,
            _ => Zip::Unknown,
        })
    }

    pub fn message(&mut self) -> &CStr {
        unsafe {
            let message = ffi::zip_error_strerror(self.deref_mut());
            assert!(!message.is_null());
            CStr::from_ptr(message)
        }
    }
}

impl<T> Drop for ZipErrorT<T>
where
    T: Borrow<ffi::zip_error_t> + BorrowMut<ffi::zip_error_t>,
{
    fn drop(&mut self) {
        if self.cleanup {
            unsafe { ffi::zip_error_fini(self.error.borrow_mut()) }
        }
    }
}

impl<T> Deref for ZipErrorT<T>
where
    T: Borrow<ffi::zip_error_t> + BorrowMut<ffi::zip_error_t>,
{
    type Target = ffi::zip_error_t;
    fn deref(&self) -> &Self::Target {
        self.error.borrow()
    }
}

impl<T> DerefMut for ZipErrorT<T>
where
    T: Borrow<ffi::zip_error_t> + BorrowMut<ffi::zip_error_t>,
{
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.error.borrow_mut()
    }
}

impl<T> From<ZipErrorT<T>> for Error
where
    T: Borrow<ffi::zip_error_t> + BorrowMut<ffi::zip_error_t>,
{
    fn from(mut error: ZipErrorT<T>) -> Self {
        let system = error.system();
        let zip = error.zip();
        let message = error.message().to_string_lossy().into_owned();
        Error {
            system,
            zip,
            message,
        }
    }
}