apple_cf/cf/
primitives.rs1#![allow(clippy::missing_panics_doc)]
4
5use super::base::{impl_cf_type_wrapper, CFType};
23use crate::ffi;
24use std::ffi::CString;
25use std::fmt;
26use std::time::{Duration, SystemTime, UNIX_EPOCH};
27
28const CF_ABSOLUTE_TIME_INTERVAL_SINCE_1970: f64 = 978_307_200.0;
29
30fn to_cstring(value: &str) -> CString {
31 CString::new(value).expect("Core Foundation strings may not contain interior NUL bytes")
32}
33
34impl_cf_type_wrapper!(CFString, cf_string_get_type_id);
35impl_cf_type_wrapper!(CFNumber, cf_number_get_type_id);
36impl_cf_type_wrapper!(CFData, cf_data_get_type_id);
37impl_cf_type_wrapper!(CFDate, cf_date_get_type_id);
38impl_cf_type_wrapper!(CFUUID, cf_uuid_get_type_id);
39impl_cf_type_wrapper!(CFError, cf_error_get_type_id);
40
41impl CFString {
42 #[must_use]
44 pub fn new(value: &str) -> Self {
45 let value = to_cstring(value);
46 let ptr = unsafe { ffi::cf_string_create_with_cstring(value.as_ptr()) };
47 Self::from_raw(ptr).expect("CFStringCreateWithCString returned NULL")
48 }
49
50 #[must_use]
52 pub fn len(&self) -> usize {
53 unsafe { ffi::cf_string_get_length(self.as_ptr()) }
54 }
55
56 #[must_use]
58 pub fn is_empty(&self) -> bool {
59 self.len() == 0
60 }
61
62 #[must_use]
64 pub fn to_string_lossy(&self) -> String {
65 let ptr = unsafe { ffi::cf_string_copy_cstring(self.as_ptr()) };
66 if ptr.is_null() {
67 return String::new();
68 }
69 let string = unsafe { std::ffi::CStr::from_ptr(ptr) }
70 .to_string_lossy()
71 .into_owned();
72 unsafe { ffi::acf_free_string(ptr) };
73 string
74 }
75}
76
77impl Default for CFString {
78 fn default() -> Self {
79 Self::new("")
80 }
81}
82
83impl fmt::Display for CFString {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 f.write_str(&self.to_string_lossy())
86 }
87}
88
89impl CFNumber {
90 #[must_use]
92 pub fn from_i64(value: i64) -> Self {
93 let ptr = unsafe { ffi::cf_number_create_i64(value) };
94 Self::from_raw(ptr).expect("CFNumberCreate returned NULL")
95 }
96
97 #[must_use]
99 pub fn from_u64(value: u64) -> Self {
100 let ptr = unsafe { ffi::cf_number_create_u64(value) };
101 Self::from_raw(ptr).expect("CFNumberCreate returned NULL")
102 }
103
104 #[must_use]
106 pub fn from_f64(value: f64) -> Self {
107 let ptr = unsafe { ffi::cf_number_create_f64(value) };
108 Self::from_raw(ptr).expect("CFNumberCreate returned NULL")
109 }
110
111 #[must_use]
113 pub fn to_i64(&self) -> Option<i64> {
114 let mut out = 0_i64;
115 let ok = unsafe { ffi::cf_number_get_i64(self.as_ptr(), &mut out) };
116 ok.then_some(out)
117 }
118
119 #[must_use]
121 pub fn to_u64(&self) -> Option<u64> {
122 let mut out = 0_u64;
123 let ok = unsafe { ffi::cf_number_get_u64(self.as_ptr(), &mut out) };
124 ok.then_some(out)
125 }
126
127 #[must_use]
129 pub fn to_f64(&self) -> Option<f64> {
130 let mut out = 0.0_f64;
131 let ok = unsafe { ffi::cf_number_get_f64(self.as_ptr(), &mut out) };
132 ok.then_some(out)
133 }
134
135 #[must_use]
137 pub fn is_float_type(&self) -> bool {
138 unsafe { ffi::cf_number_is_float_type(self.as_ptr()) }
139 }
140}
141
142impl From<i64> for CFNumber {
143 fn from(value: i64) -> Self {
144 Self::from_i64(value)
145 }
146}
147
148impl From<u64> for CFNumber {
149 fn from(value: u64) -> Self {
150 Self::from_u64(value)
151 }
152}
153
154impl From<f64> for CFNumber {
155 fn from(value: f64) -> Self {
156 Self::from_f64(value)
157 }
158}
159
160impl CFData {
161 #[must_use]
163 pub fn from_bytes<B: AsRef<[u8]>>(bytes: B) -> Self {
164 let bytes = bytes.as_ref();
165 let ptr = unsafe { ffi::cf_data_create(bytes.as_ptr(), bytes.len()) };
166 Self::from_raw(ptr).expect("CFDataCreate returned NULL")
167 }
168
169 #[must_use]
171 pub fn len(&self) -> usize {
172 unsafe { ffi::cf_data_get_length(self.as_ptr()) }
173 }
174
175 #[must_use]
177 pub fn is_empty(&self) -> bool {
178 self.len() == 0
179 }
180
181 #[must_use]
183 pub fn to_vec(&self) -> Vec<u8> {
184 let mut bytes = vec![0_u8; self.len()];
185 if !bytes.is_empty() {
186 unsafe { ffi::cf_data_copy_bytes(self.as_ptr(), bytes.as_mut_ptr()) };
187 }
188 bytes
189 }
190}
191
192impl CFDate {
193 #[must_use]
195 pub fn from_absolute_time(absolute_time: f64) -> Self {
196 let ptr = unsafe { ffi::cf_date_create(absolute_time) };
197 Self::from_raw(ptr).expect("CFDateCreate returned NULL")
198 }
199
200 #[must_use]
202 pub fn now() -> Self {
203 Self::from_system_time(SystemTime::now())
204 }
205
206 #[must_use]
208 pub fn from_system_time(time: SystemTime) -> Self {
209 let unix_seconds = match time.duration_since(UNIX_EPOCH) {
210 Ok(duration) => duration.as_secs_f64(),
211 Err(err) => -err.duration().as_secs_f64(),
212 };
213 let absolute = unix_seconds - CF_ABSOLUTE_TIME_INTERVAL_SINCE_1970;
214 Self::from_absolute_time(absolute)
215 }
216
217 #[must_use]
219 pub fn absolute_time(&self) -> f64 {
220 unsafe { ffi::cf_date_get_absolute_time(self.as_ptr()) }
221 }
222
223 #[must_use]
225 pub fn to_system_time(&self) -> Option<SystemTime> {
226 let unix_seconds = self.absolute_time() + CF_ABSOLUTE_TIME_INTERVAL_SINCE_1970;
227 if unix_seconds.is_nan() || !unix_seconds.is_finite() {
228 return None;
229 }
230 if unix_seconds >= 0.0 {
231 Some(UNIX_EPOCH + Duration::from_secs_f64(unix_seconds))
232 } else {
233 Some(UNIX_EPOCH - Duration::from_secs_f64(-unix_seconds))
234 }
235 }
236}
237
238impl CFUUID {
239 #[must_use]
241 pub fn new() -> Self {
242 let ptr = unsafe { ffi::cf_uuid_create() };
243 Self::from_raw(ptr).expect("CFUUIDCreate returned NULL")
244 }
245
246 #[must_use]
248 pub fn parse_str(value: &str) -> Option<Self> {
249 let value = to_cstring(value);
250 let ptr = unsafe { ffi::cf_uuid_create_from_string(value.as_ptr()) };
251 Self::from_raw(ptr)
252 }
253
254 #[must_use]
256 pub fn string(&self) -> CFString {
257 let ptr = unsafe { ffi::cf_uuid_copy_string(self.as_ptr()) };
258 CFString::from_raw(ptr).expect("CFUUIDCreateString returned NULL")
259 }
260
261 #[must_use]
263 pub fn bytes(&self) -> [u8; 16] {
264 let mut bytes = [0_u8; 16];
265 unsafe { ffi::cf_uuid_get_bytes(self.as_ptr(), bytes.as_mut_ptr()) };
266 bytes
267 }
268}
269
270impl Default for CFUUID {
271 fn default() -> Self {
272 Self::new()
273 }
274}
275
276impl fmt::Display for CFUUID {
277 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
278 fmt::Display::fmt(&self.string(), f)
279 }
280}
281
282impl CFError {
283 #[must_use]
285 pub fn new(domain: &CFString, code: i64, description: Option<&str>) -> Self {
286 let description = description.map(to_cstring);
287 let description_ptr = description
288 .as_ref()
289 .map_or(std::ptr::null(), |s| s.as_ptr());
290 let ptr = unsafe { ffi::cf_error_create(domain.as_ptr(), code, description_ptr) };
291 Self::from_raw(ptr).expect("CFErrorCreate returned NULL")
292 }
293
294 #[must_use]
296 pub fn domain(&self) -> CFString {
297 let ptr = unsafe { ffi::cf_error_get_domain(self.as_ptr()) };
298 CFString::from_raw(ptr).expect("CFErrorGetDomain returned NULL")
299 }
300
301 #[must_use]
303 pub fn code(&self) -> i64 {
304 unsafe { ffi::cf_error_get_code(self.as_ptr()) }
305 }
306
307 #[must_use]
309 pub fn description_string(&self) -> Option<CFString> {
310 let ptr = unsafe { ffi::cf_error_copy_description(self.as_ptr()) };
311 CFString::from_raw(ptr)
312 }
313
314 #[must_use]
316 pub fn failure_reason(&self) -> Option<CFString> {
317 let ptr = unsafe { ffi::cf_error_copy_failure_reason(self.as_ptr()) };
318 CFString::from_raw(ptr)
319 }
320}
321
322impl fmt::Display for CFError {
323 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
324 if let Some(description) = self.description_string() {
325 write!(f, "{} ({})", description, self.code())
326 } else {
327 write!(f, "{} ({})", self.domain(), self.code())
328 }
329 }
330}
331
332impl std::error::Error for CFError {}
333
334impl From<CFString> for CFType {
335 fn from(value: CFString) -> Self {
336 value.into_cf_type()
337 }
338}
339
340impl From<CFNumber> for CFType {
341 fn from(value: CFNumber) -> Self {
342 value.into_cf_type()
343 }
344}
345
346impl From<CFData> for CFType {
347 fn from(value: CFData) -> Self {
348 value.into_cf_type()
349 }
350}
351
352impl From<CFDate> for CFType {
353 fn from(value: CFDate) -> Self {
354 value.into_cf_type()
355 }
356}
357
358impl From<CFUUID> for CFType {
359 fn from(value: CFUUID) -> Self {
360 value.into_cf_type()
361 }
362}