use alloc::vec::Vec;
use core::ops::{Index, IndexMut};
use core::ptr;
use objc2::rc::{DefaultId, Id, Owned, Shared};
use objc2::{extern_methods, msg_send_id, ClassType, Message};
use crate::Foundation::{
NSArray, NSCopying, NSDictionary, NSFastEnumeration2, NSMutableDictionary,
};
extern_methods!(
unsafe impl<K: Message, V: Message> NSMutableDictionary<K, V> {
#[method_id(new)]
pub fn new() -> Id<Self, Owned>;
pub fn from_keys_and_objects<T>(keys: &[&T], vals: Vec<Id<V, Owned>>) -> Id<Self, Owned>
where
T: NSCopying<Output = K>,
{
let mut dict = NSMutableDictionary::new();
dict.setDictionary(&*NSDictionary::from_keys_and_objects(keys, vals));
dict
}
#[doc(alias = "objectForKey:")]
#[method(objectForKey:)]
pub fn get_mut(&mut self, key: &K) -> Option<&mut V>;
#[method(getObjects:andKeys:)]
unsafe fn get_objects_and_keys(&self, objects: *mut &mut V, keys: *mut &K);
#[doc(alias = "getObjects:andKeys:")]
pub fn values_mut(&mut self) -> Vec<&mut V> {
let len = self.len();
let mut vals: Vec<&mut V> = Vec::with_capacity(len);
unsafe {
self.get_objects_and_keys(vals.as_mut_ptr(), ptr::null_mut());
vals.set_len(len);
}
vals
}
#[method(setObject:forKey:)]
fn set_object_for_key(&mut self, object: &V, key: &K);
#[doc(alias = "setObject:forKey:")]
pub fn insert(&mut self, key: Id<K, Shared>, value: Id<V, Owned>) -> Option<Id<V, Owned>> {
let obj = self.get(&*key).map(|obj| unsafe {
Id::retain_autoreleased(obj as *const V as *mut V).unwrap_unchecked()
});
self.set_object_for_key(&*value, &*key);
obj
}
#[doc(alias = "removeObjectForKey:")]
pub fn remove(&mut self, key: &K) -> Option<Id<V, Owned>> {
let obj = self.get(key).map(|obj| unsafe {
Id::retain_autoreleased(obj as *const V as *mut V).unwrap_unchecked()
});
self.removeObjectForKey(key);
obj
}
pub fn into_values_array(dict: Id<Self, Owned>) -> Id<NSArray<V, Owned>, Shared> {
unsafe { msg_send_id![&dict, allValues] }
}
}
);
unsafe impl<K: Message, V: Message> NSFastEnumeration2 for NSMutableDictionary<K, V> {
type Item = K;
}
impl<'a, K: Message, V: Message> Index<&'a K> for NSMutableDictionary<K, V> {
type Output = V;
fn index<'s>(&'s self, index: &'a K) -> &'s V {
self.get(index).unwrap()
}
}
impl<'a, K: Message, V: Message> IndexMut<&'a K> for NSMutableDictionary<K, V> {
fn index_mut<'s>(&'s mut self, index: &'a K) -> &'s mut V {
self.get_mut(index).unwrap()
}
}
impl<K: Message, V: Message> DefaultId for NSMutableDictionary<K, V> {
type Ownership = Owned;
#[inline]
fn default_id() -> Id<Self, Self::Ownership> {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
use crate::Foundation::{NSNumber, NSObject, NSString};
use objc2::rc::{__RcTestObject, __ThreadTestData};
fn sample_dict() -> Id<NSMutableDictionary<NSNumber, NSObject>, Owned> {
NSMutableDictionary::from_keys_and_objects(
&[
&*NSNumber::new_i32(1),
&*NSNumber::new_i32(2),
&*NSNumber::new_i32(3),
],
vec![NSObject::new(), NSObject::new(), NSObject::new()],
)
}
#[test]
fn test_new() {
let dict = NSMutableDictionary::<NSString, NSObject>::new();
assert!(dict.is_empty());
}
#[test]
fn test_get_mut() {
let mut dict = sample_dict();
assert!(dict.get_mut(&NSNumber::new_i32(1)).is_some());
assert!(dict.get_mut(&NSNumber::new_i32(2)).is_some());
assert!(dict.get_mut(&NSNumber::new_i32(4)).is_none());
}
#[test]
fn test_values_mut() {
let mut dict = sample_dict();
let vec = dict.values_mut();
assert_eq!(vec.len(), 3);
}
#[test]
fn test_insert() {
let mut dict = NSMutableDictionary::new();
assert!(dict.insert(NSNumber::new_i32(1), NSObject::new()).is_none());
assert!(dict.insert(NSNumber::new_i32(2), NSObject::new()).is_none());
assert!(dict.insert(NSNumber::new_i32(3), NSObject::new()).is_none());
assert!(dict.insert(NSNumber::new_i32(1), NSObject::new()).is_some());
assert_eq!(dict.len(), 3);
}
#[test]
fn test_insert_retain_release() {
let mut dict = NSMutableDictionary::new();
dict.insert(NSNumber::new_i32(1), __RcTestObject::new());
let mut expected = __ThreadTestData::current();
let old = dict.insert(NSNumber::new_i32(1), __RcTestObject::new());
expected.alloc += 1;
expected.init += 1;
expected.retain += 2;
expected.release += 2;
expected.assert_current();
drop(old);
expected.release += 1;
expected.dealloc += 1;
expected.assert_current();
}
#[test]
fn test_remove() {
let mut dict = sample_dict();
assert_eq!(dict.len(), 3);
assert!(dict.remove(&NSNumber::new_i32(1)).is_some());
assert!(dict.remove(&NSNumber::new_i32(2)).is_some());
assert!(dict.remove(&NSNumber::new_i32(1)).is_none());
assert!(dict.remove(&NSNumber::new_i32(4)).is_none());
assert_eq!(dict.len(), 1);
}
#[test]
fn test_clear() {
let mut dict = sample_dict();
assert_eq!(dict.len(), 3);
dict.removeAllObjects();
assert!(dict.is_empty());
}
#[test]
fn test_remove_clear_release_dealloc() {
let mut dict = NSMutableDictionary::new();
for i in 0..4 {
dict.insert(NSNumber::new_i32(i), __RcTestObject::new());
}
let mut expected = __ThreadTestData::current();
let _obj = dict.remove(&NSNumber::new_i32(1));
expected.retain += 1;
expected.release += 1;
expected.assert_current();
assert_eq!(dict.len(), 3);
let _obj = dict.remove(&NSNumber::new_i32(2));
expected.retain += 1;
expected.release += 1;
expected.assert_current();
assert_eq!(dict.len(), 2);
dict.removeAllObjects();
expected.release += 2;
expected.dealloc += 2;
expected.assert_current();
assert_eq!(dict.len(), 0);
}
#[test]
fn test_into_values_array() {
let dict = sample_dict();
let array = NSMutableDictionary::into_values_array(dict);
assert_eq!(array.len(), 3);
}
}