icrate/additions/Foundation/
array.rs

1//! Utilities for the `NSArray` and `NSMutableArray` classes.
2#![cfg(feature = "Foundation_NSArray")]
3use alloc::vec::Vec;
4use core::fmt;
5use core::mem;
6use core::ops::{Index, IndexMut, Range};
7use core::panic::{RefUnwindSafe, UnwindSafe};
8
9use objc2::mutability::{IsMutable, IsRetainable};
10use objc2::rc::IdFromIterator;
11
12use super::iter;
13use super::util;
14use crate::common::*;
15#[cfg(feature = "Foundation_NSMutableArray")]
16use crate::Foundation::NSMutableArray;
17use crate::Foundation::{self, NSArray};
18
19impl<T: Message> NSArray<T> {
20    pub fn from_vec(mut vec: Vec<Id<T>>) -> Id<Self> {
21        // We intentionally extract the length before we access the
22        // pointer as mutable, to not invalidate that mutable pointer.
23        let len = vec.len();
24        let ptr = util::id_ptr_cast(vec.as_mut_ptr());
25        // SAFETY: We've consumed the `Id<T>`s, which means that we can
26        // now safely take ownership (even if `T` is mutable).
27        unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) }
28        // The drop of `Vec` here would invalidate our mutable pointer,
29        // except for the fact that we're using `UnsafeCell` in `AnyObject`.
30    }
31
32    pub fn from_id_slice(slice: &[Id<T>]) -> Id<Self>
33    where
34        T: IsIdCloneable,
35    {
36        let len = slice.len();
37        let ptr = util::id_ptr_cast_const(slice.as_ptr());
38        // SAFETY: Because of the `T: IsIdCloneable` bound, and since we
39        // take `&[Id<T>]` (effectively `&Id<T>`), we are allowed to give
40        // the slice to Objective-C, which will retain it internally.
41        //
42        // Faster version of:
43        //     Self::from_vec(slice.iter().map(|obj| obj.clone()).collect())
44        unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) }
45    }
46
47    pub fn from_slice(slice: &[&T]) -> Id<Self>
48    where
49        T: IsRetainable,
50    {
51        let len = slice.len();
52        let ptr = util::ref_ptr_cast_const(slice.as_ptr());
53        // SAFETY: Because of the `T: IsRetainable` bound, we are allowed
54        // to give the slice to Objective-C, which will retain it
55        // internally.
56        //
57        // Faster version of:
58        //     Self::from_vec(slice.iter().map(|obj| obj.retain()).collect())
59        unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) }
60    }
61
62    #[doc(alias = "getObjects:range:")]
63    pub fn to_vec(&self) -> Vec<&T> {
64        // SAFETY: The range is know to be in bounds
65        unsafe { self.objects_in_range_unchecked(0..self.len()) }
66    }
67
68    #[doc(alias = "getObjects:range:")]
69    pub fn to_vec_retained(&self) -> Vec<Id<T>>
70    where
71        T: IsIdCloneable,
72    {
73        // SAFETY: The objects are stored in the array
74        self.to_vec()
75            .into_iter()
76            .map(|obj| unsafe { util::collection_retain_id(obj) })
77            .collect()
78    }
79
80    // `fn into_vec(Id<NSArray>) -> Vec<Id<T>>` would not be safe, since
81    // the array itself is unconditionally `IsIdCloneable`, even when
82    // containing mutable elements, and hence we would be able to
83    // duplicate those.
84}
85
86#[cfg(feature = "Foundation_NSMutableArray")]
87impl<T: Message> NSMutableArray<T> {
88    pub fn from_vec(mut vec: Vec<Id<T>>) -> Id<Self> {
89        let len = vec.len();
90        let ptr = util::id_ptr_cast(vec.as_mut_ptr());
91        // SAFETY: Same as `NSArray::from_vec`.
92        unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) }
93    }
94
95    pub fn from_id_slice(slice: &[Id<T>]) -> Id<Self>
96    where
97        T: IsIdCloneable,
98    {
99        let len = slice.len();
100        let ptr = util::id_ptr_cast_const(slice.as_ptr());
101        // SAFETY: Same as `NSArray::from_id_slice`
102        unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) }
103    }
104
105    pub fn from_slice(slice: &[&T]) -> Id<Self>
106    where
107        T: IsRetainable,
108    {
109        let len = slice.len();
110        let ptr = util::ref_ptr_cast_const(slice.as_ptr());
111        // SAFETY: Same as `NSArray::from_slice`.
112        unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) }
113    }
114
115    pub fn into_vec(array: Id<Self>) -> Vec<Id<T>> {
116        // SAFETY: We've consumed the array, so taking ownership of the
117        // returned values is safe.
118        array
119            .to_vec()
120            .into_iter()
121            .map(|obj| unsafe { util::mutable_collection_retain_removed_id(obj) })
122            .collect()
123    }
124}
125
126impl<T: Message> NSArray<T> {
127    #[doc(alias = "count")]
128    pub fn len(&self) -> usize {
129        self.count()
130    }
131
132    pub fn is_empty(&self) -> bool {
133        self.len() == 0
134    }
135}
136
137extern_methods!(
138    unsafe impl<T: Message> NSArray<T> {
139        #[method(objectAtIndex:)]
140        unsafe fn get_unchecked(&self, index: usize) -> &T;
141
142        #[doc(alias = "objectAtIndex:")]
143        pub fn get(&self, index: usize) -> Option<&T> {
144            // TODO: Replace this check with catching the thrown NSRangeException
145            if index < self.len() {
146                // SAFETY: The index is checked to be in bounds.
147                Some(unsafe { self.get_unchecked(index) })
148            } else {
149                None
150            }
151        }
152
153        #[doc(alias = "objectAtIndex:")]
154        pub fn get_retained(&self, index: usize) -> Option<Id<T>>
155        where
156            T: IsIdCloneable,
157        {
158            // SAFETY: The object is stored in the array
159            self.get(index)
160                .map(|obj| unsafe { util::collection_retain_id(obj) })
161        }
162
163        #[method(objectAtIndex:)]
164        unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut T;
165
166        #[doc(alias = "objectAtIndex:")]
167        pub fn get_mut(&mut self, index: usize) -> Option<&mut T>
168        where
169            T: IsMutable,
170        {
171            // TODO: Replace this check with catching the thrown NSRangeException
172            if index < self.len() {
173                // SAFETY: The index is checked to be in bounds, and the
174                // reference is safe as mutable because of the `T: IsMutable`
175                // bound.
176                Some(unsafe { self.get_unchecked_mut(index) })
177            } else {
178                None
179            }
180        }
181
182        #[doc(alias = "firstObject")]
183        #[method(firstObject)]
184        pub fn first(&self) -> Option<&T>;
185
186        #[doc(alias = "firstObject")]
187        pub fn first_retained(&self) -> Option<Id<T>>
188        where
189            T: IsIdCloneable,
190        {
191            // SAFETY: The object is stored in the array
192            self.first()
193                .map(|obj| unsafe { util::collection_retain_id(obj) })
194        }
195
196        #[doc(alias = "firstObject")]
197        #[method(firstObject)]
198        pub fn first_mut(&mut self) -> Option<&mut T>
199        where
200            T: IsMutable;
201
202        #[doc(alias = "lastObject")]
203        #[method(lastObject)]
204        pub fn last(&self) -> Option<&T>;
205
206        #[doc(alias = "lastObject")]
207        pub fn last_retained(&self) -> Option<Id<T>>
208        where
209            T: IsIdCloneable,
210        {
211            // SAFETY: The object is stored in the array
212            self.last()
213                .map(|obj| unsafe { util::collection_retain_id(obj) })
214        }
215
216        #[doc(alias = "lastObject")]
217        #[method(lastObject)]
218        pub fn last_mut(&mut self) -> Option<&mut T>
219        where
220            T: IsMutable;
221    }
222);
223
224impl<T: Message> NSArray<T> {
225    unsafe fn objects_in_range_unchecked(&self, range: Range<usize>) -> Vec<&T> {
226        let range = Foundation::NSRange::from(range);
227        let mut vec: Vec<NonNull<T>> = Vec::with_capacity(range.length);
228        unsafe {
229            self.getObjects_range(NonNull::new(vec.as_mut_ptr()).unwrap(), range);
230            vec.set_len(range.length);
231            mem::transmute(vec)
232        }
233    }
234
235    #[doc(alias = "getObjects:range:")]
236    pub fn objects_in_range(&self, range: Range<usize>) -> Option<Vec<&T>> {
237        if range.end > self.len() {
238            return None;
239        }
240        // SAFETY: Just checked that the range is in bounds
241        Some(unsafe { self.objects_in_range_unchecked(range) })
242    }
243}
244
245#[cfg(feature = "Foundation_NSMutableArray")]
246impl<T: Message> NSMutableArray<T> {
247    #[doc(alias = "addObject:")]
248    pub fn push(&mut self, obj: Id<T>) {
249        // SAFETY: We've consumed ownership of the object.
250        unsafe { self.addObject(&obj) }
251    }
252
253    #[doc(alias = "insertObject:atIndex:")]
254    pub fn insert(&mut self, index: usize, obj: Id<T>) {
255        // TODO: Replace this check with catching the thrown NSRangeException
256        let len = self.len();
257        if index < len {
258            // SAFETY: We've consumed ownership of the object, and the
259            // index is checked to be in bounds.
260            unsafe { self.insertObject_atIndex(&obj, index) }
261        } else {
262            panic!(
263                "insertion index (is {}) should be <= len (is {})",
264                index, len
265            );
266        }
267    }
268
269    #[doc(alias = "replaceObjectAtIndex:withObject:")]
270    pub fn replace(&mut self, index: usize, obj: Id<T>) -> Result<Id<T>, Id<T>> {
271        if let Some(old_obj) = self.get(index) {
272            // SAFETY: We remove the object from the array below.
273            let old_obj = unsafe { util::mutable_collection_retain_removed_id(old_obj) };
274            // SAFETY: The index is checked to be in bounds, and we've
275            // consumed ownership of the new object.
276            unsafe { self.replaceObjectAtIndex_withObject(index, &obj) };
277            Ok(old_obj)
278        } else {
279            Err(obj)
280        }
281    }
282
283    #[doc(alias = "removeObjectAtIndex:")]
284    pub fn remove(&mut self, index: usize) -> Option<Id<T>> {
285        let obj = self.get(index)?;
286        // SAFETY: We remove the object from the array below.
287        let obj = unsafe { util::mutable_collection_retain_removed_id(obj) };
288        // SAFETY: The index is checked to be in bounds.
289        unsafe { self.removeObjectAtIndex(index) };
290        Some(obj)
291    }
292
293    #[doc(alias = "removeLastObject")]
294    pub fn pop(&mut self) -> Option<Id<T>> {
295        let obj = self.last()?;
296        // SAFETY: We remove the object from the array below.
297        let obj = unsafe { util::mutable_collection_retain_removed_id(obj) };
298        // SAFETY: Just checked that there is an object.
299        unsafe { self.removeLastObject() };
300        Some(obj)
301    }
302
303    #[doc(alias = "sortUsingFunction:context:")]
304    pub fn sort_by<F: FnMut(&T, &T) -> core::cmp::Ordering>(&mut self, compare: F) {
305        // TODO: "C-unwind"
306        unsafe extern "C" fn compare_with_closure<T, F: FnMut(&T, &T) -> core::cmp::Ordering>(
307            obj1: NonNull<T>,
308            obj2: NonNull<T>,
309            context: *mut c_void,
310        ) -> isize {
311            let context: *mut F = context.cast();
312            // Bring back a reference to the closure.
313            // Guaranteed to be unique, we gave `sortUsingFunction` unique is
314            // ownership, and that method only runs one function at a time.
315            let closure: &mut F = unsafe { context.as_mut().unwrap_unchecked() };
316
317            // SAFETY: The objects are guaranteed to be valid
318            let (obj1, obj2) = unsafe { (obj1.as_ref(), obj2.as_ref()) };
319
320            Foundation::NSComparisonResult::from((*closure)(obj1, obj2)) as _
321        }
322
323        // Create function pointer
324        let f: unsafe extern "C" fn(_, _, _) -> _ = compare_with_closure::<T, F>;
325
326        // Grab a type-erased pointer to the closure (a pointer to stack).
327        let mut closure = compare;
328        let context: *mut F = &mut closure;
329
330        unsafe { self.sortUsingFunction_context(f, context.cast()) };
331        // Keep the closure alive until the function has run.
332        drop(closure);
333    }
334}
335
336impl<T: Message> NSArray<T> {
337    #[doc(alias = "objectEnumerator")]
338    #[inline]
339    pub fn iter(&self) -> Iter<'_, T> {
340        Iter(super::iter::Iter::new(self))
341    }
342
343    #[doc(alias = "objectEnumerator")]
344    #[inline]
345    pub fn iter_mut(&mut self) -> IterMut<'_, T>
346    where
347        T: IsMutable,
348    {
349        IterMut(super::iter::IterMut::new(self))
350    }
351
352    #[doc(alias = "objectEnumerator")]
353    #[inline]
354    pub fn iter_retained(&self) -> IterRetained<'_, T>
355    where
356        T: IsIdCloneable,
357    {
358        IterRetained(super::iter::IterRetained::new(self))
359    }
360}
361
362unsafe impl<T: Message> iter::FastEnumerationHelper for NSArray<T> {
363    type Item = T;
364
365    #[inline]
366    fn maybe_len(&self) -> Option<usize> {
367        Some(self.len())
368    }
369}
370
371#[cfg(feature = "Foundation_NSMutableArray")]
372unsafe impl<T: Message> iter::FastEnumerationHelper for NSMutableArray<T> {
373    type Item = T;
374
375    #[inline]
376    fn maybe_len(&self) -> Option<usize> {
377        Some(self.len())
378    }
379}
380
381/// An iterator over the items of a `NSArray`.
382#[derive(Debug)]
383pub struct Iter<'a, T: Message>(iter::Iter<'a, NSArray<T>>);
384
385__impl_iter! {
386    impl<'a, T: Message> Iterator<Item = &'a T> for Iter<'a, T> { ... }
387}
388
389/// A mutable iterator over the items of a `NSArray`.
390#[derive(Debug)]
391pub struct IterMut<'a, T: Message>(iter::IterMut<'a, NSArray<T>>);
392
393__impl_iter! {
394    impl<'a, T: Message + IsMutable> Iterator<Item = &'a mut T> for IterMut<'a, T> { ... }
395}
396
397/// An iterator that retains the items of a `NSArray`.
398#[derive(Debug)]
399pub struct IterRetained<'a, T: Message>(iter::IterRetained<'a, NSArray<T>>);
400
401__impl_iter! {
402    impl<'a, T: Message + IsIdCloneable> Iterator<Item = Id<T>> for IterRetained<'a, T> { ... }
403}
404
405/// A consuming iterator over the items of a `NSArray`.
406#[derive(Debug)]
407pub struct IntoIter<T: Message>(iter::IntoIter<NSArray<T>>);
408
409__impl_iter! {
410    impl<'a, T: Message> Iterator<Item = Id<T>> for IntoIter<T> { ... }
411}
412
413__impl_into_iter! {
414    impl<T: Message> IntoIterator for &NSArray<T> {
415        type IntoIter = Iter<'_, T>;
416    }
417
418    #[cfg(feature = "Foundation_NSMutableArray")]
419    impl<T: Message> IntoIterator for &NSMutableArray<T> {
420        type IntoIter = Iter<'_, T>;
421    }
422
423    impl<T: Message + IsMutable> IntoIterator for &mut NSArray<T> {
424        type IntoIter = IterMut<'_, T>;
425    }
426
427    #[cfg(feature = "Foundation_NSMutableArray")]
428    impl<T: Message + IsMutable> IntoIterator for &mut NSMutableArray<T> {
429        type IntoIter = IterMut<'_, T>;
430    }
431
432    impl<T: Message + IsIdCloneable> IntoIterator for Id<NSArray<T>> {
433        type IntoIter = IntoIter<T>;
434    }
435
436    #[cfg(feature = "Foundation_NSMutableArray")]
437    impl<T: Message> IntoIterator for Id<NSMutableArray<T>> {
438        type IntoIter = IntoIter<T>;
439    }
440}
441
442impl<T: Message> Index<usize> for NSArray<T> {
443    type Output = T;
444
445    fn index(&self, index: usize) -> &T {
446        self.get(index).unwrap()
447    }
448}
449
450#[cfg(feature = "Foundation_NSMutableArray")]
451impl<T: Message> Index<usize> for NSMutableArray<T> {
452    type Output = T;
453
454    fn index(&self, index: usize) -> &T {
455        self.get(index).unwrap()
456    }
457}
458
459impl<T: Message + IsMutable> IndexMut<usize> for NSArray<T> {
460    fn index_mut(&mut self, index: usize) -> &mut T {
461        self.get_mut(index).unwrap()
462    }
463}
464
465#[cfg(feature = "Foundation_NSMutableArray")]
466impl<T: Message + IsMutable> IndexMut<usize> for NSMutableArray<T> {
467    fn index_mut(&mut self, index: usize) -> &mut T {
468        self.get_mut(index).unwrap()
469    }
470}
471
472impl<T: fmt::Debug + Message> fmt::Debug for NSArray<T> {
473    #[inline]
474    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
475        f.debug_list().entries(self).finish()
476    }
477}
478
479#[cfg(feature = "Foundation_NSMutableArray")]
480impl<T: Message> Extend<Id<T>> for NSMutableArray<T> {
481    fn extend<I: IntoIterator<Item = Id<T>>>(&mut self, iter: I) {
482        iter.into_iter().for_each(move |item| self.push(item))
483    }
484}
485
486#[cfg(feature = "Foundation_NSMutableArray")]
487impl<'a, T: Message + IsRetainable> Extend<&'a T> for NSMutableArray<T> {
488    fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) {
489        // SAFETY: Because of the `T: IsRetainable` bound, it is safe for the
490        // array to retain the object here.
491        iter.into_iter()
492            .for_each(move |item| unsafe { self.addObject(item) })
493    }
494}
495
496impl<'a, T: Message + IsRetainable + 'a> IdFromIterator<&'a T> for NSArray<T> {
497    fn id_from_iter<I: IntoIterator<Item = &'a T>>(iter: I) -> Id<Self> {
498        let vec = Vec::from_iter(iter);
499        Self::from_slice(&vec)
500    }
501}
502
503impl<T: Message> IdFromIterator<Id<T>> for NSArray<T> {
504    fn id_from_iter<I: IntoIterator<Item = Id<T>>>(iter: I) -> Id<Self> {
505        let vec = Vec::from_iter(iter);
506        Self::from_vec(vec)
507    }
508}
509
510#[cfg(feature = "Foundation_NSMutableArray")]
511impl<'a, T: Message + IsRetainable + 'a> IdFromIterator<&'a T> for NSMutableArray<T> {
512    fn id_from_iter<I: IntoIterator<Item = &'a T>>(iter: I) -> Id<Self> {
513        let vec = Vec::from_iter(iter);
514        Self::from_slice(&vec)
515    }
516}
517
518#[cfg(feature = "Foundation_NSMutableArray")]
519impl<T: Message> IdFromIterator<Id<T>> for NSMutableArray<T> {
520    fn id_from_iter<I: IntoIterator<Item = Id<T>>>(iter: I) -> Id<Self> {
521        let vec = Vec::from_iter(iter);
522        Self::from_vec(vec)
523    }
524}