use crate::capi::{ret::IntoCReturn, utils};
use std::{mem, ptr};
use bytemuck::{Pod, TransparentWrapper, Zeroable};
use libc::c_int;
#[derive(Debug, Clone, Copy, TransparentWrapper)]
#[repr(transparent)]
pub struct CStringPtr {
ptr: *const u8,
}
impl Default for CStringPtr {
fn default() -> Self {
Self { ptr: ptr::null() }
}
}
impl From<&'static str> for CStringPtr {
fn from(s: &'static str) -> Self {
s.as_ptr().into()
}
}
impl From<*const u8> for CStringPtr {
fn from(ptr: *const u8) -> Self {
Self { ptr }
}
}
unsafe impl Zeroable for CStringPtr {}
unsafe impl Pod for CStringPtr {}
#[derive(Debug, Clone, Copy, Default, Pod, Zeroable)]
#[repr(C)]
pub struct VersionInfo {
version_string: CStringPtr,
}
const VERSION_STRING: &str = concat!(env!("CARGO_PKG_VERSION"), "\0");
impl VersionInfo {
pub(crate) fn new() -> Self {
Self {
version_string: VERSION_STRING.into(),
}
}
}
#[no_mangle]
pub unsafe extern "C" fn pathrs_version(dst: *mut VersionInfo, dst_size: usize) -> c_int {
{
let v = VersionInfo::new();
unsafe { utils::copy_into_extensible_struct(dst, dst_size, &v) }.map(|truncated| {
if truncated {
mem::size_of::<VersionInfo>() as isize
} else {
0isize }
})
}
.into_c_return()
}
utils::symver! {
fn pathrs_version <- (pathrs_version, version = "LIBPATHRS_0.2.5", default);
}
#[cfg(test)]
mod tests {
use super::*;
use std::{mem, ptr};
use bytemuck::TransparentWrapper;
use pretty_assertions::assert_eq;
#[test]
fn version() {
let mut info = VersionInfo::default();
let ret = unsafe { pathrs_version(&mut info as *mut _, mem::size_of::<VersionInfo>()) };
assert_eq!(
ret, 0,
"pathrs_version with sizeof(VersionInfo) should not indicate truncation",
);
assert_eq!(
TransparentWrapper::peel_ref(&info.version_string),
&VERSION_STRING.as_ptr(),
"pathrs_version should set info.version_string to VERSION_STRING pointer",
);
}
#[test]
fn version_zerosize() {
let mut info = VersionInfo::default();
let ret = unsafe { pathrs_version(&mut info as *mut _, 0) };
assert_eq!(
ret,
mem::size_of::<VersionInfo>() as i32,
"pathrs_version with zero size should indicate truncation",
);
assert_eq!(
TransparentWrapper::peel_ref(&info.version_string),
&ptr::null(),
"pathrs_version should not modify info.version_string with zero size",
);
}
#[test]
fn version_null() {
let ret = unsafe { pathrs_version(ptr::null_mut(), 0) };
assert_eq!(
ret,
mem::size_of::<VersionInfo>() as i32,
"pathrs_version with NULL and zero size should indicate truncation",
);
}
}