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    /// Adopt a retained `CFTypeRef`.
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    /// Retain a borrowed `CFTypeRef` and wrap it.
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    #[must_use]
136    pub(crate) fn from_raw(ptr: *mut c_void) -> Option<Self> {
137        if ptr.is_null() {
138            None
139        } else {
140            Some(Self(ptr))
141        }
142    }
143
144    #[must_use]
145    pub(crate) const fn as_ptr(&self) -> *mut c_void {
146        self.0
147    }
148}
149
150impl Clone for SwiftObject {
151    fn clone(&self) -> Self {
152        let retained = unsafe { ffi::acf_object_retain(self.0) };
153        Self(retained)
154    }
155}
156
157impl Drop for SwiftObject {
158    fn drop(&mut self) {
159        if !self.0.is_null() {
160            unsafe { ffi::acf_object_release(self.0) };
161        }
162    }
163}
164
165impl PartialEq for SwiftObject {
166    fn eq(&self, other: &Self) -> bool {
167        self.0 == other.0
168    }
169}
170
171impl Eq for SwiftObject {}
172
173impl std::hash::Hash for SwiftObject {
174    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
175        unsafe { ffi::acf_object_hash(self.0) }.hash(state);
176    }
177}
178
179impl fmt::Debug for SwiftObject {
180    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181        f.debug_struct("SwiftObject").field("ptr", &self.0).finish()
182    }
183}
184
185macro_rules! impl_cf_type_wrapper {
186    ($name:ident, $type_id_fn:ident) => {
187        #[derive(Clone, PartialEq, Eq, Hash)]
188        pub struct $name(pub(crate) crate::cf::base::CFType);
189
190        impl $name {
191            #[must_use]
192            pub fn from_raw(ptr: *mut std::ffi::c_void) -> Option<Self> {
193                crate::cf::base::CFType::from_raw(ptr).map(Self)
194            }
195
196            /// Retain a borrowed pointer before wrapping it.
197            ///
198            /// # Safety
199            ///
200            /// `ptr` must be NULL or a valid pointer of the expected Core Foundation type.
201            #[must_use]
202            pub unsafe fn from_raw_retained(ptr: *mut std::ffi::c_void) -> Option<Self> {
203                unsafe { crate::cf::base::CFType::from_raw_retained(ptr) }.map(Self)
204            }
205
206            #[must_use]
207            pub const fn as_ptr(&self) -> *mut std::ffi::c_void {
208                self.0.as_ptr()
209            }
210
211            #[must_use]
212            pub fn type_id() -> usize {
213                unsafe { crate::ffi::$type_id_fn() }
214            }
215
216            #[must_use]
217            pub fn into_cf_type(self) -> crate::cf::base::CFType {
218                self.0
219            }
220        }
221
222        impl crate::cf::base::AsCFType for $name {
223            fn as_ptr(&self) -> *mut std::ffi::c_void {
224                self.as_ptr()
225            }
226        }
227
228        impl std::fmt::Debug for $name {
229            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
230                f.debug_struct(stringify!($name))
231                    .field("ptr", &self.as_ptr())
232                    .field("description", &self.0.description())
233                    .finish()
234            }
235        }
236    };
237}
238
239pub(crate) use impl_cf_type_wrapper;