Skip to main content

apple_cf/cf/
base.rs

1use crate::ffi;
2use std::ffi::c_void;
3use std::fmt;
4
5/// Trait for Core Foundation values that can be inserted into CF collections.
6///
7/// # Safety
8///
9/// Implementors must return a non-null, live Core Foundation object pointer
10/// of the represented type. The pointer must remain valid for the duration of
11/// the borrow and support generic Core Foundation retain/release operations.
12pub unsafe trait AsCFType {
13    /// Borrow the underlying Core Foundation object pointer.
14    fn as_ptr(&self) -> *mut c_void;
15
16    /// Clone this value as an erased [`CFType`].
17    #[must_use]
18    fn to_cf_type(&self) -> CFType {
19        let retained = unsafe { ffi::cf_type_retain(self.as_ptr()) };
20        unsafe { CFType::from_raw(retained) }.expect("retained CFType pointer must be non-null")
21    }
22}
23
24/// Owned, type-erased `CFTypeRef`.
25pub struct CFType(*mut c_void);
26
27impl CFType {
28    /// Adopts a +1 retained `CFTypeRef` and returns `None` for null.
29    ///
30    /// # Safety
31    ///
32    /// A non-null `ptr` must be a live Core Foundation object pointer carrying
33    /// one retain that is transferred to the returned wrapper. The caller must
34    /// not release or separately adopt that transferred retain.
35    #[must_use]
36    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
37        if ptr.is_null() { None } else { Some(Self(ptr)) }
38    }
39
40    /// Retains a +0 borrowed `CFTypeRef` and returns an owned wrapper.
41    ///
42    /// # Safety
43    ///
44    /// A non-null `ptr` must be a live Core Foundation object pointer for the
45    /// duration of the retain call.
46    #[must_use]
47    pub unsafe fn from_raw_borrowed(ptr: *mut c_void) -> Option<Self> {
48        if ptr.is_null() {
49            None
50        } else {
51            let retained = unsafe { ffi::cf_type_retain(ptr) };
52            unsafe { Self::from_raw(retained) }
53        }
54    }
55
56    /// Borrow the raw +0 `CFTypeRef` pointer while `self` remains alive.
57    #[must_use]
58    pub const fn as_ptr(&self) -> *mut c_void {
59        self.0
60    }
61
62    /// Runtime type identifier of the wrapped object.
63    #[must_use]
64    pub fn type_id(&self) -> usize {
65        unsafe { ffi::cf_type_get_type_id(self.0) }
66    }
67
68    #[must_use]
69    pub fn as_number(&self) -> Option<crate::cf::CFNumber> {
70        (self.type_id() == crate::cf::CFNumber::type_id())
71            .then(|| unsafe { crate::cf::CFNumber::from_raw_borrowed(self.0) })
72            .flatten()
73    }
74
75    /// `CFHash` of the wrapped object.
76    #[must_use]
77    pub fn hash_code(&self) -> usize {
78        unsafe { ffi::cf_type_hash(self.0) }
79    }
80
81    /// Human-readable Core Foundation description.
82    #[must_use]
83    pub fn description(&self) -> String {
84        if self.type_id() == unsafe { ffi::cf_run_loop_get_type_id() } {
85            return format!("<CFRunLoop {:p}>", self.0);
86        }
87        let ptr = unsafe { ffi::acf_cf_type_copy_description_string(self.0) };
88        unsafe { crate::cf::CFString::from_raw(ptr) }
89            .map_or_else(String::new, |description| description.to_string_lossy())
90    }
91}
92
93crate::utils::retained::cf_retained!(
94    CFType,
95    retain = ffi::cf_type_retain,
96    release = ffi::cf_type_release,
97);
98
99unsafe impl AsCFType for CFType {
100    fn as_ptr(&self) -> *mut c_void {
101        self.0
102    }
103}
104
105impl PartialEq for CFType {
106    fn eq(&self, other: &Self) -> bool {
107        unsafe { ffi::cf_type_equal(self.0, other.0) }
108    }
109}
110
111impl Eq for CFType {}
112
113impl std::hash::Hash for CFType {
114    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
115        self.hash_code().hash(state);
116    }
117}
118
119impl fmt::Debug for CFType {
120    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121        f.debug_struct("CFType")
122            .field("ptr", &self.0)
123            .field("type_id", &self.type_id())
124            .field("description", &self.description())
125            .finish()
126    }
127}
128
129impl fmt::Display for CFType {
130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131        f.write_str(&self.description())
132    }
133}
134
135/// Owned Swift bridge holder object used for callback-heavy wrappers.
136pub struct SwiftObject(*mut c_void);
137
138impl SwiftObject {
139    /// Wraps a +1 retained bridge object pointer and returns `None` for null.
140    #[must_use]
141    pub(crate) fn from_raw_owned(ptr: *mut c_void) -> Option<Self> {
142        if ptr.is_null() { None } else { Some(Self(ptr)) }
143    }
144
145    /// Returns the wrapped raw bridge object pointer.
146    #[must_use]
147    pub(crate) const fn as_ptr(&self) -> *mut c_void {
148        self.0
149    }
150}
151
152crate::utils::retained::cf_retained!(
153    SwiftObject,
154    retain = ffi::acf_object_retain,
155    release = ffi::acf_object_release,
156);
157
158impl PartialEq for SwiftObject {
159    fn eq(&self, other: &Self) -> bool {
160        self.0 == other.0
161    }
162}
163
164impl Eq for SwiftObject {}
165
166impl std::hash::Hash for SwiftObject {
167    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
168        unsafe { ffi::acf_object_hash(self.0) }.hash(state);
169    }
170}
171
172impl fmt::Debug for SwiftObject {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        f.debug_struct("SwiftObject").field("ptr", &self.0).finish()
175    }
176}
177
178macro_rules! impl_cf_type_wrapper {
179    ($name:ident, $type_id_fn:ident) => {
180        #[derive(Clone, PartialEq, Eq, Hash)]
181        #[doc = concat!("Safe wrapper around a retained Core Foundation `", stringify!($name), "` reference.")]
182        pub struct $name(pub(crate) crate::cf::base::CFType);
183
184        impl $name {
185            #[doc = concat!("Adopts a +1 retained `", stringify!($name), "` pointer and returns `None` for null.")]
186            ///
187            /// # Safety
188            ///
189            #[doc = concat!("A non-null `ptr` must be a live `", stringify!($name), "` pointer of the exact dynamic type carrying one retain transferred to this wrapper. The caller must not release or separately adopt that transferred retain.")]
190            #[must_use]
191            pub unsafe fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
192                unsafe { crate::cf::base::CFType::from_raw(ptr) }.map(Self)
193            }
194
195            #[doc = concat!("Retains a +0 borrowed `", stringify!($name), "` pointer and returns an owned wrapper.")]
196            ///
197            /// # Safety
198            ///
199            #[doc = concat!("A non-null `ptr` must be a live `", stringify!($name), "` pointer of the exact dynamic type for the duration of the retain call.")]
200            #[must_use]
201            pub unsafe fn from_raw_borrowed(ptr: *mut std::ffi::c_void) -> Option<Self> {
202                unsafe { crate::cf::base::CFType::from_raw_borrowed(ptr) }.map(Self)
203            }
204
205            /// Borrows the raw +0 Core Foundation pointer while `self` remains alive.
206            #[must_use]
207            pub const fn as_ptr(&self) -> *mut std::ffi::c_void {
208                self.0.as_ptr()
209            }
210
211            #[doc = concat!("Returns the Core Foundation type ID for `", stringify!($name), "`.")]
212            #[must_use]
213            pub fn type_id() -> usize {
214                unsafe { crate::ffi::$type_id_fn() }
215            }
216
217            /// Consumes this wrapper and returns the erased `CFType`.
218            #[must_use]
219            pub fn into_cf_type(self) -> crate::cf::base::CFType {
220                self.0
221            }
222        }
223
224        unsafe impl crate::cf::base::AsCFType for $name {
225            fn as_ptr(&self) -> *mut std::ffi::c_void {
226                self.as_ptr()
227            }
228        }
229
230        impl std::fmt::Debug for $name {
231            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
232                f.debug_struct(stringify!($name))
233                    .field("ptr", &self.as_ptr())
234                    .field("description", &self.0.description())
235                    .finish()
236            }
237        }
238    };
239}
240
241/// Re-exports the wrapper-generation macro within this crate.
242pub(crate) use impl_cf_type_wrapper;