1#![allow(clippy::missing_panics_doc)]
4
5use super::base::{impl_cf_type_wrapper, AsCFType, CFType};
45use super::{CFDate, CFNumber, CFString, CFUUID};
46use crate::ffi;
47use std::ffi::CString;
48
49fn to_cstring(value: &str) -> CString {
50 CString::new(value).expect("Core Foundation strings may not contain interior NUL bytes")
51}
52
53impl_cf_type_wrapper!(CFURL, cf_url_get_type_id);
54impl_cf_type_wrapper!(CFBundle, cf_bundle_get_type_id);
55impl_cf_type_wrapper!(CFLocale, cf_locale_get_type_id);
56impl_cf_type_wrapper!(CFCalendar, cf_calendar_get_type_id);
57impl_cf_type_wrapper!(CFTimeZone, cf_time_zone_get_type_id);
58impl_cf_type_wrapper!(CFCharacterSet, cf_character_set_get_type_id);
59impl_cf_type_wrapper!(CFNumberFormatter, cf_number_formatter_get_type_id);
60impl_cf_type_wrapper!(CFDateFormatter, cf_date_formatter_get_type_id);
61impl_cf_type_wrapper!(CFFileSecurity, cf_file_security_get_type_id);
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
65#[repr(i32)]
66pub enum CFNumberFormatterStyle {
67 NoStyle = 0,
68 Decimal = 1,
69 Currency = 2,
70 Percent = 3,
71 Scientific = 4,
72 SpellOut = 5,
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
77#[repr(i32)]
78pub enum CFDateFormatterStyle {
79 NoStyle = 0,
80 Short = 1,
81 Medium = 2,
82 Long = 3,
83 Full = 4,
84}
85
86impl CFURL {
87 #[must_use]
89 pub fn from_string(value: &str) -> Self {
90 let value = to_cstring(value);
91 let ptr = unsafe { ffi::cf_url_create_with_string(value.as_ptr()) };
92 unsafe { Self::from_raw(ptr) }.expect("CFURLCreateWithString returned NULL")
93 }
94
95 #[must_use]
97 pub fn from_file_system_path(path: &str, is_directory: bool) -> Self {
98 let path = to_cstring(path);
99 let ptr = unsafe { ffi::cf_url_create_file_path(path.as_ptr(), is_directory) };
100 unsafe { Self::from_raw(ptr) }.expect("CFURLCreateWithFileSystemPath returned NULL")
101 }
102
103 #[must_use]
105 pub fn absolute_string(&self) -> CFString {
106 let ptr = unsafe { ffi::cf_url_copy_absolute_string(self.as_ptr()) };
107 unsafe { CFString::from_raw(ptr) }.expect("CFURLCopyAbsoluteString returned NULL")
108 }
109
110 #[must_use]
112 pub fn file_system_path(&self) -> CFString {
113 let ptr = unsafe { ffi::cf_url_copy_file_system_path(self.as_ptr()) };
114 unsafe { CFString::from_raw(ptr) }.expect("CFURLCopyFileSystemPath returned NULL")
115 }
116
117 #[must_use]
119 pub fn has_directory_path(&self) -> bool {
120 unsafe { ffi::cf_url_has_directory_path(self.as_ptr()) }
121 }
122}
123
124impl CFBundle {
125 #[must_use]
127 pub fn main() -> Option<Self> {
128 let ptr = unsafe { ffi::cf_bundle_get_main() };
129 unsafe { Self::from_raw(ptr) }
130 }
131
132 #[must_use]
134 pub fn from_url(url: &CFURL) -> Option<Self> {
135 let ptr = unsafe { ffi::cf_bundle_create(url.as_ptr()) };
136 unsafe { Self::from_raw(ptr) }
137 }
138
139 #[must_use]
141 pub fn identifier(&self) -> Option<CFString> {
142 let ptr = unsafe { ffi::cf_bundle_copy_identifier(self.as_ptr()) };
143 unsafe { CFString::from_raw(ptr) }
144 }
145
146 #[must_use]
148 pub fn bundle_url(&self) -> CFURL {
149 let ptr = unsafe { ffi::cf_bundle_copy_bundle_url(self.as_ptr()) };
150 unsafe { CFURL::from_raw(ptr) }.expect("CFBundleCopyBundleURL returned NULL")
151 }
152
153 #[must_use]
155 pub fn resource_url(
156 &self,
157 name: &str,
158 extension: Option<&str>,
159 subdir: Option<&str>,
160 ) -> Option<CFURL> {
161 let name = to_cstring(name);
162 let extension = extension.map(to_cstring);
163 let subdir = subdir.map(to_cstring);
164 let ptr = unsafe {
165 ffi::cf_bundle_copy_resource_url(
166 self.as_ptr(),
167 name.as_ptr(),
168 extension.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
169 subdir.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
170 )
171 };
172 unsafe { CFURL::from_raw(ptr) }
173 }
174}
175
176impl CFLocale {
177 #[must_use]
179 pub fn current() -> Self {
180 let ptr = unsafe { ffi::cf_locale_copy_current() };
181 unsafe { Self::from_raw(ptr) }.expect("CFLocaleCopyCurrent returned NULL")
182 }
183
184 #[must_use]
186 pub fn new(identifier: &str) -> Self {
187 let identifier = to_cstring(identifier);
188 let ptr = unsafe { ffi::cf_locale_create(identifier.as_ptr()) };
189 unsafe { Self::from_raw(ptr) }.expect("CFLocaleCreate returned NULL")
190 }
191
192 #[must_use]
194 pub fn identifier(&self) -> CFString {
195 let ptr = unsafe { ffi::cf_locale_copy_identifier(self.as_ptr()) };
196 unsafe { CFString::from_raw(ptr) }.expect("CFLocale identifier should be non-null")
197 }
198}
199
200impl CFCalendar {
201 #[must_use]
203 pub fn current() -> Self {
204 let ptr = unsafe { ffi::cf_calendar_copy_current() };
205 unsafe { Self::from_raw(ptr) }.expect("CFCalendarCopyCurrent returned NULL")
206 }
207
208 #[must_use]
210 pub fn new(identifier: &str) -> Self {
211 let identifier = to_cstring(identifier);
212 let ptr = unsafe { ffi::cf_calendar_create(identifier.as_ptr()) };
213 unsafe { Self::from_raw(ptr) }.expect("CFCalendarCreateWithIdentifier returned NULL")
214 }
215
216 #[must_use]
218 pub fn identifier(&self) -> CFString {
219 let ptr = unsafe { ffi::cf_calendar_copy_identifier(self.as_ptr()) };
220 unsafe { CFString::from_raw(ptr) }.expect("CFCalendar identifier should be non-null")
221 }
222
223 #[must_use]
225 pub fn time_zone(&self) -> CFTimeZone {
226 let ptr = unsafe { ffi::cf_calendar_copy_time_zone(self.as_ptr()) };
227 unsafe { CFTimeZone::from_raw(ptr) }.expect("CFCalendarCopyTimeZone returned NULL")
228 }
229
230 pub fn set_time_zone(&self, time_zone: &CFTimeZone) {
232 unsafe { ffi::cf_calendar_set_time_zone(self.as_ptr(), time_zone.as_ptr()) };
233 }
234}
235
236impl CFTimeZone {
237 #[must_use]
239 pub fn current() -> Self {
240 let ptr = unsafe { ffi::cf_time_zone_copy_current() };
241 unsafe { Self::from_raw(ptr) }.expect("CFTimeZoneCopyCurrent returned NULL")
242 }
243
244 #[must_use]
246 pub fn new(name: &str) -> Self {
247 let name = to_cstring(name);
248 let ptr = unsafe { ffi::cf_time_zone_create(name.as_ptr()) };
249 unsafe { Self::from_raw(ptr) }.expect("CFTimeZoneCreateWithName returned NULL")
250 }
251
252 #[must_use]
254 pub fn name(&self) -> CFString {
255 let ptr = unsafe { ffi::cf_time_zone_copy_name(self.as_ptr()) };
256 unsafe { CFString::from_raw(ptr) }.expect("CFTimeZoneGetName returned NULL")
257 }
258
259 #[must_use]
261 pub fn seconds_from_gmt(&self, date: &CFDate) -> i32 {
262 unsafe { ffi::cf_time_zone_get_seconds_from_gmt(self.as_ptr(), date.as_ptr()) }
263 }
264}
265
266impl CFCharacterSet {
267 #[must_use]
269 pub fn from_characters_in_string(string: &CFString) -> Self {
270 let ptr =
271 unsafe { ffi::cf_character_set_create_with_characters_in_string(string.as_ptr()) };
272 unsafe { Self::from_raw(ptr) }
273 .expect("CFCharacterSetCreateWithCharactersInString returned NULL")
274 }
275
276 #[must_use]
278 pub fn inverted(&self) -> Self {
279 let ptr = unsafe { ffi::cf_character_set_create_inverted_set(self.as_ptr()) };
280 unsafe { Self::from_raw(ptr) }.expect("CFCharacterSetCreateInvertedSet returned NULL")
281 }
282
283 #[must_use]
285 pub fn contains(&self, character: char) -> bool {
286 unsafe { ffi::cf_character_set_is_character_member(self.as_ptr(), u32::from(character)) }
287 }
288}
289
290impl CFNumberFormatter {
291 #[must_use]
293 pub fn new(locale: Option<&CFLocale>, style: CFNumberFormatterStyle) -> Self {
294 let ptr = unsafe {
295 ffi::cf_number_formatter_create(
296 locale.map_or(std::ptr::null_mut(), CFLocale::as_ptr),
297 style as i32,
298 )
299 };
300 unsafe { Self::from_raw(ptr) }.expect("CFNumberFormatterCreate returned NULL")
301 }
302
303 #[must_use]
305 pub fn format_number(&self, number: &CFNumber) -> CFString {
306 let ptr = unsafe {
307 ffi::cf_number_formatter_create_string_with_number(self.as_ptr(), number.as_ptr())
308 };
309 unsafe { CFString::from_raw(ptr) }
310 .expect("CFNumberFormatterCreateStringWithNumber returned NULL")
311 }
312
313 #[must_use]
315 pub fn parse_number(&self, string: &CFString) -> Option<CFNumber> {
316 let ptr = unsafe {
317 ffi::cf_number_formatter_create_number_from_string(self.as_ptr(), string.as_ptr())
318 };
319 unsafe { CFNumber::from_raw(ptr) }
320 }
321}
322
323impl CFDateFormatter {
324 #[must_use]
326 pub fn new(
327 locale: Option<&CFLocale>,
328 date_style: CFDateFormatterStyle,
329 time_style: CFDateFormatterStyle,
330 ) -> Self {
331 let ptr = unsafe {
332 ffi::cf_date_formatter_create(
333 locale.map_or(std::ptr::null_mut(), CFLocale::as_ptr),
334 date_style as i32,
335 time_style as i32,
336 )
337 };
338 unsafe { Self::from_raw(ptr) }.expect("CFDateFormatterCreate returned NULL")
339 }
340
341 #[must_use]
343 pub fn format_date(&self, date: &CFDate) -> CFString {
344 let ptr =
345 unsafe { ffi::cf_date_formatter_create_string_with_date(self.as_ptr(), date.as_ptr()) };
346 unsafe { CFString::from_raw(ptr) }
347 .expect("CFDateFormatterCreateStringWithDate returned NULL")
348 }
349}
350
351impl CFFileSecurity {
352 #[must_use]
354 pub fn new() -> Self {
355 let ptr = unsafe { ffi::cf_file_security_create() };
356 unsafe { Self::from_raw(ptr) }.expect("CFFileSecurityCreate returned NULL")
357 }
358
359 #[must_use]
361 pub fn owner_uuid(&self) -> Option<CFUUID> {
362 let ptr = unsafe { ffi::cf_file_security_copy_owner_uuid(self.as_ptr()) };
363 unsafe { CFUUID::from_raw(ptr) }
364 }
365
366 #[must_use]
368 pub fn set_owner_uuid(&self, uuid: &CFUUID) -> bool {
369 unsafe { ffi::cf_file_security_set_owner_uuid(self.as_ptr(), uuid.as_ptr()) }
370 }
371
372 #[must_use]
374 pub fn mode(&self) -> Option<u32> {
375 let mut mode = 0_u32;
376 let ok = unsafe { ffi::cf_file_security_get_mode(self.as_ptr(), &mut mode) };
377 ok.then_some(mode)
378 }
379
380 #[must_use]
382 pub fn set_mode(&self, mode: u32) -> bool {
383 unsafe { ffi::cf_file_security_set_mode(self.as_ptr(), mode) }
384 }
385}
386
387impl Default for CFFileSecurity {
388 fn default() -> Self {
389 Self::new()
390 }
391}
392
393#[derive(Debug)]
395pub struct CFPreferences;
396
397impl CFPreferences {
398 pub fn set_app_value(key: &CFString, value: Option<&dyn AsCFType>, app_id: &CFString) {
400 unsafe {
401 ffi::cf_preferences_set_app_value(
402 key.as_ptr(),
403 value.map_or(std::ptr::null_mut(), AsCFType::as_ptr),
404 app_id.as_ptr(),
405 );
406 }
407 }
408
409 #[must_use]
411 pub fn app_value(key: &CFString, app_id: &CFString) -> Option<CFType> {
412 let ptr = unsafe { ffi::cf_preferences_copy_app_value(key.as_ptr(), app_id.as_ptr()) };
413 unsafe { CFType::from_raw(ptr) }
414 }
415
416 #[must_use]
418 pub fn synchronize(app_id: &CFString) -> bool {
419 unsafe { ffi::cf_preferences_app_synchronize(app_id.as_ptr()) }
420 }
421}
422
423#[derive(Debug)]
425pub struct CFXML;
426
427impl CFXML {
428 #[must_use]
430 pub fn escape_entities(value: &CFString) -> CFString {
431 let ptr = unsafe { ffi::cf_xml_create_string_by_escaping_entities(value.as_ptr()) };
432 unsafe { CFString::from_raw(ptr) }
433 .expect("CFXMLCreateStringByEscapingEntities returned NULL")
434 }
435
436 #[must_use]
438 pub fn unescape_entities(value: &CFString) -> CFString {
439 let ptr = unsafe { ffi::cf_xml_create_string_by_unescaping_entities(value.as_ptr()) };
440 unsafe { CFString::from_raw(ptr) }
441 .expect("CFXMLCreateStringByUnescapingEntities returned NULL")
442 }
443}