use std::ffi::{c_char, c_void, CStr};
use std::os::raw::c_long;
pub type CFTypeRef = *const c_void;
pub type CFAllocatorRef = *const c_void;
pub type CFStringRef = *const c_void;
pub type CFNumberRef = *const c_void;
pub type CFArrayRef = *const c_void;
pub type CFDictionaryRef = *const c_void;
pub type CFIndex = c_long;
pub type CFNumberType = CFIndex;
pub const kCFNumberSInt32Type: CFNumberType = 3;
pub const kCFStringEncodingUTF8: u32 = 0x08000100;
#[link(name = "CoreFoundation", kind = "framework")]
extern "C" {
pub fn CFRelease(cf: CFTypeRef);
pub fn CFArrayGetCount(theArray: CFArrayRef) -> CFIndex;
pub fn CFArrayGetValueAtIndex(theArray: CFArrayRef, idx: CFIndex) -> *const c_void;
pub fn CFDictionaryGetValue(
theDict: CFDictionaryRef,
key: *const c_void,
) -> *const c_void;
pub fn CFNumberGetValue(
number: CFNumberRef,
theType: CFNumberType,
valuePtr: *mut c_void,
) -> bool;
pub fn CFStringCreateWithCString(
alloc: CFAllocatorRef,
cStr: *const c_char,
encoding: u32,
) -> CFStringRef;
pub fn CFStringGetCStringPtr(s: CFStringRef, encoding: u32) -> *const c_char;
pub fn CFStringGetCString(
s: CFStringRef,
buffer: *mut c_char,
buffer_size: CFIndex,
encoding: u32,
) -> bool;
pub fn CFStringGetLength(s: CFStringRef) -> CFIndex;
}
pub struct CFOwned(CFTypeRef);
impl CFOwned {
pub unsafe fn from_create(ptr: CFTypeRef) -> Option<Self> {
if ptr.is_null() {
None
} else {
Some(Self(ptr))
}
}
pub fn as_ptr(&self) -> CFTypeRef {
self.0
}
}
impl Drop for CFOwned {
fn drop(&mut self) {
unsafe { CFRelease(self.0) }
}
}
pub fn cfstring_from_cstr(s: &CStr) -> Option<CFOwned> {
unsafe {
let raw = CFStringCreateWithCString(std::ptr::null(), s.as_ptr(), kCFStringEncodingUTF8);
CFOwned::from_create(raw as CFTypeRef)
}
}
pub unsafe fn dict_get_i32(dict: CFDictionaryRef, key: &CStr) -> Option<i32> {
let key_cf = cfstring_from_cstr(key)?;
let raw = CFDictionaryGetValue(dict, key_cf.as_ptr());
if raw.is_null() {
return None;
}
let mut out: i32 = 0;
let ok = CFNumberGetValue(
raw as CFNumberRef,
kCFNumberSInt32Type,
&mut out as *mut i32 as *mut c_void,
);
if ok {
Some(out)
} else {
None
}
}
pub unsafe fn dict_get_string(dict: CFDictionaryRef, key: &CStr) -> Option<String> {
let key_cf = cfstring_from_cstr(key)?;
let raw = CFDictionaryGetValue(dict, key_cf.as_ptr());
if raw.is_null() {
return None;
}
cfstring_to_string(raw as CFStringRef)
}
pub unsafe fn dict_get_dict(dict: CFDictionaryRef, key: &str) -> CFDictionaryRef {
let Ok(c) = std::ffi::CString::new(key) else {
return std::ptr::null();
};
let Some(key_cf) = cfstring_from_cstr(&c) else {
return std::ptr::null();
};
CFDictionaryGetValue(dict, key_cf.as_ptr()) as CFDictionaryRef
}
pub unsafe fn cfstring_to_string(s: CFStringRef) -> Option<String> {
if s.is_null() {
return None;
}
let fast = CFStringGetCStringPtr(s, kCFStringEncodingUTF8);
if !fast.is_null() {
return Some(CStr::from_ptr(fast).to_string_lossy().into_owned());
}
let len = CFStringGetLength(s);
let cap = (len * 4 + 1) as usize;
let mut buf = vec![0u8; cap];
let ok = CFStringGetCString(
s,
buf.as_mut_ptr() as *mut c_char,
cap as CFIndex,
kCFStringEncodingUTF8,
);
if !ok {
return None;
}
let cstr = CStr::from_ptr(buf.as_ptr() as *const c_char);
Some(cstr.to_string_lossy().into_owned())
}