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