#![cfg(target_os = "macos")]
use std::mem::ManuallyDrop;
use std::ptr::NonNull;
pub mod sys;
#[macro_use]
mod macros;
mod array;
mod dict;
mod font;
mod font_desc;
mod set;
mod string;
mod url;
pub use array::CFArray;
pub use dict::CFDictionary;
pub use font::{CTFont, FontAttribute, FontNameKey};
pub use font_desc::CTFontDescriptor;
pub use set::CFSet;
pub use string::CFString;
pub use url::CFUrl;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct CFTypeId(libc::c_ulong);
impl CFTypeId {
pub fn of<T: CoreType>() -> CFTypeId {
T::type_id()
}
pub fn of_val<T: CoreType>(val: &T) -> CFTypeId {
CFTypeId(unsafe { sys::CFGetTypeID(val.as_type_ref().cast()) })
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct CFType(NonNull<()>);
impl CFType {
pub unsafe fn downcast<T: CoreType>(self) -> Result<T, Self> {
if CFTypeId::of_val(&self) == CFTypeId::of::<T>() {
let this = ManuallyDrop::new(self);
Ok(unsafe { T::new_owned(this.0.cast()) })
} else {
Err(self)
}
}
}
unsafe impl CoreType for CFType {
type SysTy = ();
fn type_id() -> CFTypeId {
CFTypeId(0)
}
unsafe fn new_owned(ptr: NonNull<Self::SysTy>) -> Self {
CFType(ptr)
}
unsafe fn new_borrowed(ptr: NonNull<Self::SysTy>) -> Self {
unsafe { sys::CFRetain(ptr.as_ptr()) };
unsafe { CFType::new_owned(ptr) }
}
fn as_type_ref(&self) -> *const Self::SysTy {
self.0.as_ptr().cast_const()
}
fn into_ty(self) -> CFType {
self
}
}
impl Drop for CFType {
fn drop(&mut self) {
unsafe { sys::CFRelease(self.0.as_ptr()) }
}
}
pub unsafe trait CoreType: Sized {
type SysTy;
fn type_id() -> CFTypeId;
unsafe fn new_owned(ptr: NonNull<Self::SysTy>) -> Self;
unsafe fn new_borrowed(ptr: NonNull<Self::SysTy>) -> Self;
#[doc(hidden)]
fn as_type_ref(&self) -> *const Self::SysTy;
#[doc(hidden)]
fn into_type_ref(self) -> *const Self::SysTy {
let this = ManuallyDrop::new(self);
this.as_type_ref()
}
fn into_ty(self) -> CFType;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_type_id() {
let arr = CFArray::<CFType>::empty();
assert_eq!(CFTypeId::of::<CFArray<CFType>>(), CFTypeId::of_val(&arr));
assert_eq!(
CFTypeId::of::<CFArray<CFString>>(),
CFTypeId::of::<CFArray<CFUrl>>()
);
assert_ne!(CFTypeId::of::<CFString>(), CFTypeId::of::<CFUrl>());
}
#[test]
fn test_upcast() {
let arr = CFArray::<CFType>::empty();
let ty = arr.into_ty();
assert_eq!(CFTypeId::of_val(&ty), CFTypeId::of::<CFArray<CFType>>());
drop(ty);
}
}