1use crate::ffi;
2use std::ffi::{c_void, CStr};
3use std::fmt;
4
5pub trait AsCFType {
7 fn as_ptr(&self) -> *mut c_void;
9
10 #[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
18pub struct CFType(*mut c_void);
20
21impl CFType {
22 #[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 #[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 #[must_use]
49 pub const fn as_ptr(&self) -> *mut c_void {
50 self.0
51 }
52
53 #[must_use]
55 pub fn type_id(&self) -> usize {
56 unsafe { ffi::cf_type_get_type_id(self.0) }
57 }
58
59 #[must_use]
61 pub fn hash_code(&self) -> usize {
62 unsafe { ffi::cf_type_hash(self.0) }
63 }
64
65 #[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
131pub struct SwiftObject(*mut c_void);
133
134impl SwiftObject {
135 #[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 #[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 #[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 #[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 #[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
246pub(crate) use impl_cf_type_wrapper;