use std::{ffi::c_void, fmt::Debug, marker::PhantomData, ptr::null};
use super::{CFAllocatorRef, CFIndex, CFSerializable, CFType};
pub type CFArrayRef = *const c_void;
#[derive(Clone)]
pub(crate) struct CFArray {
pub(crate) inner: CFType,
}
impl CFArray {
pub fn from_slice<E: CFSerializable>(s: &[E]) -> Option<Self> {
let data: Vec<*const c_void> = s.iter().map(|x| x.to_ptr()).collect();
let alloc = null();
let values = data.as_ptr();
let num_values = data.len() as CFIndex;
let callbacks = null();
let reference = unsafe { CFArrayCreate(alloc, values, num_values, callbacks) };
Some(Self { inner: CFType::from_create(reference)? })
}
pub fn from_ptr(reference: CFType) -> Self {
Self { inner: reference }
}
#[must_use]
pub fn len(&self) -> usize {
let count = unsafe { CFArrayGetCount(self.inner.ptr()) };
count as usize
}
#[must_use]
pub fn get_ptr(&self, index: usize) -> Option<*const c_void> {
if index >= self.len() {
return None;
}
let index = index as CFIndex;
let value = unsafe { CFArrayGetValueAtIndex(self.inner.ptr(), index) };
if value.is_null() {
return None;
}
Some(value)
}
#[must_use]
pub fn get<V: CFSerializable>(&self, index: usize) -> Option<V> {
let value = self.get_ptr(index)?;
Some(V::from_ptr(value))
}
pub fn iter<V: CFSerializable>(&self) -> CFArrayIterator<V> {
CFArrayIterator {
array: self.clone(),
index: 0,
_value: PhantomData,
}
}
}
impl Debug for CFArray {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CFArray")
.field("inner", &self.inner)
.field("length", &self.len())
.finish()
}
}
pub(crate) struct CFArrayIterator<V> {
array: CFArray,
index: usize,
_value: PhantomData<V>,
}
impl<V: CFSerializable> Iterator for CFArrayIterator<V> {
type Item = V;
fn next(&mut self) -> Option<Self::Item> {
let value = self.array.get(self.index)?;
self.index += 1;
Some(value)
}
}
extern "C-unwind" {
pub(super) fn CFArrayGetCount(array: CFArrayRef) -> CFIndex;
pub(super) fn CFArrayGetValueAtIndex(array: CFArrayRef, index: CFIndex) -> *const c_void;
pub(super) fn CFArrayCreate(alloc: CFAllocatorRef, values: *const *const c_void, num_values: CFIndex, callbacks: *const c_void) -> CFArrayRef;
}
#[cfg(test)]
mod tests {
use super::CFArray;
#[test]
fn test_cf_array() {
let array = CFArray::from_slice(&[123u32, 456u32]).unwrap();
assert_eq!(array.len(), 2);
assert_eq!(array.get::<u32>(0), Some(123));
assert_eq!(array.get::<u32>(1), Some(456));
}
}