use std::ffi::{CStr, CString, NulError, OsStr};
use std::fs::File;
use std::io;
use std::mem::MaybeUninit;
use std::os::fd::AsRawFd;
use std::os::unix::ffi::OsStrExt;
use std::path::{Path, PathBuf};
use std::str::Utf8Error;
#[allow(unused_macros)]
#[macro_export]
#[doc(hidden)]
macro_rules! owning_ref_from_ptr {
($object_ref:expr, $output_ref_ty:ident, $ptr: ident) => {{
let mut obj_ptr = std::ptr::NonNull::from($object_ref.as_ref());
let boxed = Box::new($ptr);
let (boxed_ptr, item) = unsafe { <$output_ref_ty>::ref_from_boxed_ptr(boxed) };
unsafe { obj_ptr.as_mut().gc.push(boxed_ptr.into()) };
item
}};
}
#[allow(unused_macros)]
#[macro_export]
#[doc(hidden)]
macro_rules! owning_mut_from_ptr {
($object_ref:expr, $output_ref_ty:ident, $ptr: ident) => {{
let mut obj_ptr = std::ptr::NonNull::from($object_ref.as_ref());
let boxed = Box::new($ptr);
let (boxed_ptr, item) = unsafe { <$output_ref_ty>::mut_from_boxed_ptr(boxed) };
unsafe { obj_ptr.as_mut().gc.push(boxed_ptr.into()) };
item
}};
}
#[doc(hidden)]
pub fn as_ref_path_to_c_string<T>(path: T) -> Result<CString, NulError>
where
T: AsRef<Path>,
{
log::debug!(
"ffi_utils::as_ref_path_to_c_string converting `AsRef<Path>` to `CString`: {:?}",
path.as_ref()
);
CString::new(path.as_ref().as_os_str().as_bytes())
}
#[doc(hidden)]
pub fn as_ref_str_to_c_string<T>(string: T) -> Result<CString, NulError>
where
T: AsRef<str>,
{
let string: &str = string.as_ref();
log::debug!(
"ffi_utils::as_ref_str_to_c_string converting `&str` to `CString`: {:?}",
string
);
CString::new(string.as_bytes())
}
#[doc(hidden)]
pub fn as_ref_str_to_owned_c_char_array<T>(s: T) -> Result<MaybeUninit<*mut libc::c_char>, NulError>
where
T: AsRef<str>,
{
let s: &str = s.as_ref();
let c_string = CString::new(s)?;
let mut ptr = MaybeUninit::<*mut libc::c_char>::zeroed();
unsafe {
ptr.write(libc::strdup(c_string.as_ptr()));
}
Ok(ptr)
}
#[doc(hidden)]
pub fn const_c_char_array_to_path<'a>(ptr: *const libc::c_char) -> &'a Path {
unsafe {
log::debug!(
"ffi_utils::const_c_char_array_to_path_buf converting `*const libc::c_char` to `PathBuf`: {:?}",
CStr::from_ptr(ptr)
);
let bytes = CStr::from_ptr(ptr).to_bytes();
Path::new(OsStr::from_bytes(bytes))
}
}
#[doc(hidden)]
pub fn const_c_char_array_to_path_buf(ptr: *const libc::c_char) -> PathBuf {
unsafe {
log::debug!(
"ffi_utils::const_c_char_array_to_path_buf converting `*const libc::c_char` to `PathBuf`: {:?}",
CStr::from_ptr(ptr)
);
let bytes = CStr::from_ptr(ptr).to_bytes();
Path::new(OsStr::from_bytes(bytes)).to_path_buf()
}
}
#[doc(hidden)]
pub fn const_char_array_to_str_ref<'a>(ptr: *const libc::c_char) -> Result<&'a str, Utf8Error> {
let cstr = unsafe { CStr::from_ptr(ptr) };
log::debug!(
"ffi_utils::c_char_array_to_string converting `*libc::c_char` to `String`: {:?}",
cstr
);
cstr.to_str()
}
#[doc(hidden)]
pub fn c_char_array_to_string(ptr: *const libc::c_char) -> String {
let cstr = unsafe { CStr::from_ptr(ptr) };
log::debug!(
"ffi_utils::c_char_array_to_string converting `*libc::c_char` to `String`: {:?}",
cstr
);
String::from_utf8_lossy(cstr.to_bytes()).to_string()
}
#[doc(hidden)]
fn is_file_open_read_write(file: &File) -> io::Result<(bool, bool)> {
const RWMODE: libc::c_int = libc::O_RDONLY | libc::O_RDWR | libc::O_WRONLY;
unsafe {
let mode = match libc::fcntl(file.as_raw_fd(), libc::F_GETFL) {
-1 => {
log::debug!("ffi_utils::is_file_open_read_write failed to get file status flags");
Err(io::Error::last_os_error())
}
status_flags => {
log::debug!("ffi_utils::is_file_open_read_write got file status flags");
Ok(status_flags)
}
}?;
match mode & RWMODE {
libc::O_WRONLY => Ok((false, true)),
libc::O_RDONLY => Ok((true, false)),
libc::O_RDWR => Ok((true, true)),
_ => unreachable!("ffi_utils::is_file_open_read_write unsupported status flag"),
}
}
}
#[doc(hidden)]
pub fn is_open_read_only(file: &File) -> io::Result<bool> {
let state = is_file_open_read_write(file)? == (true, false);
log::debug!("ffi_utils::is_open_read_only value: {:?}", state);
Ok(state)
}
#[doc(hidden)]
pub fn is_open_read_write(file: &File) -> io::Result<bool> {
let state = is_file_open_read_write(file)? == (true, true);
log::debug!("ffi_utils::is_open_read_write value: {:?}", state);
Ok(state)
}
#[doc(hidden)]
pub fn is_open_write_only(file: &File) -> io::Result<bool> {
let state = is_file_open_read_write(file)? == (false, true);
log::debug!("ffi_utils::is_open_write_only value: {:?}", state);
Ok(state)
}
#[doc(hidden)]
fn c_file_stream_from(file: &File, mode: &CStr) -> io::Result<*mut libc::FILE> {
unsafe {
let mut ptr = MaybeUninit::<*mut libc::FILE>::zeroed();
ptr.write(libc::fdopen(file.as_raw_fd(), mode.as_ptr()));
match ptr.assume_init() {
ptr if ptr.is_null() => {
log::debug!("ffi_utils::c_file_stream_from fdopen returned a NULL pointer");
Err(io::Error::last_os_error())
}
file_ptr => {
log::debug!("ffi_utils::c_file_stream_from created FILE stream");
Ok(file_ptr)
}
}
}
}
#[doc(hidden)]
pub fn read_only_c_file_stream_from(file: &File) -> io::Result<*mut libc::FILE> {
let write_only = CString::new("r")?;
c_file_stream_from(file, write_only.as_c_str())
}
#[doc(hidden)]
pub fn write_only_c_file_stream_from(file: &File) -> io::Result<*mut libc::FILE> {
let write_only = CString::new("w")?;
c_file_stream_from(file, write_only.as_c_str())
}