use {
crate::{string::TStr, sys, CompressionType, ExtractFlags, ImageIndex, UpdateCommand},
derive_more::{Deref, DerefMut},
std::{ffi, fmt::Debug, num::NonZero},
};
pub type ProgressCallback = dyn FnMut(&mut ProgressMsg) -> ProgressStatus;
pub(crate) unsafe extern "C" fn trampoline(
tag: sys::wimlib_progress_msg,
payload: *mut sys::wimlib_progress_info,
ctx: *mut ffi::c_void,
) -> sys::wimlib_progress_status {
unsafe {
let mut msg = ProgressMsg::from_raw(tag, payload).expect("Library returned invalid data");
let callback_ptr = ctx as *mut *mut ProgressCallback;
let status = (**callback_ptr)(&mut msg);
status as _
}
}
#[repr(u32)]
pub enum ProgressStatus {
Continue = sys::wimlib_progress_status_WIMLIB_PROGRESS_STATUS_CONTINUE,
Abort = sys::wimlib_progress_status_WIMLIB_PROGRESS_STATUS_ABORT,
}
#[derive(Debug)]
pub enum ProgressMsg<'a> {
ExtractImageBegin(ExtractMsg<'a>),
ExtractTreeBegin(ExtractMsg<'a>),
ExtractFileStructure(ExtractMsg<'a, ExtractFsOrMetadataExtras>),
ExtractStreams(ExtractMsg<'a>),
ExtractSpwmPartBegin(ExtractMsg<'a, ExtractSpwmPartBeginExtras>),
ExtractMetadata(ExtractMsg<'a, ExtractFsOrMetadataExtras>),
ExtractImageEnd(ExtractMsg<'a>),
ExtractTreeEnd(ExtractMsg<'a>),
ScanBegin(ScanMsg<'a>),
ScanDentry {
scan: ScanMsg<'a>,
cur_path: &'a TStr,
status: ScanDentryStatus,
},
ScanEnd(ScanMsg<'a>),
WriteStreams {
total_bytes: u64,
total_streams: u64,
completed_bytes: u64,
completed_streams: u64,
num_threads: u32,
compression_type: CompressionType,
total_parts: u32,
completed_parts: u32,
completed_compression_bytes: u64,
},
WriteMetadataBegin,
WriteMetadataEnd,
Rename {
from: &'a TStr,
to: &'a TStr,
},
VerifyIntegrity {
calc: IntegrityMsg,
filename: &'a TStr,
},
CalcIntegrity(IntegrityMsg),
SplitBeginPart(SplitMsg<'a>),
SplitEndPart(SplitMsg<'a>),
UpdateBeginCommand(UpdateMsg<'a>),
UpdateEndCommand(UpdateMsg<'a>),
ReplaceFileInWim {
path: &'a TStr,
},
WimbootExclude {
path_in_wim: &'a TStr,
extraction_path: &'a TStr,
},
#[cfg(feature = "mount")]
UnmountBegin {
mountpoint: &'a TStr,
mounted_wim: &'a TStr,
mounted_image: ImageIndex,
mount_flags: u32,
unmount_flags: u32,
},
DoneWithFile {
path_to_file: &'a TStr,
},
BeginVerifyImage(VerifyImageMsg<'a>),
EndVerifyImage(VerifyImageMsg<'a>),
VerifyStreams {
wimfile: &'a TStr,
total_streams: u64,
total_bytes: u64,
completed_streams: u64,
completed_bytes: u64,
},
TestFileExclusion {
path: &'a TStr,
will_exclude: &'a mut bool,
},
HandleError {
path: Option<&'a TStr>,
error_code: i32,
will_ignore: &'a mut bool,
},
}
#[derive(Clone, Copy, Debug, Deref, DerefMut, PartialEq, Eq)]
pub struct ExtractMsg<'a, Extras = ()> {
pub image: ImageIndex,
pub wimfile_name: Option<&'a TStr>,
pub image_name: &'a TStr,
pub target: &'a TStr,
pub total_bytes: u64,
pub completed_bytes: u64,
#[deref]
#[deref_mut]
pub extras: Extras,
extract_flags: ExtractFlags,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ExtractSpwmPartBeginExtras {
pub part_number: u32,
pub total_parts: u32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ExtractFsOrMetadataExtras {
pub current_file_count: u64,
pub end_file_count: u64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct IntegrityMsg {
pub total_bytes: u64,
pub completed_bytes: u64,
pub total_chunks: u32,
pub completed_chunks: u32,
pub chunk_size: u32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SplitMsg<'a> {
pub total_bytes: u64,
pub completed_bytes: u64,
pub cur_part_number: u32,
pub total_parts: u32,
pub part_name: &'a TStr,
}
impl SplitMsg<'_> {
pub fn from_raw(ffi: sys::wimlib_progress_info_wimlib_progress_info_split) -> Self {
Self {
total_bytes: ffi.total_bytes,
completed_bytes: ffi.completed_bytes,
cur_part_number: ffi.cur_part_number,
total_parts: ffi.total_parts,
part_name: unsafe { TStr::from_ptr(ffi.part_name) },
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ScanMsg<'a> {
pub source: &'a TStr,
pub target_path: &'a TStr,
pub num_dirs_scanned: u64,
pub num_nondirs_scanned: u64,
pub num_bytes_scanned: u64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ScanDentryStatus {
Ok,
Exluded,
Unsupported,
FixedSymlink,
NotFixedSymlink,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct UpdateMsg<'a> {
pub command: &'a UpdateCommand<'a>,
pub completed_commands: usize,
pub total_commands: usize,
}
impl<'a> UpdateMsg<'a> {
const fn from_raw(payload: sys::wimlib_progress_info_wimlib_progress_info_update) -> Self {
Self {
command: unsafe {
&*(payload.command.cast::<UpdateMsg>() as *const crate::UpdateCommand<'a>)
},
completed_commands: payload.completed_commands,
total_commands: payload.total_commands,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct VerifyImageMsg<'a> {
pub wimfile: &'a TStr,
pub total_images: u32,
pub current_image: u32,
}
impl ProgressMsg<'_> {
unsafe fn from_raw(
tag: sys::wimlib_progress_msg,
payload: *mut sys::wimlib_progress_info,
) -> Option<Self> {
let new = match tag {
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_BEGIN => {
let payload = unsafe { ExtractMsg::from_raw((*payload).extract)? };
Self::ExtractImageBegin(payload)
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_EXTRACT_TREE_BEGIN => {
let payload = unsafe { ExtractMsg::from_raw((*payload).extract)? };
Self::ExtractTreeBegin(payload)
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_EXTRACT_FILE_STRUCTURE => {
let payload =
unsafe { ExtractMsg::with_extract_fs_or_metadata_extras((*payload).extract)? };
Self::ExtractFileStructure(payload)
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_EXTRACT_STREAMS => {
let payload = unsafe { ExtractMsg::from_raw((*payload).extract)? };
Self::ExtractStreams(payload)
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_EXTRACT_METADATA => {
let payload =
unsafe { ExtractMsg::with_extract_fs_or_metadata_extras((*payload).extract)? };
Self::ExtractFileStructure(payload)
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_EXTRACT_IMAGE_END => {
let payload = unsafe { ExtractMsg::from_raw((*payload).extract)? };
Self::ExtractImageEnd(payload)
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_EXTRACT_TREE_END => {
let payload = unsafe { ExtractMsg::from_raw((*payload).extract)? };
Self::ExtractTreeEnd(payload)
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_SCAN_BEGIN => {
let payload = unsafe { ScanMsg::from_raw((*payload).scan) };
Self::ScanBegin(payload)
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_SCAN_DENTRY => {
let (raw, scan);
unsafe {
raw = (*payload).scan;
scan = ScanMsg::from_raw(raw);
}
Self::ScanDentry {
scan,
cur_path: unsafe { TStr::from_ptr(raw.cur_path) },
status: ScanDentryStatus::from_raw(raw.status)?,
}
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_SCAN_END => {
let payload = unsafe { ScanMsg::from_raw((*payload).scan) };
Self::ScanEnd(payload)
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_WRITE_STREAMS => {
let raw = unsafe { (*payload).write_streams };
Self::WriteStreams {
total_bytes: raw.total_bytes,
total_streams: raw.total_streams,
completed_bytes: raw.completed_bytes,
completed_streams: raw.completed_streams,
num_threads: raw.num_threads,
compression_type: CompressionType::from_raw(raw.compression_type),
total_parts: raw.total_parts,
completed_parts: raw.completed_parts,
completed_compression_bytes: raw.completed_compressed_bytes,
}
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_WRITE_METADATA_BEGIN => {
Self::WriteMetadataBegin
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_WRITE_METADATA_END => {
Self::WriteMetadataEnd
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_RENAME => unsafe {
let payload = (*payload).rename;
Self::Rename {
from: TStr::from_ptr(payload.from),
to: TStr::from_ptr(payload.to),
}
},
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_VERIFY_INTEGRITY => unsafe {
let raw = (*payload).integrity;
let filename = TStr::from_ptr(raw.filename);
let calc = IntegrityMsg::from_raw(raw);
Self::VerifyIntegrity { calc, filename }
},
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_CALC_INTEGRITY => {
let raw = unsafe { (*payload).integrity };
let payload = IntegrityMsg::from_raw(raw);
Self::CalcIntegrity(payload)
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_SPLIT_BEGIN_PART => {
let raw = unsafe { (*payload).split };
Self::SplitBeginPart(SplitMsg::from_raw(raw))
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_SPLIT_END_PART => {
let raw = unsafe { (*payload).split };
Self::SplitEndPart(SplitMsg::from_raw(raw))
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_UPDATE_BEGIN_COMMAND => {
let raw = unsafe { (*payload).update };
let payload = UpdateMsg::from_raw(raw);
Self::UpdateBeginCommand(payload)
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_UPDATE_END_COMMAND => {
let raw = unsafe { (*payload).update };
let payload = UpdateMsg::from_raw(raw);
Self::UpdateEndCommand(payload)
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_REPLACE_FILE_IN_WIM => {
let path = unsafe {
let raw = (*payload).replace.path_in_wim;
TStr::from_ptr(raw)
};
Self::ReplaceFileInWim { path }
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_WIMBOOT_EXCLUDE => unsafe {
let raw = (*payload).wimboot_exclude;
let path_in_wim = TStr::from_ptr(raw.path_in_wim);
let extraction_path = TStr::from_ptr(raw.extraction_path);
Self::WimbootExclude {
path_in_wim,
extraction_path,
}
},
#[cfg(feature = "mount")]
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_UNMOUNT_BEGIN => {
let (raw, mountpoint, mounted_wim);
unsafe {
raw = (*payload).unmount;
mountpoint = TStr::from_ptr(raw.mountpoint);
mounted_wim = TStr::from_ptr(raw.mounted_wim);
}
let mounted_image = NonZero::new(raw.mounted_image)?;
Self::UnmountBegin {
mountpoint,
mounted_wim,
mounted_image,
mount_flags: raw.mount_flags,
unmount_flags: raw.unmount_flags,
}
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_DONE_WITH_FILE => {
let path_to_file =
unsafe { TStr::from_ptr((*payload).done_with_file.path_to_file) };
Self::DoneWithFile { path_to_file }
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_BEGIN_VERIFY_IMAGE => {
let raw = unsafe { VerifyImageMsg::from_raw((*payload).verify_image) };
Self::BeginVerifyImage(raw)
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_END_VERIFY_IMAGE => {
let raw = unsafe { VerifyImageMsg::from_raw((*payload).verify_image) };
Self::BeginVerifyImage(raw)
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_VERIFY_STREAMS => {
let (raw, wimfile);
unsafe {
raw = (*payload).verify_streams;
wimfile = TStr::from_ptr(raw.wimfile);
}
Self::VerifyStreams {
wimfile,
total_streams: raw.total_streams,
total_bytes: raw.total_bytes,
completed_streams: raw.completed_streams,
completed_bytes: raw.completed_bytes,
}
}
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_TEST_FILE_EXCLUSION => unsafe {
let raw = (*payload).test_file_exclusion;
let path = TStr::from_ptr(raw.path);
let will_exclude = &mut (*payload).test_file_exclusion.will_exclude;
Self::TestFileExclusion { path, will_exclude }
},
sys::wimlib_progress_msg_WIMLIB_PROGRESS_MSG_HANDLE_ERROR => unsafe {
let raw = (*payload).handle_error;
let path = TStr::from_ptr_optional(raw.path);
let error_code = raw.error_code;
let will_ignore = &mut (*payload).handle_error.will_ignore;
Self::HandleError {
path,
error_code,
will_ignore,
}
},
_ => return None,
};
Some(new)
}
}
impl ExtractMsg<'_> {
unsafe fn from_raw(
payload: sys::wimlib_progress_info_wimlib_progress_info_extract,
) -> Option<Self> {
let (wimfile_name, image_name, target);
unsafe {
wimfile_name = TStr::from_ptr_optional(payload.wimfile_name);
image_name = TStr::from_ptr(payload.image_name);
target = TStr::from_ptr(payload.target);
}
let image = NonZero::new(payload.image)?;
Some(Self {
image,
extract_flags: ExtractFlags::from_bits(payload.extract_flags as _)?,
wimfile_name,
image_name,
target,
total_bytes: payload.total_bytes,
completed_bytes: payload.completed_bytes,
extras: (),
})
}
}
impl ExtractMsg<'_, ExtractFsOrMetadataExtras> {
unsafe fn with_extract_fs_or_metadata_extras(
payload: sys::wimlib_progress_info_wimlib_progress_info_extract,
) -> Option<Self> {
let payload_0 = unsafe { ExtractMsg::from_raw(payload)? };
let extras = ExtractFsOrMetadataExtras {
current_file_count: payload.current_file_count,
end_file_count: payload.end_file_count,
};
Some(payload_0.with_extras(extras))
}
}
impl<'a, Extras> ExtractMsg<'a, Extras> {
fn with_extras<NewExtras>(self, extras: NewExtras) -> ExtractMsg<'a, NewExtras> {
ExtractMsg {
image: self.image,
extract_flags: self.extract_flags,
wimfile_name: self.wimfile_name,
image_name: self.image_name,
target: self.target,
total_bytes: self.total_bytes,
completed_bytes: self.completed_bytes,
extras,
}
}
}
impl ScanMsg<'_> {
unsafe fn from_raw(payload: sys::wimlib_progress_info_wimlib_progress_info_scan) -> Self {
let (source, target_path);
unsafe {
source = TStr::from_ptr(payload.source);
target_path = TStr::from_ptr(payload.__bindgen_anon_1.wim_target_path);
};
Self {
source,
target_path,
num_dirs_scanned: payload.num_dirs_scanned,
num_nondirs_scanned: payload.num_nondirs_scanned,
num_bytes_scanned: payload.num_bytes_scanned,
}
}
}
impl ScanDentryStatus {
const fn from_raw(val: u32) -> Option<Self> {
let new = match val {
sys::wimlib_progress_info_wimlib_progress_info_scan_WIMLIB_SCAN_DENTRY_OK => Self::Ok,
sys::wimlib_progress_info_wimlib_progress_info_scan_WIMLIB_SCAN_DENTRY_EXCLUDED => Self::Exluded,
sys::wimlib_progress_info_wimlib_progress_info_scan_WIMLIB_SCAN_DENTRY_UNSUPPORTED => Self::Unsupported,
sys::wimlib_progress_info_wimlib_progress_info_scan_WIMLIB_SCAN_DENTRY_FIXED_SYMLINK => Self::FixedSymlink,
sys::wimlib_progress_info_wimlib_progress_info_scan_WIMLIB_SCAN_DENTRY_NOT_FIXED_SYMLINK => Self::NotFixedSymlink,
_ => return None,
};
Some(new)
}
}
impl IntegrityMsg {
const fn from_raw(payload: sys::wimlib_progress_info_wimlib_progress_info_integrity) -> Self {
Self {
total_bytes: payload.total_bytes,
completed_bytes: payload.completed_bytes,
total_chunks: payload.total_chunks,
completed_chunks: payload.completed_chunks,
chunk_size: payload.chunk_size,
}
}
}
impl VerifyImageMsg<'_> {
unsafe fn from_raw(
payload: sys::wimlib_progress_info_wimlib_progress_info_verify_image,
) -> Self {
let wimfile = unsafe { TStr::from_ptr(payload.wimfile) };
Self {
wimfile,
total_images: payload.total_images,
current_image: payload.current_image,
}
}
}