1use crate::sys::{
16 dynamic_store::{
17 kSCDynamicStoreUseSessionKeys, SCDynamicStoreCallBack, SCDynamicStoreContext,
18 SCDynamicStoreCopyKeyList, SCDynamicStoreCopyValue, SCDynamicStoreCreateRunLoopSource,
19 SCDynamicStoreCreateWithOptions, SCDynamicStoreGetTypeID, SCDynamicStoreRef,
20 SCDynamicStoreRemoveValue, SCDynamicStoreSetNotificationKeys, SCDynamicStoreSetValue,
21 },
22 dynamic_store_copy_specific::SCDynamicStoreCopyProxies,
23};
24use core_foundation::{
25 array::{CFArray, CFArrayRef},
26 base::{kCFAllocatorDefault, CFType, TCFType},
27 boolean::CFBoolean,
28 dictionary::CFDictionary,
29 propertylist::{CFPropertyList, CFPropertyListSubClass},
30 runloop::CFRunLoopSource,
31 string::CFString,
32};
33use std::{ffi::c_void, ptr};
34
35pub struct SCDynamicStoreCallBackContext<T> {
37 pub callout: SCDynamicStoreCallBackT<T>,
40
41 pub info: T,
44}
45
46pub type SCDynamicStoreCallBackT<T> =
52 fn(store: SCDynamicStore, changed_keys: CFArray<CFString>, info: &mut T);
53
54pub struct SCDynamicStoreBuilder<T> {
58 name: CFString,
59 session_keys: bool,
60 callback_context: Option<SCDynamicStoreCallBackContext<T>>,
61}
62
63impl SCDynamicStoreBuilder<()> {
64 pub fn new<S: Into<CFString>>(name: S) -> Self {
69 SCDynamicStoreBuilder {
70 name: name.into(),
71 session_keys: false,
72 callback_context: None,
73 }
74 }
75}
76
77impl<T> SCDynamicStoreBuilder<T> {
78 pub fn session_keys(mut self, session_keys: bool) -> Self {
86 self.session_keys = session_keys;
87 self
88 }
89
90 pub fn callback_context<T2>(
94 self,
95 callback_context: SCDynamicStoreCallBackContext<T2>,
96 ) -> SCDynamicStoreBuilder<T2> {
97 SCDynamicStoreBuilder {
98 name: self.name,
99 session_keys: self.session_keys,
100 callback_context: Some(callback_context),
101 }
102 }
103
104 pub fn build(mut self) -> SCDynamicStore {
106 let store_options = self.create_store_options();
107 if let Some(callback_context) = self.callback_context.take() {
108 SCDynamicStore::create(
109 &self.name,
110 &store_options,
111 Some(convert_callback::<T>),
112 &mut self.create_context(callback_context),
113 )
114 } else {
115 SCDynamicStore::create(&self.name, &store_options, None, ptr::null_mut())
116 }
117 }
118
119 fn create_store_options(&self) -> CFDictionary {
120 let key = unsafe { CFString::wrap_under_create_rule(kSCDynamicStoreUseSessionKeys) };
121 let value = CFBoolean::from(self.session_keys);
122 let typed_dict = CFDictionary::from_CFType_pairs(&[(key, value)]);
123 unsafe { CFDictionary::wrap_under_get_rule(typed_dict.as_concrete_TypeRef()) }
124 }
125
126 fn create_context(
127 &self,
128 callback_context: SCDynamicStoreCallBackContext<T>,
129 ) -> SCDynamicStoreContext {
130 let info_ptr = Box::into_raw(Box::new(callback_context));
134
135 SCDynamicStoreContext {
136 version: 0,
137 info: info_ptr as *mut _ as *mut c_void,
138 retain: None,
139 release: Some(release_callback_context::<T>),
140 copyDescription: None,
141 }
142 }
143}
144
145declare_TCFType! {
146 SCDynamicStore, SCDynamicStoreRef
152}
153
154impl_TCFType!(SCDynamicStore, SCDynamicStoreRef, SCDynamicStoreGetTypeID);
155
156impl SCDynamicStore {
157 fn create(
160 name: &CFString,
161 store_options: &CFDictionary,
162 callout: SCDynamicStoreCallBack,
163 context: *mut SCDynamicStoreContext,
164 ) -> Self {
165 unsafe {
166 let store = SCDynamicStoreCreateWithOptions(
167 kCFAllocatorDefault,
168 name.as_concrete_TypeRef(),
169 store_options.as_concrete_TypeRef(),
170 callout,
171 context,
172 );
173 SCDynamicStore::wrap_under_create_rule(store)
174 }
175 }
176
177 pub fn get_keys<S: Into<CFString>>(&self, pattern: S) -> Option<CFArray<CFString>> {
182 let cf_pattern = pattern.into();
183 unsafe {
184 let array_ref = SCDynamicStoreCopyKeyList(
185 self.as_concrete_TypeRef(),
186 cf_pattern.as_concrete_TypeRef(),
187 );
188 if !array_ref.is_null() {
189 Some(CFArray::wrap_under_create_rule(array_ref))
190 } else {
191 None
192 }
193 }
194 }
195
196 pub fn get_proxies(&self) -> Option<CFDictionary<CFString, CFType>> {
199 unsafe {
200 let dictionary_ref = SCDynamicStoreCopyProxies(self.as_concrete_TypeRef());
201 if !dictionary_ref.is_null() {
202 Some(CFDictionary::wrap_under_create_rule(dictionary_ref))
203 } else {
204 None
205 }
206 }
207 }
208
209 pub fn get<S: Into<CFString>>(&self, key: S) -> Option<CFPropertyList> {
213 let cf_key = key.into();
214 unsafe {
215 let dict_ref =
216 SCDynamicStoreCopyValue(self.as_concrete_TypeRef(), cf_key.as_concrete_TypeRef());
217 if !dict_ref.is_null() {
218 Some(CFPropertyList::wrap_under_create_rule(dict_ref))
219 } else {
220 None
221 }
222 }
223 }
224
225 pub fn set<S: Into<CFString>, V: CFPropertyListSubClass>(&self, key: S, value: V) -> bool {
228 self.set_raw(key, &value.into_CFPropertyList())
229 }
230
231 pub fn set_raw<S: Into<CFString>>(&self, key: S, value: &CFPropertyList) -> bool {
234 let cf_key = key.into();
235 let success = unsafe {
236 SCDynamicStoreSetValue(
237 self.as_concrete_TypeRef(),
238 cf_key.as_concrete_TypeRef(),
239 value.as_concrete_TypeRef(),
240 )
241 };
242 success != 0
243 }
244
245 pub fn remove<S: Into<CFString>>(&self, key: S) -> bool {
247 let cf_key = key.into();
248 let success = unsafe {
249 SCDynamicStoreRemoveValue(self.as_concrete_TypeRef(), cf_key.as_concrete_TypeRef())
250 };
251 success != 0
252 }
253
254 pub fn set_notification_keys<T1, T2>(
256 &self,
257 keys: &CFArray<T1>,
258 patterns: &CFArray<T2>,
259 ) -> bool {
260 let success = unsafe {
261 SCDynamicStoreSetNotificationKeys(
262 self.as_concrete_TypeRef(),
263 keys.as_concrete_TypeRef(),
264 patterns.as_concrete_TypeRef(),
265 )
266 };
267 success != 0
268 }
269
270 pub fn create_run_loop_source(&self) -> CFRunLoopSource {
272 unsafe {
273 let run_loop_source_ref = SCDynamicStoreCreateRunLoopSource(
274 kCFAllocatorDefault,
275 self.as_concrete_TypeRef(),
276 0,
277 );
278 CFRunLoopSource::wrap_under_create_rule(run_loop_source_ref)
279 }
280 }
281}
282
283unsafe extern "C" fn convert_callback<T>(
286 store_ref: SCDynamicStoreRef,
287 changed_keys_ref: CFArrayRef,
288 context_ptr: *mut c_void,
289) {
290 let store = SCDynamicStore::wrap_under_get_rule(store_ref);
291 let changed_keys = CFArray::<CFString>::wrap_under_get_rule(changed_keys_ref);
292 let context = &mut *(context_ptr as *mut _ as *mut SCDynamicStoreCallBackContext<T>);
293
294 (context.callout)(store, changed_keys, &mut context.info);
295}
296
297unsafe extern "C" fn release_callback_context<T>(context_ptr: *const c_void) {
299 let _context = Box::from_raw(context_ptr as *mut SCDynamicStoreCallBackContext<T>);
301}