//! Utilities for the `NSSet` and `NSMutableSet` classes. use alloc::vec::Vec; #[cfg(feature = "NSEnumerator")] use core::fmt; use objc2::rc::{Retained, RetainedFromIterator}; use objc2::{msg_send, AnyThread, Message}; #[cfg(feature = "NSEnumerator")] use crate::iter; use crate::{util, NSMutableSet, NSSet}; /// Convenience creation methods. impl NSSet { /// Creates an [`NSSet`] from a slice of `Retained`s. /// /// # Examples /// /// ``` /// use objc2_foundation::{NSSet, NSString}; /// /// let strs = ["one", "two", "three"].map(NSString::from_str); /// let set = NSSet::from_retained_slice(&strs); /// ``` pub fn from_retained_slice(slice: &[Retained]) -> Retained { let len = slice.len(); let ptr = util::retained_ptr_cast_const(slice.as_ptr()); // SAFETY: Same as `NSArray::from_retained_slice` unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } } pub fn from_slice(slice: &[&ObjectType]) -> Retained { let len = slice.len(); let ptr = util::ref_ptr_cast_const(slice.as_ptr()); // SAFETY: Same as `NSArray::from_slice`. unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } } } /// Convenience creation methods. impl NSMutableSet { /// Creates an [`NSMutableSet`] from a slice of `Retained`s. /// /// # Examples /// /// ``` /// use objc2_foundation::{NSMutableSet, NSString}; /// /// let strs = ["one", "two", "three"].map(NSString::from_str); /// let set = NSMutableSet::from_retained_slice(&strs); /// ``` pub fn from_retained_slice(slice: &[Retained]) -> Retained { let len = slice.len(); let ptr = util::retained_ptr_cast_const(slice.as_ptr()); // SAFETY: Same as `NSArray::from_retained_slice` unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } } pub fn from_slice(slice: &[&ObjectType]) -> Retained { let len = slice.len(); let ptr = util::ref_ptr_cast_const(slice.as_ptr()); // SAFETY: Same as `NSArray::from_slice`. unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } } } /// Direct, unsafe object accessors. /// /// Foundation's collection types store their items in such a way that they /// can give out references to their data without having to autorelease it /// first, see [the docs][collections-own]. /// /// This means that we can more efficiently access the set's objects, but /// _only_ if the set isn't mutated via e.g. `NSMutableSet` methods while /// doing so - otherwise, we might end up accessing a deallocated object. /// /// [collections-own]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW12 impl NSSet { /// A direct reference to an arbitrary object in the set. /// /// Consider using the [`anyObject`](Self::anyObject) method instead, /// unless you're seeing performance issues from the retaining. /// /// # Safety /// /// The set must not be mutated while the reference is live. #[doc(alias = "anyObject")] pub unsafe fn anyObject_unchecked(&self) -> Option<&ObjectType> { // SAFETY: Upheld by caller. unsafe { msg_send![self, anyObject] } } /// A direct reference the member that matches the given object. /// /// Consider using the [`member`](Self::member) method instead, /// unless you're seeing performance issues from the retaining. /// /// # Safety /// /// The set must not be mutated while the returned reference is live. #[doc(alias = "member:")] pub unsafe fn member_unchecked(&self, object: &ObjectType) -> Option<&ObjectType> { // SAFETY: Upheld by caller. unsafe { msg_send![self, member: object] } } /// Iterate over the set in an arbitrary order without retaining the /// elements. /// /// Consider using the [`iter`](Self::iter) method instead, unless you're /// seeing performance issues from the retaining. /// /// # Safety /// /// The set must not be mutated for the lifetime of the iterator, or the /// elements it returns. #[cfg(feature = "NSEnumerator")] #[doc(alias = "objectEnumerator")] #[inline] pub unsafe fn iter_unchecked(&self) -> IterUnchecked<'_, ObjectType> { IterUnchecked(super::iter::IterUnchecked::new(self)) } } /// Various accessor methods. impl NSSet { /// Returns the number of elements in the set. /// /// # Examples /// /// ``` /// use objc2_foundation::{ns_string, NSSet}; /// /// let strs = [ns_string!("one"), ns_string!("two"), ns_string!("three")]; /// let set = NSSet::from_slice(&strs); /// assert_eq!(set.len(), 3); /// ``` #[doc(alias = "count")] pub fn len(&self) -> usize { self.count() } /// Returns `true` if the set contains no elements. /// /// # Examples /// /// ``` /// use objc2_foundation::{NSSet, NSObject}; /// /// let set = NSSet::::new(); /// assert!(set.is_empty()); /// ``` pub fn is_empty(&self) -> bool { self.count() == 0 } /// An iterator visiting all elements in arbitrary order. /// /// # Examples /// /// ``` /// use objc2_foundation::{ns_string, NSSet}; /// /// let set = NSSet::from_slice(&[ns_string!("one"), ns_string!("two"), ns_string!("three")]); /// for s in set.iter() { /// println!("{s}"); /// } /// ``` #[cfg(feature = "NSEnumerator")] #[doc(alias = "objectEnumerator")] #[inline] pub fn iter(&self) -> Iter<'_, ObjectType> { Iter(super::iter::Iter::new(self)) } /// Returns a [`Vec`] containing the set's elements in arbitrary order. /// /// # Examples /// /// ``` /// use objc2_foundation::{NSSet, NSString}; /// /// let strs = [ /// NSString::from_str("one"), /// NSString::from_str("two"), /// NSString::from_str("three"), /// ]; /// let set = NSSet::from_retained_slice(&strs); /// let vec = set.to_vec(); /// assert_eq!(vec.len(), 3); /// ``` #[cfg(feature = "NSEnumerator")] pub fn to_vec(&self) -> Vec> { self.iter().collect() } } #[cfg(feature = "NSEnumerator")] unsafe impl iter::FastEnumerationHelper for NSSet { type Item = ObjectType; #[inline] fn maybe_len(&self) -> Option { Some(self.len()) } } #[cfg(feature = "NSEnumerator")] unsafe impl iter::FastEnumerationHelper for NSMutableSet { type Item = ObjectType; #[inline] fn maybe_len(&self) -> Option { Some(self.len()) } } /// An iterator over the items of a set. #[derive(Debug)] #[cfg(feature = "NSEnumerator")] pub struct Iter<'a, ObjectType: Message>(iter::Iter<'a, NSSet>); #[cfg(feature = "NSEnumerator")] __impl_iter! { impl<'a, ObjectType: Message> Iterator> for Iter<'a, ObjectType> { ... } } /// An unchecked iterator over the items of a set. #[derive(Debug)] #[cfg(feature = "NSEnumerator")] pub struct IterUnchecked<'a, ObjectType: Message>(iter::IterUnchecked<'a, NSSet>); #[cfg(feature = "NSEnumerator")] __impl_iter! { impl<'a, ObjectType: Message> Iterator for IterUnchecked<'a, ObjectType> { ... } } /// An iterator over unretained items of a set. /// /// # Safety /// /// The set must not be mutated while this is alive. #[derive(Debug)] #[cfg(feature = "NSEnumerator")] pub struct IntoIter(iter::IntoIter>); #[cfg(feature = "NSEnumerator")] __impl_iter! { impl<'a, ObjectType: Message> Iterator> for IntoIter { ... } } #[cfg(feature = "NSEnumerator")] __impl_into_iter! { impl IntoIterator for &NSSet { type IntoIter = Iter<'_, ObjectType>; } impl IntoIterator for &NSMutableSet { type IntoIter = Iter<'_, ObjectType>; } impl IntoIterator for Retained> { #[uses(new)] type IntoIter = IntoIter; } impl IntoIterator for Retained> { #[uses(new_mutable)] type IntoIter = IntoIter; } } #[cfg(feature = "NSEnumerator")] impl fmt::Debug for NSSet { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_set().entries(self).finish() } } #[cfg(feature = "NSEnumerator")] impl fmt::Debug for NSMutableSet { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } #[cfg(feature = "NSEnumerator")] impl fmt::Debug for crate::NSCountedSet { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } impl Extend> for &NSMutableSet { fn extend>>(&mut self, iter: I) { iter.into_iter().for_each(move |item| { self.addObject(&item); }); } } impl<'a, ObjectType: Message> Extend<&'a ObjectType> for &NSMutableSet { fn extend>(&mut self, iter: I) { iter.into_iter().for_each(move |item| { self.addObject(item); }); } } impl<'a, ObjectType: Message + 'a> RetainedFromIterator<&'a ObjectType> for NSSet { fn retained_from_iter>(iter: I) -> Retained { let vec = Vec::from_iter(iter); Self::from_slice(&vec) } } impl RetainedFromIterator> for NSSet { fn retained_from_iter>>(iter: I) -> Retained { let vec = Vec::from_iter(iter); Self::from_retained_slice(&vec) } } impl<'a, ObjectType: Message + 'a> RetainedFromIterator<&'a ObjectType> for NSMutableSet { fn retained_from_iter>(iter: I) -> Retained { // TODO: Is this, or is using `initWithCapacity` the most optimal? let vec = Vec::from_iter(iter); Self::from_slice(&vec) } } impl RetainedFromIterator> for NSMutableSet { fn retained_from_iter>>(iter: I) -> Retained { // TODO: Is this, or is using `initWithCapacity` the most optimal? let vec = Vec::from_iter(iter); Self::from_retained_slice(&vec) } }