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
49impl_cf_type_wrapper!(CFURL, cf_url_get_type_id);
50impl_cf_type_wrapper!(CFBundle, cf_bundle_get_type_id);
51impl_cf_type_wrapper!(CFLocale, cf_locale_get_type_id);
52impl_cf_type_wrapper!(CFCalendar, cf_calendar_get_type_id);
53impl_cf_type_wrapper!(CFTimeZone, cf_time_zone_get_type_id);
54impl_cf_type_wrapper!(CFCharacterSet, cf_character_set_get_type_id);
55impl_cf_type_wrapper!(CFNumberFormatter, cf_number_formatter_get_type_id);
56impl_cf_type_wrapper!(CFDateFormatter, cf_date_formatter_get_type_id);
57impl_cf_type_wrapper!(CFFileSecurity, cf_file_security_get_type_id);
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
61#[repr(i32)]
62pub enum CFNumberFormatterStyle {
63 NoStyle = 0,
64 Decimal = 1,
65 Currency = 2,
66 Percent = 3,
67 Scientific = 4,
68 SpellOut = 5,
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
73#[repr(i32)]
74pub enum CFDateFormatterStyle {
75 NoStyle = 0,
76 Short = 1,
77 Medium = 2,
78 Long = 3,
79 Full = 4,
80}
81
82impl CFURL {
83 #[must_use]
85 pub fn from_string(value: &str) -> Option<Self> {
86 let value = CString::new(value).ok()?;
87 let ptr = unsafe { ffi::cf_url_create_with_string(value.as_ptr()) };
88 unsafe { Self::from_raw(ptr) }
89 }
90
91 #[must_use]
93 pub fn from_file_system_path(path: &str, is_directory: bool) -> Option<Self> {
94 let path = CString::new(path).ok()?;
95 let ptr = unsafe { ffi::cf_url_create_file_path(path.as_ptr(), is_directory) };
96 unsafe { Self::from_raw(ptr) }
97 }
98
99 #[must_use]
101 pub fn absolute_string(&self) -> CFString {
102 let ptr = unsafe { ffi::cf_url_copy_absolute_string(self.as_ptr()) };
103 unsafe { CFString::from_raw(ptr) }.expect("CFURLCopyAbsoluteString returned NULL")
104 }
105
106 #[must_use]
108 pub fn file_system_path(&self) -> Option<CFString> {
109 let ptr = unsafe { ffi::cf_url_copy_file_system_path(self.as_ptr()) };
110 unsafe { CFString::from_raw(ptr) }
111 }
112
113 #[must_use]
115 pub fn has_directory_path(&self) -> bool {
116 unsafe { ffi::cf_url_has_directory_path(self.as_ptr()) }
117 }
118}
119
120impl CFBundle {
121 #[must_use]
123 pub fn main() -> Option<Self> {
124 let ptr = unsafe { ffi::cf_bundle_get_main() };
125 unsafe { Self::from_raw(ptr) }
126 }
127
128 #[must_use]
130 pub fn from_url(url: &CFURL) -> Option<Self> {
131 let ptr = unsafe { ffi::cf_bundle_create(url.as_ptr()) };
132 unsafe { Self::from_raw(ptr) }
133 }
134
135 #[must_use]
137 pub fn identifier(&self) -> Option<CFString> {
138 let ptr = unsafe { ffi::cf_bundle_copy_identifier(self.as_ptr()) };
139 unsafe { CFString::from_raw(ptr) }
140 }
141
142 #[must_use]
144 pub fn bundle_url(&self) -> CFURL {
145 let ptr = unsafe { ffi::cf_bundle_copy_bundle_url(self.as_ptr()) };
146 unsafe { CFURL::from_raw(ptr) }.expect("CFBundleCopyBundleURL returned NULL")
147 }
148
149 #[must_use]
151 pub fn resource_url(
152 &self,
153 name: &str,
154 extension: Option<&str>,
155 subdir: Option<&str>,
156 ) -> Option<CFURL> {
157 let name = CString::new(name).ok()?;
158 let extension = extension.map(CString::new).transpose().ok()?;
159 let subdir = subdir.map(CString::new).transpose().ok()?;
160 let ptr = unsafe {
161 ffi::cf_bundle_copy_resource_url(
162 self.as_ptr(),
163 name.as_ptr(),
164 extension.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
165 subdir.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
166 )
167 };
168 unsafe { CFURL::from_raw(ptr) }
169 }
170}
171
172impl CFLocale {
173 #[must_use]
175 pub fn current() -> Self {
176 let ptr = unsafe { ffi::cf_locale_copy_current() };
177 unsafe { Self::from_raw(ptr) }.expect("CFLocaleCopyCurrent returned NULL")
178 }
179
180 #[must_use]
182 pub fn new(identifier: &str) -> Self {
183 let identifier = crate::utils::ffi_string::cstring_until_nul(identifier);
184 let ptr = unsafe { ffi::cf_locale_create(identifier.as_ptr()) };
185 unsafe { Self::from_raw(ptr) }.expect("CFLocaleCreate returned NULL")
186 }
187
188 #[must_use]
190 pub fn identifier(&self) -> CFString {
191 let ptr = unsafe { ffi::cf_locale_copy_identifier(self.as_ptr()) };
192 unsafe { CFString::from_raw(ptr) }.expect("CFLocale identifier should be non-null")
193 }
194}
195
196impl CFCalendar {
197 #[must_use]
199 pub fn current() -> Self {
200 let ptr = unsafe { ffi::cf_calendar_copy_current() };
201 unsafe { Self::from_raw(ptr) }.expect("CFCalendarCopyCurrent returned NULL")
202 }
203
204 #[must_use]
206 pub fn new(identifier: &str) -> Option<Self> {
207 let identifier = CString::new(identifier).ok()?;
208 let ptr = unsafe { ffi::cf_calendar_create(identifier.as_ptr()) };
209 unsafe { Self::from_raw(ptr) }
210 }
211
212 #[must_use]
214 pub fn identifier(&self) -> CFString {
215 let ptr = unsafe { ffi::cf_calendar_copy_identifier(self.as_ptr()) };
216 unsafe { CFString::from_raw(ptr) }.expect("CFCalendar identifier should be non-null")
217 }
218
219 #[must_use]
221 pub fn time_zone(&self) -> CFTimeZone {
222 let ptr = unsafe { ffi::cf_calendar_copy_time_zone(self.as_ptr()) };
223 unsafe { CFTimeZone::from_raw(ptr) }.expect("CFCalendarCopyTimeZone returned NULL")
224 }
225
226 pub fn set_time_zone(&self, time_zone: &CFTimeZone) {
228 unsafe { ffi::cf_calendar_set_time_zone(self.as_ptr(), time_zone.as_ptr()) };
229 }
230}
231
232impl CFTimeZone {
233 #[must_use]
235 pub fn current() -> Self {
236 let ptr = unsafe { ffi::cf_time_zone_copy_current() };
237 unsafe { Self::from_raw(ptr) }.expect("CFTimeZoneCopyCurrent returned NULL")
238 }
239
240 #[must_use]
242 pub fn new(name: &str) -> Option<Self> {
243 let name = CString::new(name).ok()?;
244 let ptr = unsafe { ffi::cf_time_zone_create(name.as_ptr()) };
245 unsafe { Self::from_raw(ptr) }
246 }
247
248 #[must_use]
250 pub fn name(&self) -> CFString {
251 let ptr = unsafe { ffi::cf_time_zone_copy_name(self.as_ptr()) };
252 unsafe { CFString::from_raw(ptr) }.expect("CFTimeZoneGetName returned NULL")
253 }
254
255 #[must_use]
257 pub fn seconds_from_gmt(&self, date: &CFDate) -> i32 {
258 unsafe { ffi::cf_time_zone_get_seconds_from_gmt(self.as_ptr(), date.as_ptr()) }
259 }
260}
261
262impl CFCharacterSet {
263 #[must_use]
265 pub fn from_characters_in_string(string: &CFString) -> Self {
266 let ptr =
267 unsafe { ffi::cf_character_set_create_with_characters_in_string(string.as_ptr()) };
268 unsafe { Self::from_raw(ptr) }
269 .expect("CFCharacterSetCreateWithCharactersInString returned NULL")
270 }
271
272 #[must_use]
274 pub fn inverted(&self) -> Self {
275 let ptr = unsafe { ffi::cf_character_set_create_inverted_set(self.as_ptr()) };
276 unsafe { Self::from_raw(ptr) }.expect("CFCharacterSetCreateInvertedSet returned NULL")
277 }
278
279 #[must_use]
281 pub fn contains(&self, character: char) -> bool {
282 unsafe { ffi::cf_character_set_is_character_member(self.as_ptr(), u32::from(character)) }
283 }
284}
285
286impl CFNumberFormatter {
287 #[must_use]
289 pub fn new(locale: Option<&CFLocale>, style: CFNumberFormatterStyle) -> Self {
290 let ptr = unsafe {
291 ffi::cf_number_formatter_create(
292 locale.map_or(std::ptr::null_mut(), CFLocale::as_ptr),
293 style as i32,
294 )
295 };
296 unsafe { Self::from_raw(ptr) }.expect("CFNumberFormatterCreate returned NULL")
297 }
298
299 #[must_use]
301 pub fn format_number(&self, number: &CFNumber) -> CFString {
302 let ptr = unsafe {
303 ffi::cf_number_formatter_create_string_with_number(self.as_ptr(), number.as_ptr())
304 };
305 unsafe { CFString::from_raw(ptr) }
306 .expect("CFNumberFormatterCreateStringWithNumber returned NULL")
307 }
308
309 #[must_use]
311 pub fn parse_number(&self, string: &CFString) -> Option<CFNumber> {
312 let ptr = unsafe {
313 ffi::cf_number_formatter_create_number_from_string(self.as_ptr(), string.as_ptr())
314 };
315 unsafe { CFNumber::from_raw(ptr) }
316 }
317}
318
319impl CFDateFormatter {
320 #[must_use]
322 pub fn new(
323 locale: Option<&CFLocale>,
324 date_style: CFDateFormatterStyle,
325 time_style: CFDateFormatterStyle,
326 ) -> Self {
327 let ptr = unsafe {
328 ffi::cf_date_formatter_create(
329 locale.map_or(std::ptr::null_mut(), CFLocale::as_ptr),
330 date_style as i32,
331 time_style as i32,
332 )
333 };
334 unsafe { Self::from_raw(ptr) }.expect("CFDateFormatterCreate returned NULL")
335 }
336
337 #[must_use]
339 pub fn format_date(&self, date: &CFDate) -> CFString {
340 let ptr =
341 unsafe { ffi::cf_date_formatter_create_string_with_date(self.as_ptr(), date.as_ptr()) };
342 unsafe { CFString::from_raw(ptr) }
343 .expect("CFDateFormatterCreateStringWithDate returned NULL")
344 }
345}
346
347impl CFFileSecurity {
348 #[must_use]
350 pub fn new() -> Self {
351 let ptr = unsafe { ffi::cf_file_security_create() };
352 unsafe { Self::from_raw(ptr) }.expect("CFFileSecurityCreate returned NULL")
353 }
354
355 #[must_use]
357 pub fn owner_uuid(&self) -> Option<CFUUID> {
358 let ptr = unsafe { ffi::cf_file_security_copy_owner_uuid(self.as_ptr()) };
359 unsafe { CFUUID::from_raw(ptr) }
360 }
361
362 #[must_use]
364 pub fn set_owner_uuid(&self, uuid: &CFUUID) -> bool {
365 unsafe { ffi::cf_file_security_set_owner_uuid(self.as_ptr(), uuid.as_ptr()) }
366 }
367
368 #[must_use]
370 pub fn mode(&self) -> Option<u32> {
371 let mut mode = 0_u32;
372 let ok = unsafe { ffi::cf_file_security_get_mode(self.as_ptr(), &raw mut mode) };
373 ok.then_some(mode)
374 }
375
376 #[must_use]
378 pub fn set_mode(&self, mode: u32) -> bool {
379 unsafe { ffi::cf_file_security_set_mode(self.as_ptr(), mode) }
380 }
381}
382
383impl Default for CFFileSecurity {
384 fn default() -> Self {
385 Self::new()
386 }
387}
388
389#[derive(Debug)]
391pub 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 unsafe { 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
419#[derive(Debug)]
421pub struct CFXML;
422
423impl CFXML {
424 #[must_use]
426 pub fn escape_entities(value: &CFString) -> CFString {
427 let ptr = unsafe { ffi::cf_xml_create_string_by_escaping_entities(value.as_ptr()) };
428 unsafe { CFString::from_raw(ptr) }
429 .expect("CFXMLCreateStringByEscapingEntities returned NULL")
430 }
431
432 #[must_use]
434 pub fn unescape_entities(value: &CFString) -> CFString {
435 let ptr = unsafe { ffi::cf_xml_create_string_by_unescaping_entities(value.as_ptr()) };
436 unsafe { CFString::from_raw(ptr) }
437 .expect("CFXMLCreateStringByUnescapingEntities returned NULL")
438 }
439}