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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 Self::from_raw(ptr).expect("CFCharacterSetCreateWithCharactersInString returned NULL")
273 }
274
275 #[must_use]
277 pub fn inverted(&self) -> Self {
278 let ptr = unsafe { ffi::cf_character_set_create_inverted_set(self.as_ptr()) };
279 Self::from_raw(ptr).expect("CFCharacterSetCreateInvertedSet returned NULL")
280 }
281
282 #[must_use]
284 pub fn contains(&self, character: char) -> bool {
285 unsafe { ffi::cf_character_set_is_character_member(self.as_ptr(), u32::from(character)) }
286 }
287}
288
289impl CFNumberFormatter {
290 #[must_use]
292 pub fn new(locale: Option<&CFLocale>, style: CFNumberFormatterStyle) -> Self {
293 let ptr = unsafe {
294 ffi::cf_number_formatter_create(
295 locale.map_or(std::ptr::null_mut(), CFLocale::as_ptr),
296 style as i32,
297 )
298 };
299 Self::from_raw(ptr).expect("CFNumberFormatterCreate returned NULL")
300 }
301
302 #[must_use]
304 pub fn format_number(&self, number: &CFNumber) -> CFString {
305 let ptr = unsafe {
306 ffi::cf_number_formatter_create_string_with_number(self.as_ptr(), number.as_ptr())
307 };
308 CFString::from_raw(ptr).expect("CFNumberFormatterCreateStringWithNumber returned NULL")
309 }
310
311 #[must_use]
313 pub fn parse_number(&self, string: &CFString) -> Option<CFNumber> {
314 let ptr = unsafe {
315 ffi::cf_number_formatter_create_number_from_string(self.as_ptr(), string.as_ptr())
316 };
317 CFNumber::from_raw(ptr)
318 }
319}
320
321impl CFDateFormatter {
322 #[must_use]
324 pub fn new(
325 locale: Option<&CFLocale>,
326 date_style: CFDateFormatterStyle,
327 time_style: CFDateFormatterStyle,
328 ) -> Self {
329 let ptr = unsafe {
330 ffi::cf_date_formatter_create(
331 locale.map_or(std::ptr::null_mut(), CFLocale::as_ptr),
332 date_style as i32,
333 time_style as i32,
334 )
335 };
336 Self::from_raw(ptr).expect("CFDateFormatterCreate returned NULL")
337 }
338
339 #[must_use]
341 pub fn format_date(&self, date: &CFDate) -> CFString {
342 let ptr =
343 unsafe { ffi::cf_date_formatter_create_string_with_date(self.as_ptr(), date.as_ptr()) };
344 CFString::from_raw(ptr).expect("CFDateFormatterCreateStringWithDate returned NULL")
345 }
346}
347
348impl CFFileSecurity {
349 #[must_use]
351 pub fn new() -> Self {
352 let ptr = unsafe { ffi::cf_file_security_create() };
353 Self::from_raw(ptr).expect("CFFileSecurityCreate returned NULL")
354 }
355
356 #[must_use]
358 pub fn owner_uuid(&self) -> Option<CFUUID> {
359 let ptr = unsafe { ffi::cf_file_security_copy_owner_uuid(self.as_ptr()) };
360 CFUUID::from_raw(ptr)
361 }
362
363 #[must_use]
365 pub fn set_owner_uuid(&self, uuid: &CFUUID) -> bool {
366 unsafe { ffi::cf_file_security_set_owner_uuid(self.as_ptr(), uuid.as_ptr()) }
367 }
368
369 #[must_use]
371 pub fn mode(&self) -> Option<u32> {
372 let mut mode = 0_u32;
373 let ok = unsafe { ffi::cf_file_security_get_mode(self.as_ptr(), &mut mode) };
374 ok.then_some(mode)
375 }
376
377 #[must_use]
379 pub fn set_mode(&self, mode: u32) -> bool {
380 unsafe { ffi::cf_file_security_set_mode(self.as_ptr(), mode) }
381 }
382}
383
384impl Default for CFFileSecurity {
385 fn default() -> Self {
386 Self::new()
387 }
388}
389
390pub struct CFPreferences;
392
393impl CFPreferences {
394 pub fn set_app_value(key: &CFString, value: Option<&dyn AsCFType>, app_id: &CFString) {
396 unsafe {
397 ffi::cf_preferences_set_app_value(
398 key.as_ptr(),
399 value.map_or(std::ptr::null_mut(), AsCFType::as_ptr),
400 app_id.as_ptr(),
401 );
402 }
403 }
404
405 #[must_use]
407 pub fn app_value(key: &CFString, app_id: &CFString) -> Option<CFType> {
408 let ptr = unsafe { ffi::cf_preferences_copy_app_value(key.as_ptr(), app_id.as_ptr()) };
409 CFType::from_raw(ptr)
410 }
411
412 #[must_use]
414 pub fn synchronize(app_id: &CFString) -> bool {
415 unsafe { ffi::cf_preferences_app_synchronize(app_id.as_ptr()) }
416 }
417}
418
419pub struct CFXML;
421
422impl CFXML {
423 #[must_use]
425 pub fn escape_entities(value: &CFString) -> CFString {
426 let ptr = unsafe { ffi::cf_xml_create_string_by_escaping_entities(value.as_ptr()) };
427 CFString::from_raw(ptr).expect("CFXMLCreateStringByEscapingEntities returned NULL")
428 }
429
430 #[must_use]
432 pub fn unescape_entities(value: &CFString) -> CFString {
433 let ptr = unsafe { ffi::cf_xml_create_string_by_unescaping_entities(value.as_ptr()) };
434 CFString::from_raw(ptr).expect("CFXMLCreateStringByUnescapingEntities returned NULL")
435 }
436}