1pub use core_foundation_sys::dictionary::*;
13
14use core_foundation_sys::base::{kCFAllocatorDefault, CFRelease, CFTypeRef};
15use std::marker::PhantomData;
16use std::mem;
17use std::os::raw::c_void;
18use std::ptr;
19
20use crate::base::{CFIndexConvertible, TCFType};
21use crate::base::{FromVoid, ItemRef, ToVoid};
22use crate::ConcreteCFType;
23
24pub struct CFDictionary<K = *const c_void, V = *const c_void>(
26 CFDictionaryRef,
27 PhantomData<K>,
28 PhantomData<V>,
29);
30
31impl<K, V> Drop for CFDictionary<K, V> {
32 fn drop(&mut self) {
33 unsafe { CFRelease(self.as_CFTypeRef()) }
34 }
35}
36
37impl_TCFType!(CFDictionary<K, V>, CFDictionaryRef, CFDictionaryGetTypeID);
38impl_CFTypeDescription!(CFDictionary<K, V>);
39
40unsafe impl ConcreteCFType for CFDictionary<*const c_void, *const c_void> {}
41
42impl<K, V> CFDictionary<K, V> {
43 pub fn from_CFType_pairs(pairs: &[(K, V)]) -> CFDictionary<K, V>
44 where
45 K: TCFType,
46 V: TCFType,
47 {
48 let (keys, values): (Vec<CFTypeRef>, Vec<CFTypeRef>) = pairs
49 .iter()
50 .map(|(key, value)| (key.as_CFTypeRef(), value.as_CFTypeRef()))
51 .unzip();
52
53 unsafe {
54 let dictionary_ref = CFDictionaryCreate(
55 kCFAllocatorDefault,
56 keys.as_ptr(),
57 values.as_ptr(),
58 keys.len().to_CFIndex(),
59 &kCFTypeDictionaryKeyCallBacks,
60 &kCFTypeDictionaryValueCallBacks,
61 );
62 TCFType::wrap_under_create_rule(dictionary_ref)
63 }
64 }
65
66 #[inline]
67 pub fn to_untyped(&self) -> CFDictionary {
68 unsafe { CFDictionary::wrap_under_get_rule(self.0) }
69 }
70
71 #[inline]
74 pub unsafe fn to_mutable(&self) -> CFMutableDictionary<K, V> {
75 CFMutableDictionary::wrap_under_get_rule(self.0 as CFMutableDictionaryRef)
76 }
77
78 #[inline]
81 pub fn into_untyped(self) -> CFDictionary {
82 let reference = self.0;
83 mem::forget(self);
84 unsafe { CFDictionary::wrap_under_create_rule(reference) }
85 }
86
87 #[inline]
88 pub fn len(&self) -> usize {
89 unsafe { CFDictionaryGetCount(self.0) as usize }
90 }
91
92 #[inline]
93 pub fn is_empty(&self) -> bool {
94 self.len() == 0
95 }
96
97 #[inline]
98 pub fn contains_key(&self, key: &K) -> bool
99 where
100 K: ToVoid<K>,
101 {
102 unsafe { CFDictionaryContainsKey(self.0, key.to_void()) != 0 }
103 }
104
105 #[inline]
106 pub fn find<T: ToVoid<K>>(&self, key: T) -> Option<ItemRef<'_, V>>
107 where
108 V: FromVoid,
109 K: ToVoid<K>,
110 {
111 unsafe {
112 let mut value: *const c_void = ptr::null();
113 if CFDictionaryGetValueIfPresent(self.0, key.to_void(), &mut value) != 0 {
114 Some(V::from_void(value))
115 } else {
116 None
117 }
118 }
119 }
120
121 #[inline]
126 pub fn get<T: ToVoid<K>>(&self, key: T) -> ItemRef<'_, V>
127 where
128 V: FromVoid,
129 K: ToVoid<K>,
130 {
131 let ptr = key.to_void();
132 self.find(key)
133 .unwrap_or_else(|| panic!("No entry found for key {:p}", ptr))
134 }
135
136 pub fn get_keys_and_values(&self) -> (Vec<*const c_void>, Vec<*const c_void>) {
137 let length = self.len();
138 let mut keys = Vec::with_capacity(length);
139 let mut values = Vec::with_capacity(length);
140
141 unsafe {
142 CFDictionaryGetKeysAndValues(self.0, keys.as_mut_ptr(), values.as_mut_ptr());
143 keys.set_len(length);
144 values.set_len(length);
145 }
146
147 (keys, values)
148 }
149}
150
151pub struct CFMutableDictionary<K = *const c_void, V = *const c_void>(
153 CFMutableDictionaryRef,
154 PhantomData<K>,
155 PhantomData<V>,
156);
157
158impl<K, V> Drop for CFMutableDictionary<K, V> {
159 fn drop(&mut self) {
160 unsafe { CFRelease(self.as_CFTypeRef()) }
161 }
162}
163
164impl_TCFType!(CFMutableDictionary<K, V>, CFMutableDictionaryRef, CFDictionaryGetTypeID);
165impl_CFTypeDescription!(CFMutableDictionary);
166
167impl<K, V> CFMutableDictionary<K, V> {
168 pub fn new() -> Self {
169 Self::with_capacity(0)
170 }
171
172 pub fn with_capacity(capacity: isize) -> Self {
173 unsafe {
174 let dictionary_ref = CFDictionaryCreateMutable(
175 kCFAllocatorDefault,
176 capacity as _,
177 &kCFTypeDictionaryKeyCallBacks,
178 &kCFTypeDictionaryValueCallBacks,
179 );
180 TCFType::wrap_under_create_rule(dictionary_ref)
181 }
182 }
183
184 pub fn copy_with_capacity(&self, capacity: isize) -> Self {
185 unsafe {
186 let dictionary_ref =
187 CFDictionaryCreateMutableCopy(kCFAllocatorDefault, capacity as _, self.0);
188 TCFType::wrap_under_get_rule(dictionary_ref)
189 }
190 }
191
192 pub fn from_CFType_pairs(pairs: &[(K, V)]) -> CFMutableDictionary<K, V>
193 where
194 K: ToVoid<K>,
195 V: ToVoid<V>,
196 {
197 let mut result = Self::with_capacity(pairs.len() as _);
198 for (key, value) in pairs {
199 result.add(key, value);
200 }
201 result
202 }
203
204 #[inline]
205 pub fn to_untyped(&self) -> CFMutableDictionary {
206 unsafe { CFMutableDictionary::wrap_under_get_rule(self.0) }
207 }
208
209 #[inline]
212 pub fn into_untyped(self) -> CFMutableDictionary {
213 let reference = self.0;
214 mem::forget(self);
215 unsafe { CFMutableDictionary::wrap_under_create_rule(reference) }
216 }
217
218 #[inline]
220 pub fn to_immutable(&self) -> CFDictionary<K, V> {
221 unsafe { CFDictionary::wrap_under_get_rule(self.0) }
222 }
223
224 #[inline]
227 pub fn len(&self) -> usize {
228 unsafe { CFDictionaryGetCount(self.0) as usize }
229 }
230
231 #[inline]
232 pub fn is_empty(&self) -> bool {
233 self.len() == 0
234 }
235
236 #[inline]
237 pub fn contains_key(&self, key: *const c_void) -> bool {
238 unsafe { CFDictionaryContainsKey(self.0, key) != 0 }
239 }
240
241 #[inline]
242 pub fn find<'a>(&'a self, key: &K) -> Option<ItemRef<'a, V>>
243 where
244 V: FromVoid,
245 K: ToVoid<K>,
246 {
247 unsafe {
248 let mut value: *const c_void = ptr::null();
249 if CFDictionaryGetValueIfPresent(self.0, key.to_void(), &mut value) != 0 {
250 Some(V::from_void(value))
251 } else {
252 None
253 }
254 }
255 }
256
257 #[inline]
262 pub fn get<'a>(&'a self, key: &K) -> ItemRef<'a, V>
263 where
264 V: FromVoid,
265 K: ToVoid<K>,
266 {
267 let ptr = key.to_void();
268 self.find(key)
269 .unwrap_or_else(|| panic!("No entry found for key {:p}", ptr))
270 }
271
272 pub fn get_keys_and_values(&self) -> (Vec<*const c_void>, Vec<*const c_void>) {
273 let length = self.len();
274 let mut keys = Vec::with_capacity(length);
275 let mut values = Vec::with_capacity(length);
276
277 unsafe {
278 CFDictionaryGetKeysAndValues(self.0, keys.as_mut_ptr(), values.as_mut_ptr());
279 keys.set_len(length);
280 values.set_len(length);
281 }
282
283 (keys, values)
284 }
285
286 #[inline]
290 pub fn add(&mut self, key: &K, value: &V)
291 where
292 K: ToVoid<K>,
293 V: ToVoid<V>,
294 {
295 unsafe { CFDictionaryAddValue(self.0, key.to_void(), value.to_void()) }
296 }
297
298 #[inline]
300 pub fn set(&mut self, key: K, value: V)
301 where
302 K: ToVoid<K>,
303 V: ToVoid<V>,
304 {
305 unsafe { CFDictionarySetValue(self.0, key.to_void(), value.to_void()) }
306 }
307
308 #[inline]
310 pub fn replace(&mut self, key: K, value: V)
311 where
312 K: ToVoid<K>,
313 V: ToVoid<V>,
314 {
315 unsafe { CFDictionaryReplaceValue(self.0, key.to_void(), value.to_void()) }
316 }
317
318 #[inline]
320 pub fn remove(&mut self, key: K)
321 where
322 K: ToVoid<K>,
323 {
324 unsafe { CFDictionaryRemoveValue(self.0, key.to_void()) }
325 }
326
327 #[inline]
328 pub fn remove_all(&mut self) {
329 unsafe { CFDictionaryRemoveAllValues(self.0) }
330 }
331}
332
333impl<K, V> Default for CFMutableDictionary<K, V> {
334 fn default() -> Self {
335 Self::new()
336 }
337}
338
339impl<'a, K, V> From<&'a CFDictionary<K, V>> for CFMutableDictionary<K, V> {
340 fn from(dict: &'a CFDictionary<K, V>) -> Self {
343 unsafe {
344 let mut_dict_ref = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, dict.0);
345 TCFType::wrap_under_create_rule(mut_dict_ref)
346 }
347 }
348}
349
350#[cfg(test)]
351pub mod test {
352 use super::*;
353 use crate::base::{CFType, TCFType};
354 use crate::boolean::CFBoolean;
355 use crate::number::CFNumber;
356 use crate::string::CFString;
357
358 #[test]
359 fn dictionary() {
360 let bar = CFString::from_static_string("Bar");
361 let baz = CFString::from_static_string("Baz");
362 let boo = CFString::from_static_string("Boo");
363 let foo = CFString::from_static_string("Foo");
364 let tru = CFBoolean::true_value();
365 let n42 = CFNumber::from(42);
366
367 let d = CFDictionary::from_CFType_pairs(&[
368 (bar.as_CFType(), boo.as_CFType()),
369 (baz.as_CFType(), tru.as_CFType()),
370 (foo.as_CFType(), n42.as_CFType()),
371 ]);
372
373 let (v1, v2) = d.get_keys_and_values();
374 assert_eq!(
375 v1,
376 &[bar.as_CFTypeRef(), baz.as_CFTypeRef(), foo.as_CFTypeRef()]
377 );
378 assert_eq!(
379 v2,
380 &[boo.as_CFTypeRef(), tru.as_CFTypeRef(), n42.as_CFTypeRef()]
381 );
382 }
383
384 #[test]
385 fn mutable_dictionary() {
386 let bar = CFString::from_static_string("Bar");
387 let baz = CFString::from_static_string("Baz");
388 let boo = CFString::from_static_string("Boo");
389 let foo = CFString::from_static_string("Foo");
390 let tru = CFBoolean::true_value();
391 let n42 = CFNumber::from(42);
392
393 let mut d = CFMutableDictionary::<CFString, CFType>::new();
394 d.add(&bar, &boo.as_CFType());
395 d.add(&baz, &tru.as_CFType());
396 d.add(&foo, &n42.as_CFType());
397 assert_eq!(d.len(), 3);
398
399 let (v1, v2) = d.get_keys_and_values();
400 assert_eq!(
401 v1,
402 &[bar.as_CFTypeRef(), baz.as_CFTypeRef(), foo.as_CFTypeRef()]
403 );
404 assert_eq!(
405 v2,
406 &[boo.as_CFTypeRef(), tru.as_CFTypeRef(), n42.as_CFTypeRef()]
407 );
408
409 d.remove(baz);
410 assert_eq!(d.len(), 2);
411
412 let (v1, v2) = d.get_keys_and_values();
413 assert_eq!(v1, &[bar.as_CFTypeRef(), foo.as_CFTypeRef()]);
414 assert_eq!(v2, &[boo.as_CFTypeRef(), n42.as_CFTypeRef()]);
415
416 d.remove_all();
417 assert_eq!(d.len(), 0)
418 }
419
420 #[test]
421 fn dict_find_and_contains_key() {
422 let dict = CFDictionary::from_CFType_pairs(&[(
423 CFString::from_static_string("hello"),
424 CFBoolean::true_value(),
425 )]);
426 let key = CFString::from_static_string("hello");
427 let invalid_key = CFString::from_static_string("foobar");
428
429 assert!(dict.contains_key(&key));
430 assert!(!dict.contains_key(&invalid_key));
431
432 let value = dict.find(&key).unwrap().clone();
433 assert_eq!(value, CFBoolean::true_value());
434 assert_eq!(dict.find(&invalid_key), None);
435 }
436
437 #[test]
438 fn convert_immutable_to_mutable_dict() {
439 let dict: CFDictionary<CFString, CFBoolean> = CFDictionary::from_CFType_pairs(&[(
440 CFString::from_static_string("Foo"),
441 CFBoolean::true_value(),
442 )]);
443 let mut mut_dict = CFMutableDictionary::from(&dict);
444 assert_eq!(dict.retain_count(), 1);
445 assert_eq!(mut_dict.retain_count(), 1);
446
447 assert_eq!(mut_dict.len(), 1);
448 assert_eq!(
449 *mut_dict.get(&CFString::from_static_string("Foo")),
450 CFBoolean::true_value()
451 );
452
453 mut_dict.add(
454 &CFString::from_static_string("Bar"),
455 &CFBoolean::false_value(),
456 );
457 assert_eq!(dict.len(), 1);
458 assert_eq!(mut_dict.len(), 2);
459 }
460
461 #[test]
462 fn mutable_dictionary_as_immutable() {
463 let mut mut_dict: CFMutableDictionary<CFString, CFBoolean> = CFMutableDictionary::new();
464 mut_dict.add(
465 &CFString::from_static_string("Bar"),
466 &CFBoolean::false_value(),
467 );
468 assert_eq!(mut_dict.retain_count(), 1);
469
470 let dict = mut_dict.to_immutable();
471 assert_eq!(mut_dict.retain_count(), 2);
472 assert_eq!(dict.retain_count(), 2);
473 assert_eq!(
474 *dict.get(&CFString::from_static_string("Bar")),
475 CFBoolean::false_value()
476 );
477
478 mem::drop(dict);
479 assert_eq!(mut_dict.retain_count(), 1);
480 }
481}