unrar-async 0.1.12

List and extract .rar archives, async
Documentation
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
		}
	}
}

/*
impl<'a> Drop for OpenArchiveData<'a> {
	fn drop(&mut self) {
		self.comment_buffer.into_raw();
	}
}
*/

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
		})
	}
}

// SAFETY:  comment_buffer is the only !Send field, because it's a raw pointer; that pointer is
// always strictly null.
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl Send for HeaderData {}

/// A single entry contained in an archive - usually, a file or directory
#[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 {
	/// Whether or not an entry is split across multiple volumes in a multi-part archive
	#[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 RESERVED = 0x08;
		const SOLID = 0x10;
		const DIRECTORY = 0x20;
	}
}