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 = unsafe { Self::create_with_authorization(name, prefs_id, authorization) }?;
148 preferences.set_callback(callback)?;
149 Ok(preferences)
150 }
151
152 fn create(name: &str, prefs_id: Option<&str>) -> Result<Self> {
153 let name = bridge::cstring(name, "sc_preferences_create")?;
154 let prefs_id = bridge::optional_cstring(prefs_id, "sc_preferences_create")?;
155 let raw = unsafe {
156 ffi::preferences::sc_preferences_create(
157 name.as_ptr(),
158 prefs_id.as_ref().map_or(std::ptr::null(), |value| value.as_ptr()),
159 )
160 };
161 let raw = bridge::owned_handle_or_last("sc_preferences_create", raw)?;
162 Ok(Self {
163 raw,
164 callback_state: None,
165 })
166 }
167
168 unsafe fn create_with_authorization(
169 name: &str,
170 prefs_id: Option<&str>,
171 authorization: Option<NonNull<c_void>>,
172 ) -> Result<Self> {
173 let name = bridge::cstring(name, "sc_preferences_create_with_authorization")?;
174 let prefs_id = bridge::optional_cstring(prefs_id, "sc_preferences_create_with_authorization")?;
175 let raw = unsafe {
176 ffi::preferences::sc_preferences_create_with_authorization(
177 name.as_ptr(),
178 prefs_id.as_ref().map_or(std::ptr::null(), |value| value.as_ptr()),
179 authorization.map_or(std::ptr::null_mut(), NonNull::as_ptr),
180 )
181 };
182 let raw = bridge::owned_handle_or_last("sc_preferences_create_with_authorization", raw)?;
183 Ok(Self {
184 raw,
185 callback_state: None,
186 })
187 }
188
189 pub fn set_callback<F>(&mut self, callback: F) -> Result<()>
190 where
191 F: FnMut(PreferencesNotification) + Send + 'static,
192 {
193 let state = Arc::new(Mutex::new(CallbackState {
194 callback: Box::new(callback),
195 }));
196 self.set_callback_state(Some(state))
197 }
198
199 pub fn clear_callback(&mut self) -> Result<()> {
200 self.set_callback_state(None)
201 }
202
203 fn set_callback_state(&mut self, callback: Option<Arc<Mutex<CallbackState>>>) -> Result<()> {
204 let ok = unsafe {
205 ffi::preferences::sc_preferences_set_callback(
206 self.raw.as_ptr(),
207 callback.as_ref().map(|_| preferences_callback as unsafe extern "C" fn(u32, *mut c_void)),
208 callback
209 .as_ref()
210 .map_or(std::ptr::null_mut(), |state| Arc::as_ptr(state).cast_mut().cast::<c_void>()),
211 )
212 };
213 bridge::bool_result("sc_preferences_set_callback", ok)?;
214 self.callback_state = callback;
215 Ok(())
216 }
217
218 pub fn schedule_with_run_loop_current(&self) -> Result<()> {
219 let ok = unsafe { ffi::preferences::sc_preferences_schedule_with_run_loop_current(self.raw.as_ptr()) };
220 bridge::bool_result("sc_preferences_schedule_with_run_loop_current", ok)
221 }
222
223 pub fn unschedule_from_run_loop_current(&self) -> Result<()> {
224 let ok = unsafe {
225 ffi::preferences::sc_preferences_unschedule_from_run_loop_current(self.raw.as_ptr())
226 };
227 bridge::bool_result("sc_preferences_unschedule_from_run_loop_current", ok)
228 }
229
230 pub fn set_dispatch_queue_global(&self) -> Result<()> {
231 let ok = unsafe { ffi::preferences::sc_preferences_set_dispatch_queue_global(self.raw.as_ptr()) };
232 bridge::bool_result("sc_preferences_set_dispatch_queue_global", ok)
233 }
234
235 pub fn clear_dispatch_queue(&self) -> Result<()> {
236 let ok = unsafe { ffi::preferences::sc_preferences_clear_dispatch_queue(self.raw.as_ptr()) };
237 bridge::bool_result("sc_preferences_clear_dispatch_queue", ok)
238 }
239
240 pub fn lock(&self, wait: bool) -> Result<()> {
241 let ok = unsafe { ffi::preferences::sc_preferences_lock(self.raw.as_ptr(), u8::from(wait)) };
242 bridge::bool_result("sc_preferences_lock", ok)
243 }
244
245 pub fn commit_changes(&self) -> Result<()> {
246 let ok = unsafe { ffi::preferences::sc_preferences_commit_changes(self.raw.as_ptr()) };
247 bridge::bool_result("sc_preferences_commit_changes", ok)
248 }
249
250 pub fn apply_changes(&self) -> Result<()> {
251 let ok = unsafe { ffi::preferences::sc_preferences_apply_changes(self.raw.as_ptr()) };
252 bridge::bool_result("sc_preferences_apply_changes", ok)
253 }
254
255 pub fn unlock(&self) -> Result<()> {
256 let ok = unsafe { ffi::preferences::sc_preferences_unlock(self.raw.as_ptr()) };
257 bridge::bool_result("sc_preferences_unlock", ok)
258 }
259
260 pub fn synchronize(&self) {
261 unsafe { ffi::preferences::sc_preferences_synchronize(self.raw.as_ptr()) };
262 }
263
264 pub fn signature(&self) -> Option<String> {
265 bridge::take_optional_string(unsafe { ffi::preferences::sc_preferences_copy_signature(self.raw.as_ptr()) })
266 }
267
268 pub fn copy_key_list(&self) -> Vec<String> {
269 bridge::take_string_array(unsafe { ffi::preferences::sc_preferences_copy_key_list(self.raw.as_ptr()) })
270 }
271
272 pub fn get_value(&self, key: &str) -> Result<Option<PropertyList>> {
273 let key = bridge::cstring(key, "sc_preferences_get_value")?;
274 let raw = unsafe { ffi::preferences::sc_preferences_get_value(self.raw.as_ptr(), key.as_ptr()) };
275 Ok(unsafe { bridge::OwnedHandle::from_raw(raw) }.map(PropertyList::from_owned_handle))
276 }
277
278 pub fn add_value(&self, key: &str, value: &PropertyList) -> Result<()> {
279 let key = bridge::cstring(key, "sc_preferences_add_value")?;
280 let ok = unsafe { ffi::preferences::sc_preferences_add_value(self.raw.as_ptr(), key.as_ptr(), value.as_ptr()) };
281 bridge::bool_result("sc_preferences_add_value", ok)
282 }
283
284 pub fn set_value(&self, key: &str, value: &PropertyList) -> Result<()> {
285 let key = bridge::cstring(key, "sc_preferences_set_value")?;
286 let ok = unsafe { ffi::preferences::sc_preferences_set_value(self.raw.as_ptr(), key.as_ptr(), value.as_ptr()) };
287 bridge::bool_result("sc_preferences_set_value", ok)
288 }
289
290 pub fn remove_value(&self, key: &str) -> Result<()> {
291 let key = bridge::cstring(key, "sc_preferences_remove_value")?;
292 let ok = unsafe { ffi::preferences::sc_preferences_remove_value(self.raw.as_ptr(), key.as_ptr()) };
293 bridge::bool_result("sc_preferences_remove_value", ok)
294 }
295
296 pub fn path_create_unique_child(&self, prefix: &str) -> Result<Option<String>> {
297 let prefix = bridge::cstring(prefix, "sc_preferences_path_create_unique_child")?;
298 Ok(bridge::take_optional_string(unsafe {
299 ffi::preferences::sc_preferences_path_create_unique_child(self.raw.as_ptr(), prefix.as_ptr())
300 }))
301 }
302
303 pub fn path_get_value(&self, path: &str) -> Result<Option<PropertyList>> {
304 let path = bridge::cstring(path, "sc_preferences_path_get_value")?;
305 let raw = unsafe { ffi::preferences::sc_preferences_path_get_value(self.raw.as_ptr(), path.as_ptr()) };
306 Ok(unsafe { bridge::OwnedHandle::from_raw(raw) }.map(PropertyList::from_owned_handle))
307 }
308
309 pub fn path_get_link(&self, path: &str) -> Result<Option<String>> {
310 let path = bridge::cstring(path, "sc_preferences_path_get_link")?;
311 Ok(bridge::take_optional_string(unsafe {
312 ffi::preferences::sc_preferences_path_get_link(self.raw.as_ptr(), path.as_ptr())
313 }))
314 }
315
316 pub fn path_set_value(&self, path: &str, value: &PropertyList) -> Result<()> {
317 let path = bridge::cstring(path, "sc_preferences_path_set_value")?;
318 let ok = unsafe {
319 ffi::preferences::sc_preferences_path_set_value(self.raw.as_ptr(), path.as_ptr(), value.as_ptr())
320 };
321 bridge::bool_result("sc_preferences_path_set_value", ok)
322 }
323
324 pub fn path_set_link(&self, path: &str, link: &str) -> Result<()> {
325 let path = bridge::cstring(path, "sc_preferences_path_set_link")?;
326 let link = bridge::cstring(link, "sc_preferences_path_set_link")?;
327 let ok = unsafe {
328 ffi::preferences::sc_preferences_path_set_link(self.raw.as_ptr(), path.as_ptr(), link.as_ptr())
329 };
330 bridge::bool_result("sc_preferences_path_set_link", ok)
331 }
332
333 pub fn path_remove_value(&self, path: &str) -> Result<()> {
334 let path = bridge::cstring(path, "sc_preferences_path_remove_value")?;
335 let ok = unsafe { ffi::preferences::sc_preferences_path_remove_value(self.raw.as_ptr(), path.as_ptr()) };
336 bridge::bool_result("sc_preferences_path_remove_value", ok)
337 }
338
339 pub fn set_computer_name(&self, name: Option<&str>) -> Result<()> {
340 let name = bridge::optional_cstring(name, "sc_preferences_set_computer_name")?;
341 let ok = unsafe {
342 ffi::preferences::sc_preferences_set_computer_name(
343 self.raw.as_ptr(),
344 name.as_ref().map_or(std::ptr::null(), |value| value.as_ptr()),
345 )
346 };
347 bridge::bool_result("sc_preferences_set_computer_name", ok)
348 }
349
350 pub fn set_local_host_name(&self, name: Option<&str>) -> Result<()> {
351 let name = bridge::optional_cstring(name, "sc_preferences_set_local_host_name")?;
352 let ok = unsafe {
353 ffi::preferences::sc_preferences_set_local_host_name(
354 self.raw.as_ptr(),
355 name.as_ref().map_or(std::ptr::null(), |value| value.as_ptr()),
356 )
357 };
358 bridge::bool_result("sc_preferences_set_local_host_name", ok)
359 }
360
361 pub fn network_services(&self) -> Vec<NetworkService> {
362 NetworkService::copy_all(self)
363 }
364
365 pub(crate) fn as_ptr(&self) -> bridge::RawHandle {
366 self.raw.as_ptr()
367 }
368}