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