Skip to main content

mtl_foundation/
dictionary.rs

1//! Dictionary type for Foundation.
2//!
3//! Corresponds to `Foundation/NSDictionary.hpp`.
4//!
5//! # C++ Equivalent
6//!
7//! ```cpp
8//! namespace NS {
9//! class Dictionary : public NS::Copying<Dictionary> {
10//! public:
11//!     static Dictionary* dictionary();
12//!     static Dictionary* dictionary(const Object* pObject, const Object* pKey);
13//!     static Dictionary* dictionary(const Object* const* pObjects, const Object* const* pKeys, UInteger count);
14//!     static Dictionary* alloc();
15//!     Dictionary*        init();
16//!     Dictionary*        init(const Object* const* pObjects, const Object* const* pKeys, UInteger count);
17//!     Dictionary*        init(const class Coder* pCoder);
18//!     template <class _KeyType = Object>
19//!     Enumerator<_KeyType>* keyEnumerator() const;
20//!     template <class _Object = Object>
21//!     _Object* object(const Object* pKey) const;
22//!     UInteger count() const;
23//! };
24//! }
25//! ```
26
27use std::ffi::c_void;
28use std::marker::PhantomData;
29use std::ptr::NonNull;
30
31use mtl_sys::{class, msg_send_0, msg_send_1, msg_send_2, msg_send_3, sel};
32
33use crate::enumerator::Enumerator;
34use crate::object::{Copying, Object, Referencing};
35use crate::types::UInteger;
36
37/// An Objective-C dictionary object.
38///
39/// C++ equivalent: `NS::Dictionary`
40#[repr(transparent)]
41pub struct Dictionary<K = Object, V = Object> {
42    inner: NonNull<c_void>,
43    _key_marker: PhantomData<K>,
44    _value_marker: PhantomData<V>,
45}
46
47impl<K, V> Clone for Dictionary<K, V> {
48    fn clone(&self) -> Self {
49        Self {
50            inner: self.inner,
51            _key_marker: PhantomData,
52            _value_marker: PhantomData,
53        }
54    }
55}
56
57impl<K, V> Dictionary<K, V> {
58    /// Create an empty dictionary.
59    ///
60    /// C++ equivalent: `static Dictionary* dictionary()`
61    #[inline]
62    pub fn dictionary() -> Option<Self> {
63        unsafe {
64            let ptr: *mut c_void = msg_send_0(class!(NSDictionary).as_ptr(), sel!(dictionary));
65            Self::from_ptr(ptr)
66        }
67    }
68
69    /// Create a dictionary with a single object and key.
70    ///
71    /// C++ equivalent: `static Dictionary* dictionary(const Object* pObject, const Object* pKey)`
72    #[inline]
73    pub fn dictionary_with_object(object: *const V, key: *const K) -> Option<Self> {
74        unsafe {
75            let ptr: *mut c_void = msg_send_2(
76                class!(NSDictionary).as_ptr(),
77                sel!(dictionaryWithObject:forKey:),
78                object,
79                key,
80            );
81            Self::from_ptr(ptr)
82        }
83    }
84
85    /// Create a dictionary with multiple objects and keys.
86    ///
87    /// C++ equivalent: `static Dictionary* dictionary(const Object* const* pObjects, const Object* const* pKeys, UInteger count)`
88    #[inline]
89    pub fn dictionary_with_objects(
90        objects: *const *const V,
91        keys: *const *const K,
92        count: UInteger,
93    ) -> Option<Self> {
94        unsafe {
95            let ptr: *mut c_void = msg_send_3(
96                class!(NSDictionary).as_ptr(),
97                sel!(dictionaryWithObjects:forKeys:count:),
98                objects,
99                keys,
100                count,
101            );
102            Self::from_ptr(ptr)
103        }
104    }
105
106    /// Allocate a new dictionary.
107    ///
108    /// C++ equivalent: `static Dictionary* alloc()`
109    #[inline]
110    pub fn alloc() -> Option<Self> {
111        unsafe {
112            let ptr: *mut c_void = msg_send_0(class!(NSDictionary).as_ptr(), sel!(alloc));
113            Self::from_ptr(ptr)
114        }
115    }
116
117    /// Initialize an allocated dictionary.
118    ///
119    /// C++ equivalent: `Dictionary* init()`
120    #[inline]
121    pub fn init(&self) -> Option<Self> {
122        unsafe {
123            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(init));
124            Self::from_ptr(ptr)
125        }
126    }
127
128    /// Initialize with multiple objects and keys.
129    ///
130    /// C++ equivalent: `Dictionary* init(const Object* const* pObjects, const Object* const* pKeys, UInteger count)`
131    #[inline]
132    pub fn init_with_objects(
133        &self,
134        objects: *const *const V,
135        keys: *const *const K,
136        count: UInteger,
137    ) -> Option<Self> {
138        unsafe {
139            let ptr: *mut c_void = msg_send_3(
140                self.as_ptr(),
141                sel!(initWithObjects:forKeys:count:),
142                objects,
143                keys,
144                count,
145            );
146            Self::from_ptr(ptr)
147        }
148    }
149
150    /// Initialize with a coder.
151    ///
152    /// C++ equivalent: `Dictionary* init(const class Coder* pCoder)`
153    #[inline]
154    pub fn init_with_coder(&self, coder: *const c_void) -> Option<Self> {
155        unsafe {
156            let ptr: *mut c_void = msg_send_1(self.as_ptr(), sel!(initWithCoder:), coder);
157            Self::from_ptr(ptr)
158        }
159    }
160
161    /// Get a key enumerator for the dictionary.
162    ///
163    /// C++ equivalent: `template <class _KeyType = Object> Enumerator<_KeyType>* keyEnumerator() const`
164    #[inline]
165    pub fn key_enumerator(&self) -> Option<Enumerator<K>> {
166        unsafe {
167            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(keyEnumerator));
168            Enumerator::from_ptr(ptr)
169        }
170    }
171
172    /// Get the object for the specified key.
173    ///
174    /// C++ equivalent: `template <class _Object = Object> _Object* object(const Object* pKey) const`
175    #[inline]
176    pub fn object(&self, key: *const K) -> *mut V {
177        unsafe { msg_send_1(self.as_ptr(), sel!(objectForKey:), key) }
178    }
179
180    /// Get the count of key-value pairs in the dictionary.
181    ///
182    /// C++ equivalent: `UInteger count() const`
183    #[inline]
184    pub fn count(&self) -> UInteger {
185        unsafe { msg_send_0(self.as_ptr(), sel!(count)) }
186    }
187
188    /// Create a Dictionary from a raw pointer.
189    ///
190    /// # Safety
191    ///
192    /// The pointer must be a valid Objective-C NSDictionary object.
193    #[inline]
194    pub unsafe fn from_ptr(ptr: *mut c_void) -> Option<Self> {
195        NonNull::new(ptr).map(|inner| Self {
196            inner,
197            _key_marker: PhantomData,
198            _value_marker: PhantomData,
199        })
200    }
201}
202
203impl<K, V> Referencing for Dictionary<K, V> {
204    #[inline]
205    fn as_ptr(&self) -> *const c_void {
206        self.inner.as_ptr()
207    }
208}
209
210impl<K, V> Copying for Dictionary<K, V> {
211    #[inline]
212    fn copy(&self) -> Option<Self> {
213        unsafe {
214            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(copy));
215            Self::from_ptr(ptr)
216        }
217    }
218}
219
220// SAFETY: NSDictionary is thread-safe for reference counting
221unsafe impl<K: Send, V: Send> Send for Dictionary<K, V> {}
222unsafe impl<K: Sync, V: Sync> Sync for Dictionary<K, V> {}
223
224impl<K, V> std::fmt::Debug for Dictionary<K, V> {
225    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226        f.debug_struct("Dictionary")
227            .field("ptr", &self.inner)
228            .field("count", &self.count())
229            .finish()
230    }
231}
232
233#[cfg(test)]
234mod tests {
235    use super::*;
236
237    #[test]
238    fn test_dictionary_size() {
239        // Dictionary should be pointer-sized
240        assert_eq!(
241            std::mem::size_of::<Dictionary<Object, Object>>(),
242            std::mem::size_of::<*mut c_void>()
243        );
244    }
245}