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) -> Option<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 ) -> Option<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 if store.is_null() {
174 None
175 } else {
176 Some(SCDynamicStore::wrap_under_create_rule(store))
177 }
178 }
179 }
180
181 pub fn get_keys<S: Into<CFString>>(&self, pattern: S) -> Option<CFArray<CFString>> {
186 let cf_pattern = pattern.into();
187 unsafe {
188 let array_ref = SCDynamicStoreCopyKeyList(
189 self.as_concrete_TypeRef(),
190 cf_pattern.as_concrete_TypeRef(),
191 );
192 if array_ref.is_null() {
193 None
194 } else {
195 Some(CFArray::wrap_under_create_rule(array_ref))
196 }
197 }
198 }
199
200 pub fn get_proxies(&self) -> Option<CFDictionary<CFString, CFType>> {
203 unsafe {
204 let dictionary_ref = SCDynamicStoreCopyProxies(self.as_concrete_TypeRef());
205 if !dictionary_ref.is_null() {
206 Some(CFDictionary::wrap_under_create_rule(dictionary_ref))
207 } else {
208 None
209 }
210 }
211 }
212
213 pub fn get<S: Into<CFString>>(&self, key: S) -> Option<CFPropertyList> {
217 let cf_key = key.into();
218 unsafe {
219 let dict_ref =
220 SCDynamicStoreCopyValue(self.as_concrete_TypeRef(), cf_key.as_concrete_TypeRef());
221 if !dict_ref.is_null() {
222 Some(CFPropertyList::wrap_under_create_rule(dict_ref))
223 } else {
224 None
225 }
226 }
227 }
228
229 pub fn set<S: Into<CFString>, V: CFPropertyListSubClass>(&self, key: S, value: V) -> bool {
232 self.set_raw(key, &value.into_CFPropertyList())
233 }
234
235 pub fn set_raw<S: Into<CFString>>(&self, key: S, value: &CFPropertyList) -> bool {
238 let cf_key = key.into();
239 let success = unsafe {
240 SCDynamicStoreSetValue(
241 self.as_concrete_TypeRef(),
242 cf_key.as_concrete_TypeRef(),
243 value.as_concrete_TypeRef(),
244 )
245 };
246 success != 0
247 }
248
249 pub fn remove<S: Into<CFString>>(&self, key: S) -> bool {
251 let cf_key = key.into();
252 let success = unsafe {
253 SCDynamicStoreRemoveValue(self.as_concrete_TypeRef(), cf_key.as_concrete_TypeRef())
254 };
255 success != 0
256 }
257
258 pub fn set_notification_keys<T1, T2>(
260 &self,
261 keys: &CFArray<T1>,
262 patterns: &CFArray<T2>,
263 ) -> bool {
264 let success = unsafe {
265 SCDynamicStoreSetNotificationKeys(
266 self.as_concrete_TypeRef(),
267 keys.as_concrete_TypeRef(),
268 patterns.as_concrete_TypeRef(),
269 )
270 };
271 success != 0
272 }
273
274 pub fn create_run_loop_source(&self) -> Option<CFRunLoopSource> {
276 unsafe {
277 let run_loop_source_ref = SCDynamicStoreCreateRunLoopSource(
278 kCFAllocatorDefault,
279 self.as_concrete_TypeRef(),
280 0,
281 );
282 if run_loop_source_ref.is_null() {
283 None
284 } else {
285 Some(CFRunLoopSource::wrap_under_create_rule(run_loop_source_ref))
286 }
287 }
288 }
289}
290
291unsafe extern "C" fn convert_callback<T>(
294 store_ref: SCDynamicStoreRef,
295 changed_keys_ref: CFArrayRef,
296 context_ptr: *mut c_void,
297) {
298 let store = SCDynamicStore::wrap_under_get_rule(store_ref);
299 let changed_keys = CFArray::<CFString>::wrap_under_get_rule(changed_keys_ref);
300 let context = &mut *(context_ptr as *mut _ as *mut SCDynamicStoreCallBackContext<T>);
301
302 (context.callout)(store, changed_keys, &mut context.info);
303}
304
305unsafe extern "C" fn release_callback_context<T>(context_ptr: *const c_void) {
307 let _context = Box::from_raw(context_ptr as *mut SCDynamicStoreCallBackContext<T>);
309}