use core_foundation::base::{CFType, TCFType};
use core_foundation::boolean::CFBoolean;
use core_foundation::dictionary::{CFDictionary, CFMutableDictionaryRef};
use core_foundation::number::CFNumber;
use core_foundation::string::CFString;
use crate::sampler::BatterySnap;
#[link(name = "IOKit", kind = "framework")]
extern "C" {
fn IOServiceMatching(name: *const libc::c_char) -> CFMutableDictionaryRef;
fn IOServiceGetMatchingService(port: u32, matching: CFMutableDictionaryRef) -> u32;
fn IORegistryEntryCreateCFProperties(
entry: u32,
props: *mut CFMutableDictionaryRef,
allocator: *const libc::c_void,
options: u32,
) -> i32;
fn IOObjectRelease(obj: u32) -> i32;
}
fn get_i64(d: &CFDictionary<CFString, CFType>, key: &str) -> Option<i64> {
d.find(CFString::new(key))
.and_then(|v| v.downcast::<CFNumber>())
.and_then(|n| n.to_i64())
}
pub fn read() -> Option<BatterySnap> {
unsafe {
let matching = IOServiceMatching(c"AppleSmartBattery".as_ptr());
if matching.is_null() {
return None;
}
let service = IOServiceGetMatchingService(0, matching); if service == 0 {
return None;
}
let mut props: CFMutableDictionaryRef = std::ptr::null_mut();
let rc = IORegistryEntryCreateCFProperties(service, &mut props, std::ptr::null(), 0);
IOObjectRelease(service);
if rc != 0 || props.is_null() {
return None;
}
let dict: CFDictionary<CFString, CFType> =
CFDictionary::wrap_under_create_rule(props as _);
let percent = get_i64(&dict, "CurrentCapacity")?.clamp(0, 100) as u8; let cycles = get_i64(&dict, "CycleCount").unwrap_or(0) as u32;
let charging = dict
.find(CFString::new("IsCharging"))
.and_then(|v| v.downcast::<CFBoolean>())
.map(bool::from)
.unwrap_or(false);
let health_pct = match (
get_i64(&dict, "NominalChargeCapacity"),
get_i64(&dict, "DesignCapacity"),
) {
(Some(n), Some(d)) if d > 0 => Some(((n * 100) / d).min(100) as u8),
_ => None,
};
Some(BatterySnap { percent, charging, cycles, health_pct })
}
}
#[cfg(test)]
mod tests {
#[test]
fn battery_reads_or_is_absent() {
if let Some(b) = super::read() {
assert!(b.percent <= 100);
assert!(b.cycles < 10_000);
}
}
}