use std::mem;
use anyhow::{anyhow, bail};
use core_foundation::{
base::{CFType, TCFType, ToVoid, kCFAllocatorDefault},
dictionary::{
CFDictionary, CFDictionaryGetTypeID, CFDictionaryRef, CFMutableDictionary,
CFMutableDictionaryRef,
},
number::{CFNumber, CFNumberGetTypeID},
string::{CFString, CFStringGetTypeID},
};
use mach2::kern_return;
use super::bindings::*;
#[derive(Debug)]
pub struct IoObject(io_object_t);
impl IoObject {
pub fn properties(&self) -> anyhow::Result<CFDictionary<CFString, CFType>> {
unsafe {
let mut props = mem::MaybeUninit::<CFMutableDictionaryRef>::uninit();
let result = IORegistryEntryCreateCFProperties(
self.0,
props.as_mut_ptr(),
kCFAllocatorDefault,
0,
);
if result != kern_return::KERN_SUCCESS {
bail!("IORegistryEntryCreateCFProperties failed, error code {result}.")
} else {
let props = props.assume_init();
Ok(CFMutableDictionary::wrap_under_create_rule(props).to_immutable())
}
}
}
pub fn service_parent(&self) -> anyhow::Result<IoObject> {
let mut parent: io_registry_entry_t = 0;
let result = unsafe {
IORegistryEntryGetParentEntry(self.0, kIOServicePlane.as_ptr().cast(), &mut parent)
};
if result != kern_return::KERN_SUCCESS {
bail!("IORegistryEntryGetParentEntry failed, error code {result}.")
} else {
Ok(parent.into())
}
}
}
impl From<io_object_t> for IoObject {
fn from(obj: io_object_t) -> IoObject {
IoObject(obj)
}
}
impl Drop for IoObject {
fn drop(&mut self) {
let result = unsafe { IOObjectRelease(self.0) };
assert_eq!(result, kern_return::KERN_SUCCESS);
}
}
pub fn get_dict(
dict: &CFDictionary<CFString, CFType>, raw_key: &'static str,
) -> anyhow::Result<CFDictionary<CFString, CFType>> {
let key = CFString::from_static_string(raw_key);
dict.find(&key)
.map(|value_ref| {
unsafe {
debug_assert!(value_ref.type_of() == CFDictionaryGetTypeID());
}
let ptr = value_ref.to_void() as CFDictionaryRef;
unsafe { CFDictionary::wrap_under_get_rule(ptr) }
})
.ok_or_else(|| anyhow!("missing key"))
}
pub fn get_i64(
dict: &CFDictionary<CFString, CFType>, raw_key: &'static str,
) -> anyhow::Result<i64> {
let key = CFString::from_static_string(raw_key);
dict.find(&key)
.and_then(|value_ref| {
unsafe {
debug_assert!(value_ref.type_of() == CFNumberGetTypeID());
}
value_ref.downcast::<CFNumber>()
})
.and_then(|number| number.to_i64())
.ok_or_else(|| anyhow!("missing key"))
}
pub fn get_string(
dict: &CFDictionary<CFString, CFType>, raw_key: &'static str,
) -> anyhow::Result<String> {
let key = CFString::from_static_string(raw_key);
dict.find(&key)
.and_then(|value_ref| {
unsafe {
debug_assert!(value_ref.type_of() == CFStringGetTypeID());
}
value_ref.downcast::<CFString>()
})
.map(|cf_string| cf_string.to_string())
.ok_or_else(|| anyhow!("missing key"))
}