use std::ffi::CString;
use std::fmt;
use std::path::Path;
use std::ptr;
pub(crate) struct Handle(Box<unrar_sys::HANDLE>);
impl Handle {
pub fn from_ffi(handle: *mut unrar_sys::HANDLE) -> Self {
unsafe { Self(Box::from_raw(handle)) }
}
pub fn as_ffi(&self) -> unrar_sys::Handle {
&*self.0
}
}
impl fmt::Debug for Handle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Pointer::fmt(&self.0, f)
}
}
#[derive(Debug)]
pub(crate) struct OpenArchiveData {
pub archive_name: CString,
pub open_mode: u32,
pub open_result: u32,
pub comment_buffer: Option<CString>,
pub comment_buffer_size: u32,
pub comment_state: u32
}
impl OpenArchiveData {
pub fn new(archive_name: CString, open_mode: u32) -> Self {
Self{
archive_name,
open_mode,
open_result: 0,
comment_buffer: None,
comment_buffer_size: 0,
comment_state: 0
}
}
pub fn as_ffi(&self) -> unrar_sys::OpenArchiveData {
unrar_sys::OpenArchiveData{
archive_name: self.archive_name.as_ptr(),
open_mode: self.open_mode,
open_result: self.open_result,
comment_buffer: self.comment_buffer.as_ref().map(|s| s.as_ptr() as *mut _).unwrap_or(ptr::null_mut()),
comment_buffer_size: self.comment_buffer_size,
comment_size: self.comment_buffer.as_ref().map(|s| s.to_bytes().len() as u32).unwrap_or(0),
comment_state: self.comment_state
}
}
}
pub(crate) struct HeaderData(unrar_sys::HeaderData);
impl HeaderData {
fn inner(&self) -> &unrar_sys::HeaderData {
&self.0
}
}
impl Default for HeaderData {
fn default() -> Self {
Self(unrar_sys::HeaderData{
archive_name: [0; 260],
filename: [0; 260],
flags: 0,
pack_size: 0,
unp_size: 0,
host_os: 0,
file_crc: 0,
file_time: 0,
unp_ver: 0,
method: 0,
file_attr: 0,
comment_buffer: ptr::null_mut(),
comment_buffer_size: 0,
comment_size: 0,
comment_state: 0
})
}
}
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl Send for HeaderData {}
#[derive(Debug)]
pub struct Entry {
pub filename: String,
pub flags: Flags,
pub unpacked_size: u32,
pub file_crc: u32,
pub file_time: u32,
pub method: u32,
pub file_attr: u32,
pub next_volume: Option<String>
}
impl Entry {
#[inline]
pub fn is_split(&self) -> bool {
self.flags.contains(Flags::SPLIT_BEFORE) || self.flags.contains(Flags::SPLIT_AFTER)
}
#[inline]
pub fn is_directory(&self) -> bool {
self.flags.contains(Flags::DIRECTORY)
}
#[inline]
pub fn is_encrypted(&self) -> bool {
self.flags.contains(Flags::ENCRYPTED)
}
#[inline]
pub fn is_file(&self) -> bool {
!self.is_directory()
}
}
impl AsRef<Path> for Entry {
#[inline]
fn as_ref(&self) -> &Path {
self.filename.as_ref()
}
}
impl fmt::Display for Entry {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&self.filename)?;
if(self.is_directory()) {
f.write_str("/")?;
}
if(self.is_split()) {
f.write_str(" (partial)")?;
}
Ok(())
}
}
impl TryFrom<HeaderData> for Entry {
type Error = std::string::FromUtf8Error;
fn try_from(header: HeaderData) -> Result<Self, Self::Error> {
let inner = header.inner();
let filename_len = unsafe { libc::strlen(&inner.filename as *const _) };
let filename = inner.filename[..filename_len]
.iter()
.map(|&c| c as u8)
.collect::<Vec<_>>();
Ok(Self{
filename: String::from_utf8(filename)?,
flags: Flags::from_bits(inner.flags).unwrap(),
unpacked_size: inner.unp_size,
file_crc: inner.file_crc,
file_time: inner.file_time,
method: inner.method,
file_attr: inner.file_attr,
next_volume: None
})
}
}
bitflags::bitflags! {
pub struct Flags: u32 {
const SPLIT_BEFORE = 0x01;
const SPLIT_AFTER = 0x02;
const ENCRYPTED = 0x04;
const SOLID = 0x10;
const DIRECTORY = 0x20;
}
}