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
30impl_cf_type_wrapper!(CFString, cf_string_get_type_id);
31impl_cf_type_wrapper!(CFNumber, cf_number_get_type_id);
32impl_cf_type_wrapper!(CFData, cf_data_get_type_id);
33impl_cf_type_wrapper!(CFDate, cf_date_get_type_id);
34impl_cf_type_wrapper!(CFUUID, cf_uuid_get_type_id);
35impl_cf_type_wrapper!(CFError, cf_error_get_type_id);
36
37impl CFString {
38 #[must_use]
40 pub fn new(value: &str) -> Self {
41 let ptr = unsafe { ffi::acf_cf_string_create_with_bytes(value.as_ptr(), value.len()) };
42 unsafe { Self::from_raw(ptr) }.expect("CFStringCreateWithBytes returned NULL")
43 }
44
45 #[must_use]
47 pub fn len(&self) -> usize {
48 unsafe { ffi::cf_string_get_length(self.as_ptr()) }
49 }
50
51 #[must_use]
53 pub fn is_empty(&self) -> bool {
54 self.len() == 0
55 }
56
57 #[must_use]
59 pub fn to_string_lossy(&self) -> String {
60 let mut units: Vec<u16> = Vec::new();
61 loop {
62 let capacity = units.len();
63 let buffer = if capacity == 0 {
64 std::ptr::null_mut()
65 } else {
66 units.as_mut_ptr()
67 };
68 let len = unsafe { ffi::acf_cf_string_copy_utf16(self.as_ptr(), buffer, capacity) };
69 if len <= capacity {
70 units.truncate(len);
71 return String::from_utf16_lossy(&units);
72 }
73 units.resize(len, 0);
74 }
75 }
76}
77
78impl Default for CFString {
79 fn default() -> Self {
80 Self::new("")
81 }
82}
83
84impl fmt::Display for CFString {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 f.write_str(&self.to_string_lossy())
87 }
88}
89
90impl CFNumber {
91 #[must_use]
93 pub fn from_i64(value: i64) -> Self {
94 let ptr = unsafe { ffi::cf_number_create_i64(value) };
95 unsafe { Self::from_raw(ptr) }.expect("CFNumberCreate returned NULL")
96 }
97
98 #[must_use]
100 pub fn from_u64(value: u64) -> Self {
101 let ptr = unsafe { ffi::cf_number_create_u64(value) };
102 unsafe { Self::from_raw(ptr) }.expect("CFNumberCreate returned NULL")
103 }
104
105 #[must_use]
107 pub fn from_f64(value: f64) -> Self {
108 let ptr = unsafe { ffi::cf_number_create_f64(value) };
109 unsafe { Self::from_raw(ptr) }.expect("CFNumberCreate returned NULL")
110 }
111
112 #[must_use]
114 pub fn to_i64(&self) -> Option<i64> {
115 let mut out = 0_i64;
116 let ok = unsafe { ffi::cf_number_get_i64(self.as_ptr(), &raw mut out) };
117 ok.then_some(out)
118 }
119
120 #[must_use]
122 pub fn to_u64(&self) -> Option<u64> {
123 let mut out = 0_u64;
124 let ok = unsafe { ffi::cf_number_get_u64(self.as_ptr(), &raw mut out) };
125 ok.then_some(out)
126 }
127
128 #[must_use]
130 pub fn to_f64(&self) -> Option<f64> {
131 let mut out = 0.0_f64;
132 let ok = unsafe { ffi::cf_number_get_f64(self.as_ptr(), &raw mut out) };
133 ok.then_some(out)
134 }
135
136 #[must_use]
138 pub fn is_float_type(&self) -> bool {
139 unsafe { ffi::cf_number_is_float_type(self.as_ptr()) }
140 }
141}
142
143impl From<i64> for CFNumber {
144 fn from(value: i64) -> Self {
145 Self::from_i64(value)
146 }
147}
148
149impl From<u64> for CFNumber {
150 fn from(value: u64) -> Self {
151 Self::from_u64(value)
152 }
153}
154
155impl From<f64> for CFNumber {
156 fn from(value: f64) -> Self {
157 Self::from_f64(value)
158 }
159}
160
161impl CFData {
162 #[must_use]
164 pub fn from_bytes<B: AsRef<[u8]>>(bytes: B) -> Self {
165 let bytes = bytes.as_ref();
166 let ptr = unsafe { ffi::cf_data_create(bytes.as_ptr(), bytes.len()) };
167 unsafe { Self::from_raw(ptr) }.expect("CFDataCreate returned NULL")
168 }
169
170 #[must_use]
172 pub fn len(&self) -> usize {
173 unsafe { ffi::cf_data_get_length(self.as_ptr()) }
174 }
175
176 #[must_use]
178 pub fn is_empty(&self) -> bool {
179 self.len() == 0
180 }
181
182 #[must_use]
184 pub fn to_vec(&self) -> Vec<u8> {
185 let mut bytes = vec![0_u8; self.len()];
186 loop {
187 let capacity = bytes.len();
188 let buffer = if capacity == 0 {
189 std::ptr::null_mut()
190 } else {
191 bytes.as_mut_ptr()
192 };
193 let len = unsafe { ffi::acf_cf_data_copy_bytes(self.as_ptr(), buffer, capacity) };
194 if len <= capacity {
195 bytes.truncate(len);
196 return bytes;
197 }
198 bytes.resize(len, 0);
199 }
200 }
201}
202
203impl CFDate {
204 #[must_use]
206 pub fn from_absolute_time(absolute_time: f64) -> Self {
207 let ptr = unsafe { ffi::cf_date_create(absolute_time) };
208 unsafe { Self::from_raw(ptr) }.expect("CFDateCreate returned NULL")
209 }
210
211 #[must_use]
213 pub fn now() -> Self {
214 Self::from_system_time(SystemTime::now())
215 }
216
217 #[must_use]
219 pub fn from_system_time(time: SystemTime) -> Self {
220 let unix_seconds = match time.duration_since(UNIX_EPOCH) {
221 Ok(duration) => duration.as_secs_f64(),
222 Err(err) => -err.duration().as_secs_f64(),
223 };
224 let absolute = unix_seconds - CF_ABSOLUTE_TIME_INTERVAL_SINCE_1970;
225 Self::from_absolute_time(absolute)
226 }
227
228 #[must_use]
230 pub fn absolute_time(&self) -> f64 {
231 unsafe { ffi::cf_date_get_absolute_time(self.as_ptr()) }
232 }
233
234 #[must_use]
236 pub fn to_system_time(&self) -> Option<SystemTime> {
237 let unix_seconds = self.absolute_time() + CF_ABSOLUTE_TIME_INTERVAL_SINCE_1970;
238 if unix_seconds >= 0.0 {
239 UNIX_EPOCH.checked_add(Duration::try_from_secs_f64(unix_seconds).ok()?)
240 } else {
241 UNIX_EPOCH.checked_sub(Duration::try_from_secs_f64(-unix_seconds).ok()?)
242 }
243 }
244}
245
246impl CFUUID {
247 #[must_use]
249 pub fn new() -> Self {
250 let ptr = unsafe { ffi::cf_uuid_create() };
251 unsafe { Self::from_raw(ptr) }.expect("CFUUIDCreate returned NULL")
252 }
253
254 #[must_use]
256 pub fn parse_str(value: &str) -> Option<Self> {
257 let canonical = value
258 .strip_prefix('{')
259 .and_then(|inner| inner.strip_suffix('}'))
260 .unwrap_or(value);
261 let well_formed = canonical.len() == 36
262 && canonical.bytes().enumerate().all(|(index, byte)| {
263 if matches!(index, 8 | 13 | 18 | 23) {
264 byte == b'-'
265 } else {
266 byte.is_ascii_hexdigit()
267 }
268 });
269 if !well_formed {
270 return None;
271 }
272 let value = CString::new(canonical).ok()?;
273 let ptr = unsafe { ffi::cf_uuid_create_from_string(value.as_ptr()) };
274 unsafe { Self::from_raw(ptr) }
275 }
276
277 #[must_use]
279 pub fn string(&self) -> CFString {
280 let ptr = unsafe { ffi::cf_uuid_copy_string(self.as_ptr()) };
281 unsafe { CFString::from_raw(ptr) }.expect("CFUUIDCreateString returned NULL")
282 }
283
284 #[must_use]
286 pub fn bytes(&self) -> [u8; 16] {
287 let mut bytes = [0_u8; 16];
288 unsafe { ffi::cf_uuid_get_bytes(self.as_ptr(), bytes.as_mut_ptr()) };
289 bytes
290 }
291}
292
293impl Default for CFUUID {
294 fn default() -> Self {
295 Self::new()
296 }
297}
298
299impl fmt::Display for CFUUID {
300 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
301 fmt::Display::fmt(&self.string(), f)
302 }
303}
304
305impl CFError {
306 #[must_use]
308 pub fn new(domain: &CFString, code: i64, description: Option<&str>) -> Self {
309 let description = description.map(CFString::new);
310 let ptr = unsafe {
311 ffi::acf_cf_error_create(
312 domain.as_ptr(),
313 code,
314 description
315 .as_ref()
316 .map_or(std::ptr::null_mut(), CFString::as_ptr),
317 )
318 };
319 unsafe { Self::from_raw(ptr) }.expect("CFErrorCreate returned NULL")
320 }
321
322 #[must_use]
324 pub fn domain(&self) -> CFString {
325 let ptr = unsafe { ffi::cf_error_get_domain(self.as_ptr()) };
326 unsafe { CFString::from_raw(ptr) }.expect("CFErrorGetDomain returned NULL")
327 }
328
329 #[must_use]
331 pub fn code(&self) -> i64 {
332 unsafe { ffi::cf_error_get_code(self.as_ptr()) }
333 }
334
335 #[must_use]
337 pub fn description_string(&self) -> Option<CFString> {
338 let ptr = unsafe { ffi::cf_error_copy_description(self.as_ptr()) };
339 unsafe { CFString::from_raw(ptr) }
340 }
341
342 #[must_use]
344 pub fn failure_reason(&self) -> Option<CFString> {
345 let ptr = unsafe { ffi::cf_error_copy_failure_reason(self.as_ptr()) };
346 unsafe { CFString::from_raw(ptr) }
347 }
348}
349
350impl fmt::Display for CFError {
351 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
352 if let Some(description) = self.description_string() {
353 write!(f, "{} ({})", description, self.code())
354 } else {
355 write!(f, "{} ({})", self.domain(), self.code())
356 }
357 }
358}
359
360impl std::error::Error for CFError {}
361
362impl From<CFString> for CFType {
363 fn from(value: CFString) -> Self {
364 value.into_cf_type()
365 }
366}
367
368impl From<CFNumber> for CFType {
369 fn from(value: CFNumber) -> Self {
370 value.into_cf_type()
371 }
372}
373
374impl From<CFData> for CFType {
375 fn from(value: CFData) -> Self {
376 value.into_cf_type()
377 }
378}
379
380impl From<CFDate> for CFType {
381 fn from(value: CFDate) -> Self {
382 value.into_cf_type()
383 }
384}
385
386impl From<CFUUID> for CFType {
387 fn from(value: CFUUID) -> Self {
388 value.into_cf_type()
389 }
390}