1use std::{
2 ffi::c_void,
3 ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign},
4 panic::AssertUnwindSafe,
5 ptr::NonNull,
6 sync::{Arc, Mutex},
7};
8
9use crate::{bridge, error::Result, ffi, network_services::NetworkService, PropertyList};
10
11struct CallbackState {
12 callback: Box<dyn FnMut(PreferencesNotification) + Send>,
13}
14
15unsafe extern "C" fn preferences_callback(notification_type: u32, info: *mut c_void) {
16 if info.is_null() {
17 return;
18 }
19
20 let mutex = unsafe { &*info.cast::<Mutex<CallbackState>>() };
21 if let Ok(mut state) = mutex.lock() {
22 let _ = std::panic::catch_unwind(AssertUnwindSafe(|| {
24 (state.callback)(PreferencesNotification::from_raw(notification_type));
25 }));
26 }
27}
28
29#[derive(Clone, Copy, Debug, Eq, PartialEq)]
30pub struct PreferencesNotification(u32);
32
33impl PreferencesNotification {
34 pub const COMMIT: Self = Self(1 << 0);
36 pub const APPLY: Self = Self(1 << 1);
38
39 pub const fn from_raw(raw: u32) -> Self {
41 Self(raw)
42 }
43
44 pub const fn raw_value(self) -> u32 {
46 self.0
47 }
48
49 pub const fn contains(self, other: Self) -> bool {
51 self.0 & other.0 == other.0
52 }
53}
54
55impl BitOr for PreferencesNotification {
56 type Output = Self;
57
58 fn bitor(self, rhs: Self) -> Self::Output {
59 Self(self.0 | rhs.0)
60 }
61}
62
63impl BitOrAssign for PreferencesNotification {
64 fn bitor_assign(&mut self, rhs: Self) {
65 self.0 |= rhs.0;
66 }
67}
68
69impl BitAnd for PreferencesNotification {
70 type Output = Self;
71
72 fn bitand(self, rhs: Self) -> Self::Output {
73 Self(self.0 & rhs.0)
74 }
75}
76
77impl BitAndAssign for PreferencesNotification {
78 fn bitand_assign(&mut self, rhs: Self) {
79 self.0 &= rhs.0;
80 }
81}
82
83#[derive(Clone)]
84pub struct Preferences {
86 raw: bridge::OwnedHandle,
87 callback_state: Option<Arc<Mutex<CallbackState>>>,
88}
89
90impl std::fmt::Debug for Preferences {
91 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92 f.debug_struct("Preferences").finish_non_exhaustive()
93 }
94}
95
96impl Preferences {
97 pub fn type_id() -> u64 {
99 unsafe { ffi::preferences::sc_preferences_get_type_id() }
100 }
101
102 pub fn new(name: &str, prefs_id: Option<&str>) -> Result<Self> {
104 Self::create(name, prefs_id)
105 }
106
107 pub fn new_with_authorization(name: &str, prefs_id: Option<&str>) -> Result<Self> {
109 unsafe { Self::create_with_authorization(name, prefs_id, None) }
110 }
111
112 pub unsafe fn new_with_authorization_raw(
118 name: &str,
119 prefs_id: Option<&str>,
120 authorization: Option<NonNull<c_void>>,
121 ) -> Result<Self> {
122 unsafe { Self::create_with_authorization(name, prefs_id, authorization) }
123 }
124
125 pub fn new_with_callback<F>(name: &str, prefs_id: Option<&str>, callback: F) -> Result<Self>
127 where
128 F: FnMut(PreferencesNotification) + Send + 'static,
129 {
130 let mut preferences = Self::new(name, prefs_id)?;
131 preferences.set_callback(callback)?;
132 Ok(preferences)
133 }
134
135 pub fn new_with_authorization_and_callback<F>(
137 name: &str,
138 prefs_id: Option<&str>,
139 callback: F,
140 ) -> Result<Self>
141 where
142 F: FnMut(PreferencesNotification) + Send + 'static,
143 {
144 let mut preferences = Self::new_with_authorization(name, prefs_id)?;
145 preferences.set_callback(callback)?;
146 Ok(preferences)
147 }
148
149 pub unsafe fn new_with_authorization_raw_and_callback<F>(
155 name: &str,
156 prefs_id: Option<&str>,
157 authorization: Option<NonNull<c_void>>,
158 callback: F,
159 ) -> Result<Self>
160 where
161 F: FnMut(PreferencesNotification) + Send + 'static,
162 {
163 let mut preferences =
164 unsafe { Self::create_with_authorization(name, prefs_id, authorization) }?;
165 preferences.set_callback(callback)?;
166 Ok(preferences)
167 }
168
169 fn create(name: &str, prefs_id: Option<&str>) -> Result<Self> {
170 let name = bridge::cstring(name, "sc_preferences_create")?;
171 let prefs_id = bridge::optional_cstring(prefs_id, "sc_preferences_create")?;
172 let raw = unsafe {
173 ffi::preferences::sc_preferences_create(
174 name.as_ptr(),
175 prefs_id
176 .as_ref()
177 .map_or(std::ptr::null(), |value| value.as_ptr()),
178 )
179 };
180 let raw = bridge::owned_handle_or_last("sc_preferences_create", raw)?;
181 Ok(Self {
182 raw,
183 callback_state: None,
184 })
185 }
186
187 unsafe fn create_with_authorization(
188 name: &str,
189 prefs_id: Option<&str>,
190 authorization: Option<NonNull<c_void>>,
191 ) -> Result<Self> {
192 let name = bridge::cstring(name, "sc_preferences_create_with_authorization")?;
193 let prefs_id =
194 bridge::optional_cstring(prefs_id, "sc_preferences_create_with_authorization")?;
195 let raw = unsafe {
196 ffi::preferences::sc_preferences_create_with_authorization(
197 name.as_ptr(),
198 prefs_id
199 .as_ref()
200 .map_or(std::ptr::null(), |value| value.as_ptr()),
201 authorization.map_or(std::ptr::null_mut(), NonNull::as_ptr),
202 )
203 };
204 let raw = bridge::owned_handle_or_last("sc_preferences_create_with_authorization", raw)?;
205 Ok(Self {
206 raw,
207 callback_state: None,
208 })
209 }
210
211 pub fn set_callback<F>(&mut self, callback: F) -> Result<()>
213 where
214 F: FnMut(PreferencesNotification) + Send + 'static,
215 {
216 let state = Arc::new(Mutex::new(CallbackState {
217 callback: Box::new(callback),
218 }));
219 self.set_callback_state(Some(state))
220 }
221
222 pub fn clear_callback(&mut self) -> Result<()> {
224 self.set_callback_state(None)
225 }
226
227 fn set_callback_state(&mut self, callback: Option<Arc<Mutex<CallbackState>>>) -> Result<()> {
228 let ok = unsafe {
229 ffi::preferences::sc_preferences_set_callback(
230 self.raw.as_ptr(),
231 callback
232 .as_ref()
233 .map(|_| preferences_callback as unsafe extern "C" fn(u32, *mut c_void)),
234 callback.as_ref().map_or(std::ptr::null_mut(), |state| {
235 Arc::as_ptr(state).cast_mut().cast::<c_void>()
236 }),
237 )
238 };
239 bridge::bool_result("sc_preferences_set_callback", ok)?;
240 self.callback_state = callback;
241 Ok(())
242 }
243
244 pub fn schedule_with_run_loop_current(&self) -> Result<()> {
246 let ok = unsafe {
247 ffi::preferences::sc_preferences_schedule_with_run_loop_current(self.raw.as_ptr())
248 };
249 bridge::bool_result("sc_preferences_schedule_with_run_loop_current", ok)
250 }
251
252 pub fn unschedule_from_run_loop_current(&self) -> Result<()> {
254 let ok = unsafe {
255 ffi::preferences::sc_preferences_unschedule_from_run_loop_current(self.raw.as_ptr())
256 };
257 bridge::bool_result("sc_preferences_unschedule_from_run_loop_current", ok)
258 }
259
260 pub fn set_dispatch_queue_global(&self) -> Result<()> {
262 let ok = unsafe {
263 ffi::preferences::sc_preferences_set_dispatch_queue_global(self.raw.as_ptr())
264 };
265 bridge::bool_result("sc_preferences_set_dispatch_queue_global", ok)
266 }
267
268 pub fn clear_dispatch_queue(&self) -> Result<()> {
270 let ok =
271 unsafe { ffi::preferences::sc_preferences_clear_dispatch_queue(self.raw.as_ptr()) };
272 bridge::bool_result("sc_preferences_clear_dispatch_queue", ok)
273 }
274
275 pub fn lock(&self, wait: bool) -> Result<()> {
277 let ok =
278 unsafe { ffi::preferences::sc_preferences_lock(self.raw.as_ptr(), u8::from(wait)) };
279 bridge::bool_result("sc_preferences_lock", ok)
280 }
281
282 pub fn commit_changes(&self) -> Result<()> {
284 let ok = unsafe { ffi::preferences::sc_preferences_commit_changes(self.raw.as_ptr()) };
285 bridge::bool_result("sc_preferences_commit_changes", ok)
286 }
287
288 pub fn apply_changes(&self) -> Result<()> {
290 let ok = unsafe { ffi::preferences::sc_preferences_apply_changes(self.raw.as_ptr()) };
291 bridge::bool_result("sc_preferences_apply_changes", ok)
292 }
293
294 pub fn unlock(&self) -> Result<()> {
296 let ok = unsafe { ffi::preferences::sc_preferences_unlock(self.raw.as_ptr()) };
297 bridge::bool_result("sc_preferences_unlock", ok)
298 }
299
300 pub fn synchronize(&self) {
302 unsafe { ffi::preferences::sc_preferences_synchronize(self.raw.as_ptr()) };
303 }
304
305 pub fn signature(&self) -> Option<String> {
307 bridge::take_optional_string(unsafe {
308 ffi::preferences::sc_preferences_copy_signature(self.raw.as_ptr())
309 })
310 }
311
312 pub fn copy_key_list(&self) -> Vec<String> {
314 bridge::take_string_array(unsafe {
315 ffi::preferences::sc_preferences_copy_key_list(self.raw.as_ptr())
316 })
317 }
318
319 pub fn get_value(&self, key: &str) -> Result<Option<PropertyList>> {
321 let key = bridge::cstring(key, "sc_preferences_get_value")?;
322 let raw =
323 unsafe { ffi::preferences::sc_preferences_get_value(self.raw.as_ptr(), key.as_ptr()) };
324 Ok(unsafe { bridge::OwnedHandle::from_raw(raw) }.map(PropertyList::from_owned_handle))
325 }
326
327 pub fn add_value(&self, key: &str, value: &PropertyList) -> Result<()> {
329 let key = bridge::cstring(key, "sc_preferences_add_value")?;
330 let ok = unsafe {
331 ffi::preferences::sc_preferences_add_value(
332 self.raw.as_ptr(),
333 key.as_ptr(),
334 value.as_ptr(),
335 )
336 };
337 bridge::bool_result("sc_preferences_add_value", ok)
338 }
339
340 pub fn set_value(&self, key: &str, value: &PropertyList) -> Result<()> {
342 let key = bridge::cstring(key, "sc_preferences_set_value")?;
343 let ok = unsafe {
344 ffi::preferences::sc_preferences_set_value(
345 self.raw.as_ptr(),
346 key.as_ptr(),
347 value.as_ptr(),
348 )
349 };
350 bridge::bool_result("sc_preferences_set_value", ok)
351 }
352
353 pub fn remove_value(&self, key: &str) -> Result<()> {
355 let key = bridge::cstring(key, "sc_preferences_remove_value")?;
356 let ok = unsafe {
357 ffi::preferences::sc_preferences_remove_value(self.raw.as_ptr(), key.as_ptr())
358 };
359 bridge::bool_result("sc_preferences_remove_value", ok)
360 }
361
362 pub fn path_create_unique_child(&self, prefix: &str) -> Result<Option<String>> {
364 let prefix = bridge::cstring(prefix, "sc_preferences_path_create_unique_child")?;
365 Ok(bridge::take_optional_string(unsafe {
366 ffi::preferences::sc_preferences_path_create_unique_child(
367 self.raw.as_ptr(),
368 prefix.as_ptr(),
369 )
370 }))
371 }
372
373 pub fn path_get_value(&self, path: &str) -> Result<Option<PropertyList>> {
375 let path = bridge::cstring(path, "sc_preferences_path_get_value")?;
376 let raw = unsafe {
377 ffi::preferences::sc_preferences_path_get_value(self.raw.as_ptr(), path.as_ptr())
378 };
379 Ok(unsafe { bridge::OwnedHandle::from_raw(raw) }.map(PropertyList::from_owned_handle))
380 }
381
382 pub fn path_get_link(&self, path: &str) -> Result<Option<String>> {
384 let path = bridge::cstring(path, "sc_preferences_path_get_link")?;
385 Ok(bridge::take_optional_string(unsafe {
386 ffi::preferences::sc_preferences_path_get_link(self.raw.as_ptr(), path.as_ptr())
387 }))
388 }
389
390 pub fn path_set_value(&self, path: &str, value: &PropertyList) -> Result<()> {
392 let path = bridge::cstring(path, "sc_preferences_path_set_value")?;
393 let ok = unsafe {
394 ffi::preferences::sc_preferences_path_set_value(
395 self.raw.as_ptr(),
396 path.as_ptr(),
397 value.as_ptr(),
398 )
399 };
400 bridge::bool_result("sc_preferences_path_set_value", ok)
401 }
402
403 pub fn path_set_link(&self, path: &str, link: &str) -> Result<()> {
405 let path = bridge::cstring(path, "sc_preferences_path_set_link")?;
406 let link = bridge::cstring(link, "sc_preferences_path_set_link")?;
407 let ok = unsafe {
408 ffi::preferences::sc_preferences_path_set_link(
409 self.raw.as_ptr(),
410 path.as_ptr(),
411 link.as_ptr(),
412 )
413 };
414 bridge::bool_result("sc_preferences_path_set_link", ok)
415 }
416
417 pub fn path_remove_value(&self, path: &str) -> Result<()> {
419 let path = bridge::cstring(path, "sc_preferences_path_remove_value")?;
420 let ok = unsafe {
421 ffi::preferences::sc_preferences_path_remove_value(self.raw.as_ptr(), path.as_ptr())
422 };
423 bridge::bool_result("sc_preferences_path_remove_value", ok)
424 }
425
426 pub fn set_computer_name(&self, name: Option<&str>) -> Result<()> {
428 let name = bridge::optional_cstring(name, "sc_preferences_set_computer_name")?;
429 let ok = unsafe {
430 ffi::preferences::sc_preferences_set_computer_name(
431 self.raw.as_ptr(),
432 name.as_ref()
433 .map_or(std::ptr::null(), |value| value.as_ptr()),
434 )
435 };
436 bridge::bool_result("sc_preferences_set_computer_name", ok)
437 }
438
439 pub fn set_local_host_name(&self, name: Option<&str>) -> Result<()> {
441 let name = bridge::optional_cstring(name, "sc_preferences_set_local_host_name")?;
442 let ok = unsafe {
443 ffi::preferences::sc_preferences_set_local_host_name(
444 self.raw.as_ptr(),
445 name.as_ref()
446 .map_or(std::ptr::null(), |value| value.as_ptr()),
447 )
448 };
449 bridge::bool_result("sc_preferences_set_local_host_name", ok)
450 }
451
452 pub fn network_services(&self) -> Vec<NetworkService> {
454 NetworkService::copy_all(self)
455 }
456
457 pub(crate) fn as_ptr(&self) -> bridge::RawHandle {
458 self.raw.as_ptr()
459 }
460}