use std::ffi::{CStr, CString};
use std::marker::PhantomData;
use std::os::raw::{c_char, c_int};
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::ptr::NonNull;
use anyhow::{Context, Result, anyhow};
#[repr(C)]
pub struct WimStruct {
_private: [u8; 0],
}
const WIMLIB_INIT_FLAG_ASSUME_UTF8: c_int = 0x0000_0001;
#[link(name = "wim")]
unsafe extern "C" {
fn wimlib_global_init(init_flags: c_int) -> c_int;
fn wimlib_global_cleanup();
fn wimlib_open_wim(
wim_file: *const c_char,
open_flags: c_int,
wim_ret: *mut *mut WimStruct,
) -> c_int;
fn wimlib_split(
wim: *mut WimStruct,
swm_name: *const c_char,
part_size: u64,
write_flags: c_int,
) -> c_int;
fn wimlib_free(wim: *mut WimStruct);
fn wimlib_get_error_string(code: c_int) -> *const c_char;
}
fn wim_error(code: c_int) -> anyhow::Error {
let message = unsafe {
let message = wimlib_get_error_string(code);
if message.is_null() {
format!("unknown wimlib error (code {code})")
} else {
CStr::from_ptr(message).to_string_lossy().into_owned()
}
};
anyhow!("wimlib: {message} (code {code})")
}
fn c_path(path: &Path) -> Result<CString> {
CString::new(path.as_os_str().as_bytes())
.with_context(|| format!("path contains an interior NUL: {}", path.display()))
}
pub struct WimLib;
impl WimLib {
pub fn new() -> Result<Self> {
let code = unsafe {
wimlib_global_init(WIMLIB_INIT_FLAG_ASSUME_UTF8)
};
if code != 0 {
return Err(wim_error(code));
}
Ok(Self)
}
}
impl Drop for WimLib {
fn drop(&mut self) {
unsafe {
wimlib_global_cleanup();
}
}
}
pub struct Wim<'lib> {
ptr: NonNull<WimStruct>,
_lib: PhantomData<&'lib WimLib>,
}
impl<'lib> Wim<'lib> {
pub fn open(_lib: &'lib WimLib, path: &Path) -> Result<Self> {
let path = c_path(path)?;
let mut ptr = std::ptr::null_mut();
let code = unsafe {
wimlib_open_wim(path.as_ptr(), 0, std::ptr::addr_of_mut!(ptr))
};
if code != 0 {
return Err(wim_error(code));
}
let ptr = NonNull::new(ptr).ok_or_else(|| anyhow!("wimlib returned a null WIM handle"))?;
Ok(Self {
ptr,
_lib: PhantomData,
})
}
pub fn split(&self, swm_path: &Path, part_size: u64) -> Result<()> {
let swm_path = c_path(swm_path)?;
let code = unsafe {
wimlib_split(self.ptr.as_ptr(), swm_path.as_ptr(), part_size, 0)
};
if code != 0 {
return Err(wim_error(code));
}
Ok(())
}
}
impl Drop for Wim<'_> {
fn drop(&mut self) {
unsafe {
wimlib_free(self.ptr.as_ptr());
}
}
}