1use std::{
2 ffi::c_void,
3 sync::{Arc, Mutex},
4};
5
6use crate::{
7 bridge::{self, CStringArray},
8 error::Result,
9 ffi,
10 PropertyList, SystemConfigurationError,
11};
12
13struct CallbackState {
14 callback: Box<dyn FnMut(Vec<String>) + Send>,
15}
16
17unsafe extern "C" fn dynamic_store_callback(changed_keys_raw: bridge::RawHandle, info: *mut c_void) {
18 if info.is_null() {
19 return;
20 }
21
22 let mutex = &*info.cast::<Mutex<CallbackState>>();
23 if let Ok(mut state) = mutex.lock() {
24 (state.callback)(bridge::take_string_array(changed_keys_raw));
25 }
26}
27
28#[derive(Clone)]
29pub struct DynamicStore {
30 raw: bridge::OwnedHandle,
31 _callback: Option<Arc<Mutex<CallbackState>>>,
32}
33
34impl std::fmt::Debug for DynamicStore {
35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 f.debug_struct("DynamicStore").finish_non_exhaustive()
37 }
38}
39
40#[derive(Clone)]
41pub struct DynamicStoreRunLoopSource {
42 raw: bridge::OwnedHandle,
43}
44
45impl std::fmt::Debug for DynamicStoreRunLoopSource {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 f.debug_struct("DynamicStoreRunLoopSource").finish_non_exhaustive()
48 }
49}
50
51impl DynamicStore {
52 pub fn type_id() -> u64 {
53 unsafe { ffi::dynamic_store::sc_dynamic_store_get_type_id() }
54 }
55
56 pub fn new(name: &str) -> Result<Self> {
57 Self::create(name, false, None)
58 }
59
60 pub fn new_with_session_keys(name: &str) -> Result<Self> {
61 Self::create(name, true, None)
62 }
63
64 pub fn new_with_callback<F>(name: &str, callback: F) -> Result<Self>
65 where
66 F: FnMut(Vec<String>) + Send + 'static,
67 {
68 let callback = Arc::new(Mutex::new(CallbackState {
69 callback: Box::new(callback),
70 }));
71 Self::create(name, false, Some(callback))
72 }
73
74 pub fn new_with_session_keys_and_callback<F>(name: &str, callback: F) -> Result<Self>
75 where
76 F: FnMut(Vec<String>) + Send + 'static,
77 {
78 let callback = Arc::new(Mutex::new(CallbackState {
79 callback: Box::new(callback),
80 }));
81 Self::create(name, true, Some(callback))
82 }
83
84 fn create(name: &str, use_session_keys: bool, callback: Option<Arc<Mutex<CallbackState>>>) -> Result<Self> {
85 let name = bridge::cstring(name, "sc_dynamic_store_create")?;
86 let raw = unsafe {
87 callback.as_ref().map_or_else(
88 || ffi::dynamic_store::sc_dynamic_store_create(name.as_ptr(), u8::from(use_session_keys)),
89 |state| {
90 ffi::dynamic_store::sc_dynamic_store_create_with_callback(
91 name.as_ptr(),
92 u8::from(use_session_keys),
93 Some(dynamic_store_callback),
94 Arc::as_ptr(state).cast_mut().cast::<c_void>(),
95 )
96 },
97 )
98 };
99 let raw = bridge::owned_handle_or_last("sc_dynamic_store_create", raw)?;
100 Ok(Self {
101 raw,
102 _callback: callback,
103 })
104 }
105
106 pub fn copy_value(&self, key: &str) -> Result<Option<PropertyList>> {
107 let key = bridge::cstring(key, "sc_dynamic_store_copy_value")?;
108 let raw = unsafe { ffi::dynamic_store::sc_dynamic_store_copy_value(self.raw.as_ptr(), key.as_ptr()) };
109 Ok(unsafe { bridge::OwnedHandle::from_raw(raw) }.map(PropertyList::from_owned_handle))
110 }
111
112 pub fn copy_multiple<K, P>(&self, keys: &[K], patterns: &[P]) -> Result<Option<PropertyList>>
113 where
114 K: AsRef<str>,
115 P: AsRef<str>,
116 {
117 let keys = CStringArray::new(keys, "sc_dynamic_store_copy_multiple")?;
118 let patterns = CStringArray::new(patterns, "sc_dynamic_store_copy_multiple")?;
119 let raw = unsafe {
120 ffi::dynamic_store::sc_dynamic_store_copy_multiple(
121 self.raw.as_ptr(),
122 keys.as_ptr(),
123 keys.count(),
124 patterns.as_ptr(),
125 patterns.count(),
126 )
127 };
128 Ok(unsafe { bridge::OwnedHandle::from_raw(raw) }.map(PropertyList::from_owned_handle))
129 }
130
131 pub fn add_value(&self, key: &str, value: &PropertyList) -> Result<()> {
132 let key = bridge::cstring(key, "sc_dynamic_store_add_value")?;
133 let ok = unsafe {
134 ffi::dynamic_store::sc_dynamic_store_add_value(self.raw.as_ptr(), key.as_ptr(), value.as_ptr())
135 };
136 bridge::bool_result("sc_dynamic_store_add_value", ok)
137 }
138
139 pub fn add_temporary_value(&self, key: &str, value: &PropertyList) -> Result<()> {
140 let key = bridge::cstring(key, "sc_dynamic_store_add_temporary_value")?;
141 let ok = unsafe {
142 ffi::dynamic_store::sc_dynamic_store_add_temporary_value(
143 self.raw.as_ptr(),
144 key.as_ptr(),
145 value.as_ptr(),
146 )
147 };
148 bridge::bool_result("sc_dynamic_store_add_temporary_value", ok)
149 }
150
151 pub fn set_value(&self, key: &str, value: &PropertyList) -> Result<()> {
152 let key = bridge::cstring(key, "sc_dynamic_store_set_value")?;
153 let ok = unsafe {
154 ffi::dynamic_store::sc_dynamic_store_set_value(self.raw.as_ptr(), key.as_ptr(), value.as_ptr())
155 };
156 bridge::bool_result("sc_dynamic_store_set_value", ok)
157 }
158
159 pub fn set_multiple<R, N>(
160 &self,
161 keys_to_set: Option<&PropertyList>,
162 keys_to_remove: &[R],
163 keys_to_notify: &[N],
164 ) -> Result<()>
165 where
166 R: AsRef<str>,
167 N: AsRef<str>,
168 {
169 let keys_to_remove = CStringArray::new(keys_to_remove, "sc_dynamic_store_set_multiple")?;
170 let keys_to_notify = CStringArray::new(keys_to_notify, "sc_dynamic_store_set_multiple")?;
171 let ok = unsafe {
172 ffi::dynamic_store::sc_dynamic_store_set_multiple(
173 self.raw.as_ptr(),
174 keys_to_set.map_or(std::ptr::null_mut(), PropertyList::as_ptr),
175 keys_to_remove.as_ptr(),
176 keys_to_remove.count(),
177 keys_to_notify.as_ptr(),
178 keys_to_notify.count(),
179 )
180 };
181 bridge::bool_result("sc_dynamic_store_set_multiple", ok)
182 }
183
184 pub fn remove_value(&self, key: &str) -> Result<()> {
185 let key = bridge::cstring(key, "sc_dynamic_store_remove_value")?;
186 let ok = unsafe { ffi::dynamic_store::sc_dynamic_store_remove_value(self.raw.as_ptr(), key.as_ptr()) };
187 bridge::bool_result("sc_dynamic_store_remove_value", ok)
188 }
189
190 pub fn notify_value(&self, key: &str) -> Result<()> {
191 let key = bridge::cstring(key, "sc_dynamic_store_notify_value")?;
192 let ok = unsafe { ffi::dynamic_store::sc_dynamic_store_notify_value(self.raw.as_ptr(), key.as_ptr()) };
193 bridge::bool_result("sc_dynamic_store_notify_value", ok)
194 }
195
196 pub fn copy_key_list(&self, pattern: &str) -> Result<Vec<String>> {
197 let pattern = bridge::cstring(pattern, "sc_dynamic_store_copy_key_list")?;
198 let raw = unsafe { ffi::dynamic_store::sc_dynamic_store_copy_key_list(self.raw.as_ptr(), pattern.as_ptr()) };
199 Ok(bridge::take_string_array(raw))
200 }
201
202 pub fn set_notification_keys<K, P>(&self, keys: &[K], patterns: &[P]) -> Result<()>
203 where
204 K: AsRef<str>,
205 P: AsRef<str>,
206 {
207 let keys = CStringArray::new(keys, "sc_dynamic_store_set_notification_keys")?;
208 let patterns = CStringArray::new(patterns, "sc_dynamic_store_set_notification_keys")?;
209 let ok = unsafe {
210 ffi::dynamic_store::sc_dynamic_store_set_notification_keys(
211 self.raw.as_ptr(),
212 keys.as_ptr(),
213 keys.count(),
214 patterns.as_ptr(),
215 patterns.count(),
216 )
217 };
218 bridge::bool_result("sc_dynamic_store_set_notification_keys", ok)
219 }
220
221 pub fn create_run_loop_source(&self, order: isize) -> Result<DynamicStoreRunLoopSource> {
222 let raw = unsafe { ffi::dynamic_store::sc_dynamic_store_create_run_loop_source(self.raw.as_ptr(), order) };
223 let raw = bridge::owned_handle_or_last("sc_dynamic_store_create_run_loop_source", raw)?;
224 Ok(DynamicStoreRunLoopSource { raw })
225 }
226
227 pub fn set_dispatch_queue_global(&self) -> Result<()> {
228 let ok = unsafe { ffi::dynamic_store::sc_dynamic_store_set_dispatch_queue_global(self.raw.as_ptr()) };
229 bridge::bool_result("sc_dynamic_store_set_dispatch_queue_global", ok)
230 }
231
232 pub fn clear_dispatch_queue(&self) -> Result<()> {
233 let ok = unsafe { ffi::dynamic_store::sc_dynamic_store_clear_dispatch_queue(self.raw.as_ptr()) };
234 bridge::bool_result("sc_dynamic_store_clear_dispatch_queue", ok)
235 }
236
237 pub fn copy_notified_keys(&self) -> Vec<String> {
238 let raw = unsafe { ffi::dynamic_store::sc_dynamic_store_copy_notified_keys(self.raw.as_ptr()) };
239 bridge::take_string_array(raw)
240 }
241
242 pub fn computer_name(&self) -> Option<String> {
243 bridge::take_optional_string(unsafe {
244 ffi::dynamic_store::sc_dynamic_store_copy_computer_name(self.raw.as_ptr())
245 })
246 }
247
248 pub fn local_host_name(&self) -> Option<String> {
249 bridge::take_optional_string(unsafe {
250 ffi::dynamic_store::sc_dynamic_store_copy_local_host_name(self.raw.as_ptr())
251 })
252 }
253
254 pub fn location(&self) -> Option<String> {
255 bridge::take_optional_string(unsafe {
256 ffi::dynamic_store::sc_dynamic_store_copy_location(self.raw.as_ptr())
257 })
258 }
259
260 pub fn proxies(&self) -> Option<PropertyList> {
261 let raw = unsafe { ffi::dynamic_store::sc_dynamic_store_copy_proxies(self.raw.as_ptr()) };
262 unsafe { bridge::OwnedHandle::from_raw(raw) }.map(PropertyList::from_owned_handle)
263 }
264
265 pub fn dhcp_info(&self, service_id: Option<&str>) -> Result<Option<PropertyList>> {
266 let service_id = bridge::optional_cstring(service_id, "sc_dynamic_store_copy_dhcp_info")?;
267 let raw = unsafe {
268 ffi::dynamic_store::sc_dynamic_store_copy_dhcp_info(
269 self.raw.as_ptr(),
270 service_id.as_ref().map_or(std::ptr::null(), |value| value.as_ptr()),
271 )
272 };
273 Ok(unsafe { bridge::OwnedHandle::from_raw(raw) }.map(PropertyList::from_owned_handle))
274 }
275
276 pub fn dhcp_option_data(info: &PropertyList, code: u8) -> Option<PropertyList> {
277 unsafe { bridge::OwnedHandle::from_raw(ffi::dynamic_store::sc_dhcp_info_copy_option_data(info.as_ptr(), code)) }
278 .map(PropertyList::from_owned_handle)
279 }
280
281 pub fn dhcp_lease_start_time(info: &PropertyList) -> Option<PropertyList> {
282 unsafe { bridge::OwnedHandle::from_raw(ffi::dynamic_store::sc_dhcp_info_copy_lease_start_time(info.as_ptr())) }
283 .map(PropertyList::from_owned_handle)
284 }
285
286 pub fn dhcp_lease_expiration_time(info: &PropertyList) -> Option<PropertyList> {
287 unsafe {
288 bridge::OwnedHandle::from_raw(ffi::dynamic_store::sc_dhcp_info_copy_lease_expiration_time(
289 info.as_ptr(),
290 ))
291 }
292 .map(PropertyList::from_owned_handle)
293 }
294
295 pub fn key_create<A: AsRef<str>>(format: &str, arguments: &[A]) -> Result<String> {
296 let format = bridge::cstring(format, "sc_dynamic_store_key_create")?;
297 let arguments = CStringArray::new(arguments, "sc_dynamic_store_key_create")?;
298 bridge::take_optional_string(unsafe {
299 ffi::dynamic_store::sc_dynamic_store_key_create(format.as_ptr(), arguments.as_ptr(), arguments.count())
300 })
301 .ok_or_else(|| {
302 SystemConfigurationError::null(
303 "sc_dynamic_store_key_create",
304 "bridge returned null dynamic-store formatted key",
305 )
306 })
307 }
308
309 pub fn network_global_entity_key(domain: &str, entity: &str) -> Result<String> {
310 let domain = bridge::cstring(domain, "sc_dynamic_store_key_create_network_global_entity")?;
311 let entity = bridge::cstring(entity, "sc_dynamic_store_key_create_network_global_entity")?;
312 bridge::take_optional_string(unsafe {
313 ffi::dynamic_store::sc_dynamic_store_key_create_network_global_entity(
314 domain.as_ptr(),
315 entity.as_ptr(),
316 )
317 })
318 .ok_or_else(|| {
319 SystemConfigurationError::null(
320 "sc_dynamic_store_key_create_network_global_entity",
321 "bridge returned null dynamic-store global entity key",
322 )
323 })
324 }
325
326 pub fn network_interface_key(domain: &str) -> Result<String> {
327 let domain = bridge::cstring(domain, "sc_dynamic_store_key_create_network_interface")?;
328 bridge::take_optional_string(unsafe {
329 ffi::dynamic_store::sc_dynamic_store_key_create_network_interface(domain.as_ptr())
330 })
331 .ok_or_else(|| {
332 SystemConfigurationError::null(
333 "sc_dynamic_store_key_create_network_interface",
334 "bridge returned null dynamic-store interface key",
335 )
336 })
337 }
338
339 pub fn network_interface_entity_key(
340 domain: &str,
341 interface_name: &str,
342 entity: Option<&str>,
343 ) -> Result<String> {
344 let domain = bridge::cstring(domain, "sc_dynamic_store_key_create_network_interface_entity")?;
345 let interface_name = bridge::cstring(
346 interface_name,
347 "sc_dynamic_store_key_create_network_interface_entity",
348 )?;
349 let entity = bridge::optional_cstring(entity, "sc_dynamic_store_key_create_network_interface_entity")?;
350 bridge::take_optional_string(unsafe {
351 ffi::dynamic_store::sc_dynamic_store_key_create_network_interface_entity(
352 domain.as_ptr(),
353 interface_name.as_ptr(),
354 entity.as_ref().map_or(std::ptr::null(), |value| value.as_ptr()),
355 )
356 })
357 .ok_or_else(|| {
358 SystemConfigurationError::null(
359 "sc_dynamic_store_key_create_network_interface_entity",
360 "bridge returned null dynamic-store interface-entity key",
361 )
362 })
363 }
364
365 pub fn network_service_entity_key(
366 domain: &str,
367 service_id: &str,
368 entity: Option<&str>,
369 ) -> Result<String> {
370 let domain = bridge::cstring(domain, "sc_dynamic_store_key_create_network_service_entity")?;
371 let service_id = bridge::cstring(service_id, "sc_dynamic_store_key_create_network_service_entity")?;
372 let entity = bridge::optional_cstring(entity, "sc_dynamic_store_key_create_network_service_entity")?;
373 bridge::take_optional_string(unsafe {
374 ffi::dynamic_store::sc_dynamic_store_key_create_network_service_entity(
375 domain.as_ptr(),
376 service_id.as_ptr(),
377 entity.as_ref().map_or(std::ptr::null(), |value| value.as_ptr()),
378 )
379 })
380 .ok_or_else(|| {
381 SystemConfigurationError::null(
382 "sc_dynamic_store_key_create_network_service_entity",
383 "bridge returned null dynamic-store service-entity key",
384 )
385 })
386 }
387
388 pub fn computer_name_key() -> Result<String> {
389 bridge::take_optional_string(unsafe { ffi::dynamic_store::sc_dynamic_store_key_create_computer_name() })
390 .ok_or_else(|| {
391 SystemConfigurationError::null(
392 "sc_dynamic_store_key_create_computer_name",
393 "bridge returned null computer-name notification key",
394 )
395 })
396 }
397
398 pub fn console_user_key() -> Result<String> {
399 bridge::take_optional_string(unsafe { ffi::dynamic_store::sc_dynamic_store_key_create_console_user() })
400 .ok_or_else(|| {
401 SystemConfigurationError::null(
402 "sc_dynamic_store_key_create_console_user",
403 "bridge returned null console-user notification key",
404 )
405 })
406 }
407
408 pub fn host_names_key() -> Result<String> {
409 bridge::take_optional_string(unsafe { ffi::dynamic_store::sc_dynamic_store_key_create_host_names() })
410 .ok_or_else(|| {
411 SystemConfigurationError::null(
412 "sc_dynamic_store_key_create_host_names",
413 "bridge returned null host-names notification key",
414 )
415 })
416 }
417
418 pub fn location_key() -> Result<String> {
419 bridge::take_optional_string(unsafe { ffi::dynamic_store::sc_dynamic_store_key_create_location() })
420 .ok_or_else(|| {
421 SystemConfigurationError::null(
422 "sc_dynamic_store_key_create_location",
423 "bridge returned null location notification key",
424 )
425 })
426 }
427
428 pub fn proxies_key() -> Result<String> {
429 bridge::take_optional_string(unsafe { ffi::dynamic_store::sc_dynamic_store_key_create_proxies() })
430 .ok_or_else(|| {
431 SystemConfigurationError::null(
432 "sc_dynamic_store_key_create_proxies",
433 "bridge returned null proxies notification key",
434 )
435 })
436 }
437
438}
439
440impl DynamicStoreRunLoopSource {
441 pub fn schedule_current_default_mode(&self) -> Result<()> {
442 let ok = unsafe { ffi::dynamic_store::sc_run_loop_source_schedule_current_default_mode(self.raw.as_ptr()) };
443 bridge::bool_result("sc_run_loop_source_schedule_current_default_mode", ok)
444 }
445
446 pub fn unschedule_current_default_mode(&self) -> Result<()> {
447 let ok = unsafe {
448 ffi::dynamic_store::sc_run_loop_source_unschedule_current_default_mode(self.raw.as_ptr())
449 };
450 bridge::bool_result("sc_run_loop_source_unschedule_current_default_mode", ok)
451 }
452}