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