Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CFMutableDictionary type. #135

Merged
merged 1 commit into from Jan 20, 2018
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Add CFMutableDictionary type.

Added CFMutableDictionary that presents the mutable dictionary
functions of CF. Tries to present a similar interface as CFDictionary.

Fixes issue #93.
  • Loading branch information
djg committed Nov 29, 2017
commit 6a77e377ba16ed1fa0a6aa10cf4a7720c07f7b68
@@ -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);
}
@@ -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<R1, R2, K, V>(pairs: &[(K, V)]) -> CFMutableDictionary
where K: TCFType<R1>, V: TCFType<R2> {
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<X, K: TCFType<*const X>>(&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<X, K: TCFType<*const X>>(&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<RK, RV, K, V>(&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<RK, RV, K, V>(&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<RK, RV, K, V>(&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<RK, K>(&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) }
}
}
@@ -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)
}
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.