use std::{
ffi::{CStr, CString, NulError},
path::Path,
};
#[macro_export]
macro_rules! impl_enum_conversion {
($ty:ty, $from:ty, $into:ty $(,)?) => {
const _: () = {
impl From<$from> for $into {
fn from(value: $from) -> Self {
let value = value as $ty;
<$into>::try_from(value).unwrap()
}
}
impl From<$into> for $from {
fn from(value: $into) -> Self {
let value = value.into();
unsafe { core::mem::transmute::<$ty, $from>(value) }
}
}
};
};
($from:ty, $into:ty $(,)?) => {
impl_enum_conversion!(u32, $from, $into);
};
}
#[macro_export]
macro_rules! assert_close {
($actual:expr, $expected:expr $(,)?) => {
$crate::assert_close!($actual, $expected, 1.0e-5);
};
($actual:expr, $expected:expr, $tolerance:expr $(,)?) => {{
let actual = $actual;
let expected = $expected;
let tolerance = $tolerance as f64;
assert_eq!(
actual.len(),
expected.len(),
"assert_close length mismatch: actual len {}, expected len {}",
actual.len(),
expected.len(),
);
for (index, (actual, expected)) in actual.iter().zip(expected.iter()).enumerate() {
let actual = *actual as f64;
let expected = *expected as f64;
let difference = (actual - expected).abs();
assert!(
difference <= tolerance,
"assert_close failed at index {index}: actual {actual}, expected {expected}, difference {difference}, tolerance {tolerance}",
);
}
}};
($actual:expr, $expected:expr, $tolerance:expr, $name:expr $(,)?) => {{
let actual = $actual as f64;
let expected = $expected as f64;
let tolerance = $tolerance as f64;
let difference = (actual - expected).abs();
assert!(
difference <= tolerance,
"assert_close failed for {}: actual {actual}, expected {expected}, difference {difference}, tolerance {tolerance}",
$name,
);
}};
}
pub fn path_to_cstring(path: &Path) -> Result<CString, NulError> {
CString::new(path.as_os_str().to_string_lossy().as_bytes())
}
pub fn string_from_c_chars(buffer: &[i8]) -> String {
if buffer.is_empty() {
return String::new();
}
unsafe {
CStr::from_ptr(buffer.as_ptr())
.to_string_lossy()
.into_owned()
}
}
pub unsafe fn string_from_c_ptr(ptr: *const i8) -> String {
if ptr.is_null() {
return String::new();
}
unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() }
}
pub fn copy_string_to_c_chars<const N: usize>(buffer: &mut [i8; N], value: &str) {
let bytes = value.as_bytes();
let len = bytes.len().min(N.saturating_sub(1));
for (slot, byte) in buffer.iter_mut().zip(bytes.iter().copied()).take(len) {
*slot = byte as i8;
}
}