use crate::ffi::{self, string::LibcString, transparent::TransparentNewtype};
use hwlocality_sys::hwloc_info_s;
#[allow(unused)]
#[cfg(test)]
use similar_asserts::assert_eq;
use std::{ffi::CStr, fmt, hash::Hash};
#[allow(clippy::non_send_fields_in_send_ty, missing_copy_implementations)]
#[doc(alias = "hwloc_info_s")]
#[repr(transparent)]
pub struct TextualInfo(hwloc_info_s);
impl TextualInfo {
#[allow(unused)]
pub(crate) unsafe fn borrow_raw(name: &LibcString, value: &LibcString) -> hwloc_info_s {
hwloc_info_s {
name: name.borrow().cast_mut(),
value: value.borrow().cast_mut(),
}
}
#[doc(alias = "hwloc_info_s::name")]
pub fn name(&self) -> &CStr {
unsafe { ffi::deref_str(&self.0.name) }.expect("Infos should have names")
}
#[doc(alias = "hwloc_info_s::value")]
pub fn value(&self) -> &CStr {
unsafe { ffi::deref_str(&self.0.value) }.expect("Infos should have values")
}
}
impl fmt::Debug for TextualInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TextualInfo")
.field("name", &self.name())
.field("value", &self.value())
.finish()
}
}
impl Eq for TextualInfo {}
impl Hash for TextualInfo {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.name().hash(state);
self.value().hash(state);
}
}
impl PartialEq for TextualInfo {
fn eq(&self, other: &Self) -> bool {
self.name() == other.name() && self.value() == other.value()
}
}
unsafe impl Send for TextualInfo {}
unsafe impl Sync for TextualInfo {}
unsafe impl TransparentNewtype for TextualInfo {
type Inner = hwloc_info_s;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ffi::transparent::AsNewtype;
use proptest::prelude::*;
#[allow(unused)]
use similar_asserts::assert_eq;
use static_assertions::{assert_impl_all, assert_not_impl_any};
use std::{
collections::hash_map::RandomState,
ffi::CString,
fmt::{Binary, Debug, Display, LowerExp, LowerHex, Octal, Pointer, UpperExp, UpperHex},
hash::{BuildHasher, Hasher},
io::{self, Read},
ops::Deref,
panic::UnwindSafe,
};
assert_impl_all!(TextualInfo:
Debug, Hash, Sized, Sync, Unpin, UnwindSafe
);
assert_not_impl_any!(TextualInfo:
Binary, Clone, Default, Deref, Display, Drop, IntoIterator,
LowerExp, LowerHex, Octal, PartialOrd, Pointer, Read, ToOwned,
UpperExp, UpperHex, fmt::Write, io::Write
);
proptest! {
#[test]
fn unary(name: LibcString, value: LibcString) {
let raw_info = unsafe { TextualInfo::borrow_raw(&name, &value) };
let info: &TextualInfo = unsafe { (&raw_info).as_newtype() };
prop_assert_eq!(info.0.name, name.borrow().cast_mut());
prop_assert_eq!(info.0.value, value.borrow().cast_mut());
let name_c = CString::new(name.as_ref()).unwrap();
let value_c = CString::new(value.as_ref()).unwrap();
prop_assert_eq!(&CString::from(info.name()), &name_c);
prop_assert_eq!(&CString::from(info.value()), &value_c);
prop_assert_eq!(
format!("{info:#?}"),
format!(
"TextualInfo {{\n \
name: {name_c:?},\n \
value: {value_c:?},\n\
}}",
)
);
let state = RandomState::new();
let mut expected_hasher = state.build_hasher();
name_c.hash(&mut expected_hasher);
value_c.hash(&mut expected_hasher);
let expected_hash = expected_hasher.finish();
let actual_hash = state.hash_one(info);
prop_assert_eq!(actual_hash, expected_hash);
}
#[test]
fn binary(name1: LibcString, name2: LibcString, value1: LibcString, value2: LibcString) {
let raw_info1 = unsafe { TextualInfo::borrow_raw(&name1, &value1) };
let info1: &TextualInfo = unsafe { (&raw_info1).as_newtype() };
let raw_info2 = unsafe { TextualInfo::borrow_raw(&name2, &value2) };
let info2: &TextualInfo = unsafe { (&raw_info2).as_newtype() };
prop_assert_eq!(info1 == info2, name1 == name2 && value1 == value2);
}
}
}