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