Skip to main content

apple_cf/cf/
base.rs

1use crate::ffi;
2use std::ffi::{c_void, CStr};
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        let ptr = unsafe { ffi::cf_type_copy_description(self.0) };
82        if ptr.is_null() {
83            return String::new();
84        }
85        let string = unsafe { CStr::from_ptr(ptr) }
86            .to_string_lossy()
87            .into_owned();
88        unsafe { ffi::acf_free_string(ptr) };
89        string
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() {
143            None
144        } else {
145            Some(Self(ptr))
146        }
147    }
148
149    /// Returns the wrapped raw bridge object pointer.
150    #[must_use]
151    pub(crate) const fn as_ptr(&self) -> *mut c_void {
152        self.0
153    }
154}
155
156crate::utils::retained::cf_retained!(
157    SwiftObject,
158    retain = ffi::acf_object_retain,
159    release = ffi::acf_object_release,
160);
161
162impl PartialEq for SwiftObject {
163    fn eq(&self, other: &Self) -> bool {
164        self.0 == other.0
165    }
166}
167
168impl Eq for SwiftObject {}
169
170impl std::hash::Hash for SwiftObject {
171    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
172        unsafe { ffi::acf_object_hash(self.0) }.hash(state);
173    }
174}
175
176impl fmt::Debug for SwiftObject {
177    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178        f.debug_struct("SwiftObject").field("ptr", &self.0).finish()
179    }
180}
181
182macro_rules! impl_cf_type_wrapper {
183    ($name:ident, $type_id_fn:ident) => {
184        #[derive(Clone, PartialEq, Eq, Hash)]
185        #[doc = concat!("Safe wrapper around a retained Core Foundation `", stringify!($name), "` reference.")]
186        pub struct $name(pub(crate) crate::cf::base::CFType);
187
188        impl $name {
189            #[doc = concat!("Adopts a +1 retained `", stringify!($name), "` pointer and returns `None` for null.")]
190            ///
191            /// # Safety
192            ///
193            #[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.")]
194            #[must_use]
195            pub unsafe fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
196                unsafe { crate::cf::base::CFType::from_raw(ptr) }.map(Self)
197            }
198
199            #[doc = concat!("Retains a +0 borrowed `", stringify!($name), "` pointer and returns an owned wrapper.")]
200            ///
201            /// # Safety
202            ///
203            #[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.")]
204            #[must_use]
205            pub unsafe fn from_raw_borrowed(ptr: *mut std::ffi::c_void) -> Option<Self> {
206                unsafe { crate::cf::base::CFType::from_raw_borrowed(ptr) }.map(Self)
207            }
208
209            /// Borrows the raw +0 Core Foundation pointer while `self` remains alive.
210            #[must_use]
211            pub const fn as_ptr(&self) -> *mut std::ffi::c_void {
212                self.0.as_ptr()
213            }
214
215            #[doc = concat!("Returns the Core Foundation type ID for `", stringify!($name), "`.")]
216            #[must_use]
217            pub fn type_id() -> usize {
218                unsafe { crate::ffi::$type_id_fn() }
219            }
220
221            /// Consumes this wrapper and returns the erased `CFType`.
222            #[must_use]
223            pub fn into_cf_type(self) -> crate::cf::base::CFType {
224                self.0
225            }
226        }
227
228        unsafe impl crate::cf::base::AsCFType for $name {
229            fn as_ptr(&self) -> *mut std::ffi::c_void {
230                self.as_ptr()
231            }
232        }
233
234        impl std::fmt::Debug for $name {
235            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
236                f.debug_struct(stringify!($name))
237                    .field("ptr", &self.as_ptr())
238                    .field("description", &self.0.description())
239                    .finish()
240            }
241        }
242    };
243}
244
245/// Re-exports the wrapper-generation macro within this crate.
246pub(crate) use impl_cf_type_wrapper;