From 6a77e377ba16ed1fa0a6aa10cf4a7720c07f7b68 Mon Sep 17 00:00:00 2001 From: Dan Glastonbury Date: Mon, 27 Nov 2017 19:32:18 +1000 Subject: [PATCH] Add CFMutableDictionary type. Added CFMutableDictionary that presents the mutable dictionary functions of CF. Tries to present a similar interface as CFDictionary. Fixes issue #93. --- core-foundation-sys/src/dictionary.rs | 20 ++- core-foundation/src/dictionary.rs | 201 +++++++++++++++++++++++++- core-foundation/src/lib.rs | 36 +++++ 3 files changed, 252 insertions(+), 5 deletions(-) diff --git a/core-foundation-sys/src/dictionary.rs b/core-foundation-sys/src/dictionary.rs index 6626cff..61555f7 100644 --- a/core-foundation-sys/src/dictionary.rs +++ b/core-foundation-sys/src/dictionary.rs @@ -67,11 +67,25 @@ extern { pub fn CFDictionaryApplyFunction(theDict: CFDictionaryRef, applier: CFDictionaryApplierFunction, context: *mut c_void); - pub fn CFDictionarySetValue(theDict: CFMutableDictionaryRef, - key: *const c_void, - value: *const c_void); pub fn CFDictionaryGetKeysAndValues(theDict: CFDictionaryRef, keys: *mut *const c_void, values: *mut *const c_void); + pub fn CFDictionaryCreateMutable(allocator: CFAllocatorRef, capacity: CFIndex, + keyCallbacks: *const CFDictionaryKeyCallBacks, + valueCallbacks: *const CFDictionaryValueCallBacks) -> CFMutableDictionaryRef; + pub fn CFDictionaryCreateMutableCopy(allocator: CFAllocatorRef, capacity: CFIndex, + theDict: CFDictionaryRef) -> CFMutableDictionaryRef; + pub fn CFDictionaryAddValue(theDict: CFMutableDictionaryRef, + key: *const c_void, + value: *const c_void); + pub fn CFDictionarySetValue(theDict: CFMutableDictionaryRef, + key: *const c_void, + value: *const c_void); + pub fn CFDictionaryReplaceValue(theDict: CFMutableDictionaryRef, + key: *const c_void, + value: *const c_void); + pub fn CFDictionaryRemoveValue(theDict: CFMutableDictionaryRef, + key: *const c_void); + pub fn CFDictionaryRemoveAllValues(theDict: CFMutableDictionaryRef); } diff --git a/core-foundation/src/dictionary.rs b/core-foundation/src/dictionary.rs index 822eb9c..5d4a12b 100644 --- a/core-foundation/src/dictionary.rs +++ b/core-foundation/src/dictionary.rs @@ -112,9 +112,129 @@ impl CFDictionary { TCFType::wrap_under_get_rule(value) } + pub fn get_keys_and_values(&self) -> (Vec<*const c_void>, Vec<*const c_void>) { + let length = self.len(); + let mut keys = Vec::with_capacity(length); + let mut values = Vec::with_capacity(length); + + unsafe { + CFDictionaryGetKeysAndValues(self.0, keys.as_mut_ptr(), values.as_mut_ptr()); + keys.set_len(length); + values.set_len(length); + } + + (keys, values) + } +} + +/// An mutable dictionary of key-value pairs. +pub struct CFMutableDictionary(CFMutableDictionaryRef); + +impl Drop for CFMutableDictionary { + fn drop(&mut self) { + unsafe { + CFRelease(self.as_CFTypeRef()) + } + } +} + +impl_TCFType!(CFMutableDictionary, CFMutableDictionaryRef, CFDictionaryGetTypeID); +impl_CFTypeDescription!(CFMutableDictionary); + +impl CFMutableDictionary { + pub fn new() -> Self { + Self::with_capacity(0) + } + + pub fn with_capacity(capacity: isize) -> Self { + unsafe { + let dictionary_ref = CFDictionaryCreateMutable(kCFAllocatorDefault, + capacity as _, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + TCFType::wrap_under_create_rule(dictionary_ref) + } + } + + pub fn copy_with_capacity(&self, capacity: isize) -> Self { + unsafe { + let dictionary_ref = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, capacity as _, self.0); + TCFType::wrap_under_get_rule(dictionary_ref) + } + } + + pub fn from_CFType_pairs(pairs: &[(K, V)]) -> CFMutableDictionary + where K: TCFType, V: TCFType { + let result = Self::with_capacity(pairs.len() as _); + unsafe { + for &(ref key, ref value) in pairs { + result.add(key.as_CFTypeRef(), value.as_CFTypeRef()); + } + } + result + } + + // Immutable interface + #[inline] - pub unsafe fn set_value(&self, key: *const c_void, value: *const c_void) { - CFDictionarySetValue(self.0, key, value) + pub fn len(&self) -> usize { + unsafe { + CFDictionaryGetCount(self.0) as usize + } + } + + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + #[inline] + pub fn contains_key(&self, key: *const c_void) -> bool { + unsafe { + CFDictionaryContainsKey(self.0, key) != 0 + } + } + + /// Similar to `contains_key` but acts on a higher level, automatically converting from any + /// `TCFType` to the raw pointer of its concrete TypeRef. + #[inline] + pub fn contains_key2>(&self, key: &K) -> bool { + self.contains_key(key.as_concrete_TypeRef() as *const c_void) + } + + #[inline] + pub fn find(&self, key: *const c_void) -> Option<*const c_void> { + unsafe { + let mut value: *const c_void = ptr::null(); + if CFDictionaryGetValueIfPresent(self.0, key, &mut value) != 0 { + Some(value) + } else { + None + } + } + } + + /// Similar to `find` but acts on a higher level, automatically converting from any `TCFType` + /// to the raw pointer of its concrete TypeRef. + #[inline] + pub fn find2>(&self, key: &K) -> Option<*const c_void> { + self.find(key.as_concrete_TypeRef() as *const c_void) + } + + /// # Panics + /// + /// Panics if the key is not present in the dictionary. Use `find` to get an `Option` instead + /// of panicking. + #[inline] + pub fn get(&self, key: *const c_void) -> *const c_void { + self.find(key).expect(&format!("No entry found for key {:p}", key)) + } + + /// A convenience function to retrieve `CFType` instances. + #[inline] + pub unsafe fn get_CFType(&self, key: *const c_void) -> CFType { + let value: CFTypeRef = mem::transmute(self.get(key)); + TCFType::wrap_under_get_rule(value) } pub fn get_keys_and_values(&self) -> (Vec<*const c_void>, Vec<*const c_void>) { @@ -130,4 +250,81 @@ impl CFDictionary { (keys, values) } + + // Mutable interface + + /// Adds the key-value pair to the dictionary if no such key already exist. + #[inline] + pub unsafe fn add(&self, key: *const c_void, value: *const c_void) { + CFDictionaryAddValue(self.0, key, value) + } + + /// Similar to `add` but acts on a higher level, automatically converting from any `TCFType` + /// to the raw pointer of its concrete TypeRef. + #[inline] + pub fn add2(&self, key: &K, value: &V) + where K: TCFType<*const RK>, + V: TCFType<*const RV> { + unsafe { + self.add(key.as_concrete_TypeRef() as *const _, + value.as_concrete_TypeRef() as *const _) + } + } + + /// Sets the value of the key in the dictionary. + #[inline] + pub unsafe fn set(&self, key: *const c_void, value: *const c_void) { + CFDictionarySetValue(self.0, key, value) + } + + /// Similar to `set` but acts on a higher level, automatically converting from any `TCFType` + /// to the raw pointer of its concrete TypeRef. + #[inline] + pub fn set2(&self, key: &K, value: &V) + where K: TCFType<*const RK>, + V: TCFType<*const RV> { + unsafe { + self.set(key.as_concrete_TypeRef() as *const c_void, + value.as_concrete_TypeRef() as *const c_void) + } + } + + /// Replaces the value of the key in the dictionary. + #[inline] + pub unsafe fn replace(&self, key: *const c_void, value: *const c_void) { + CFDictionaryReplaceValue(self.0, key, value) + } + + /// Similar to `replace` but acts on a higher level, automatically converting from any `TCFType` + /// to the raw pointer of its concrete TypeRef. + #[inline] + pub fn replace2(&self, key: &K, value: &V) + where K: TCFType<*const RK>, + V: TCFType<*const RV> { + unsafe { + self.replace(key.as_concrete_TypeRef() as *const c_void, + value.as_concrete_TypeRef() as *const c_void) + } + } + + /// Removes the value of the key from the dictionary. + #[inline] + pub unsafe fn remove(&self, key: *const c_void) { + CFDictionaryRemoveValue(self.0, key); + } + + /// Similar to `remove` but acts on a higher level, automatically converting from any `TCFType` + /// to the raw pointer of its concrete TypeRef. + #[inline] + pub fn remove2(&self, key: &K) + where K: TCFType<*const RK> { + unsafe { + self.remove(key.as_concrete_TypeRef() as *const c_void) + } + } + + #[inline] + pub fn remove_all(&self) { + unsafe { CFDictionaryRemoveAllValues(self.0) } + } } diff --git a/core-foundation/src/lib.rs b/core-foundation/src/lib.rs index 775cd23..f1f8b4f 100644 --- a/core-foundation/src/lib.rs +++ b/core-foundation/src/lib.rs @@ -219,4 +219,40 @@ pub mod test { assert!(v1 == &[bar.as_CFTypeRef(), baz.as_CFTypeRef(), foo.as_CFTypeRef()]); assert!(v2 == &[boo.as_CFTypeRef(), tru.as_CFTypeRef(), n42.as_CFTypeRef()]); } + + #[test] + fn test_mutable_dictionary() { + use base::TCFType; + use boolean::CFBoolean; + use dictionary::CFMutableDictionary; + use number::CFNumber; + use string::CFString; + + let bar = CFString::from_static_string("Bar"); + let baz = CFString::from_static_string("Baz"); + let boo = CFString::from_static_string("Boo"); + let foo = CFString::from_static_string("Foo"); + let tru = CFBoolean::true_value(); + let n42 = CFNumber::from(42); + + let d = CFMutableDictionary::new(); + d.add2(&bar, &boo); + d.add2(&baz, &tru); + d.add2(&foo, &n42); + assert_eq!(d.len(), 3); + + let (v1, v2) = d.get_keys_and_values(); + assert!(v1 == &[bar.as_CFTypeRef(), baz.as_CFTypeRef(), foo.as_CFTypeRef()]); + assert!(v2 == &[boo.as_CFTypeRef(), tru.as_CFTypeRef(), n42.as_CFTypeRef()]); + + d.remove2(&baz); + assert_eq!(d.len(), 2); + + let (v1, v2) = d.get_keys_and_values(); + assert!(v1 == &[bar.as_CFTypeRef(), foo.as_CFTypeRef()]); + assert!(v2 == &[boo.as_CFTypeRef(), n42.as_CFTypeRef()]); + + d.remove_all(); + assert_eq!(d.len(), 0) + } }