Files
another-boids-in-rust/vendor/objc2-foundation/src/set.rs

613 lines
18 KiB
Rust

//! Utilities for the `NSSet` and `NSMutableSet` classes.
use alloc::vec::Vec;
#[cfg(feature = "NSEnumerator")]
use core::fmt;
use core::hash::Hash;
use objc2::mutability::{HasStableHash, IsIdCloneable, IsRetainable};
use objc2::rc::{Retained, RetainedFromIterator};
use objc2::{extern_methods, ClassType, Message};
#[cfg(feature = "NSEnumerator")]
use super::iter;
use super::util;
use crate::Foundation::{NSMutableSet, NSSet};
impl<T: Message> NSSet<T> {
/// Returns the number of elements in the set.
///
/// # Examples
///
/// ```
/// use objc2_foundation::{NSSet, NSString};
///
/// let strs = ["one", "two", "three"].map(NSString::from_str);
/// let set = NSSet::from_id_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, NSString};
///
/// let set = NSSet::<NSString>::new();
/// assert!(set.is_empty());
/// ```
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl<T: Message + Eq + Hash> NSSet<T> {
/// Creates an [`NSSet`] from a vector.
///
/// # Examples
///
/// ```
/// use objc2_foundation::{NSSet, NSString};
///
/// let strs = ["one", "two", "three"].map(NSString::from_str).to_vec();
/// let set = NSSet::from_vec(strs);
/// ```
pub fn from_vec(mut vec: Vec<Retained<T>>) -> Retained<Self>
where
T: HasStableHash,
{
let len = vec.len();
let ptr = util::retained_ptr_cast(vec.as_mut_ptr());
// SAFETY: Same as `NSArray::from_vec`.
unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) }
}
/// 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_id_slice(&strs);
/// ```
pub fn from_id_slice(slice: &[Retained<T>]) -> Retained<Self>
where
T: HasStableHash + IsIdCloneable,
{
let len = slice.len();
let ptr = util::retained_ptr_cast_const(slice.as_ptr());
// SAFETY: Same as `NSArray::from_id_slice`
unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) }
}
pub fn from_slice(slice: &[&T]) -> Retained<Self>
where
T: HasStableHash + IsRetainable,
{
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) }
}
/// Returns a [`Vec`] containing the set's elements.
///
/// # Examples
///
/// ```
/// use objc2_foundation::{NSMutableString, NSSet};
///
/// let strs = vec![
/// NSMutableString::from_str("one"),
/// NSMutableString::from_str("two"),
/// NSMutableString::from_str("three"),
/// ];
/// let set = NSSet::from_vec(strs);
/// let vec = set.to_vec();
/// assert_eq!(vec.len(), 3);
/// ```
#[cfg(feature = "NSEnumerator")]
pub fn to_vec(&self) -> Vec<&T> {
self.into_iter().collect()
}
#[cfg(feature = "NSEnumerator")]
pub fn to_vec_retained(&self) -> Vec<Retained<T>>
where
T: IsIdCloneable,
{
// SAFETY: The objects are stored in the set
self.into_iter()
.map(|obj| unsafe { util::collection_retain(obj) })
.collect()
}
/// Returns an [`NSArray`] containing the set's elements, or an empty
/// array if the set is empty.
///
/// [`NSArray`]: crate::Foundation::NSArray
///
/// # Examples
///
/// ```
/// use objc2_foundation::{NSNumber, NSSet, NSString};
///
/// let nums = [1, 2, 3];
/// let set = NSSet::from_id_slice(&nums.map(NSNumber::new_i32));
///
/// assert_eq!(set.to_array().len(), 3);
/// assert!(set.to_array().iter().all(|i| nums.contains(&i.as_i32())));
/// ```
#[doc(alias = "allObjects")]
#[cfg(feature = "NSArray")]
pub fn to_array(&self) -> Retained<crate::Foundation::NSArray<T>>
where
T: IsIdCloneable,
{
// SAFETY: The `T: IsIdCloneable` bound ensures that it is safe to
// create what is effectively a copy of the collection from a `&self`
// reference.
//
// Could be implemented as:
// NSArray::from_vec(self.to_vec_retained())
unsafe { self.allObjects() }
}
}
impl<T: Message + Eq + Hash> NSMutableSet<T> {
/// Creates an [`NSMutableSet`] from a vector.
///
/// # Examples
///
/// ```
/// use objc2_foundation::{NSMutableSet, NSString};
///
/// let strs = ["one", "two", "three"].map(NSString::from_str).to_vec();
/// let set = NSMutableSet::from_vec(strs);
/// ```
pub fn from_vec(mut vec: Vec<Retained<T>>) -> Retained<Self>
where
T: HasStableHash,
{
let len = vec.len();
let ptr = util::retained_ptr_cast(vec.as_mut_ptr());
// SAFETY: Same as `NSArray::from_vec`.
unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) }
}
/// 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_id_slice(&strs);
/// ```
pub fn from_id_slice(slice: &[Retained<T>]) -> Retained<Self>
where
T: HasStableHash + IsIdCloneable,
{
let len = slice.len();
let ptr = util::retained_ptr_cast_const(slice.as_ptr());
// SAFETY: Same as `NSArray::from_id_slice`
unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) }
}
pub fn from_slice(slice: &[&T]) -> Retained<Self>
where
T: HasStableHash + IsRetainable,
{
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) }
}
/// Returns a [`Vec`] containing the set's elements, consuming the set.
///
/// # Examples
///
/// ```
/// use objc2_foundation::{NSMutableSet, NSMutableString};
///
/// let strs = vec![
/// NSMutableString::from_str("one"),
/// NSMutableString::from_str("two"),
/// NSMutableString::from_str("three"),
/// ];
/// let set = NSMutableSet::from_vec(strs);
/// let vec = NSMutableSet::into_vec(set);
/// assert_eq!(vec.len(), 3);
/// ```
#[cfg(feature = "NSEnumerator")]
pub fn into_vec(set: Retained<Self>) -> Vec<Retained<T>> {
set.into_iter().collect()
}
}
extern_methods!(
unsafe impl<T: Message> NSSet<T> {
/// Returns a reference to one of the objects in the set, or `None` if
/// the set is empty.
///
/// # Examples
///
/// ```
/// use objc2_foundation::{NSSet, NSString};
///
/// let strs = ["one", "two", "three"].map(NSString::from_str);
/// let set = NSSet::from_id_slice(&strs);
/// let any = set.get_any().unwrap();
/// assert!(any == &*strs[0] || any == &*strs[1] || any == &*strs[2]);
/// ```
#[doc(alias = "anyObject")]
#[method(anyObject)]
pub fn get_any(&self) -> Option<&T>;
}
unsafe impl<T: Message + Eq + Hash> NSSet<T> {
/// Returns `true` if the set contains a value.
///
/// # Examples
///
/// ```
/// use objc2_foundation::{ns_string, NSSet, NSString};
///
/// let strs = ["one", "two", "three"].map(NSString::from_str);
/// let set = NSSet::from_id_slice(&strs);
/// assert!(set.contains(ns_string!("one")));
/// ```
#[doc(alias = "containsObject:")]
pub fn contains(&self, value: &T) -> bool {
unsafe { self.containsObject(value) }
}
/// Returns a reference to the value in the set, if any, that is equal
/// to the given value.
///
/// # Examples
///
/// ```
/// use objc2_foundation::{ns_string, NSSet, NSString};
///
/// let strs = ["one", "two", "three"].map(NSString::from_str);
/// let set = NSSet::from_id_slice(&strs);
/// assert_eq!(set.get(ns_string!("one")), Some(&*strs[0]));
/// assert_eq!(set.get(ns_string!("four")), None);
/// ```
#[doc(alias = "member:")]
#[method(member:)]
pub fn get(&self, value: &T) -> Option<&T>;
#[doc(alias = "member:")]
pub fn get_retained(&self, value: &T) -> Option<Retained<T>>
where
T: IsIdCloneable,
{
self.get(value)
.map(|obj| unsafe { util::collection_retain(obj) })
}
// Note: No `get_mut` method exposed on sets, since their objects'
// hashes are immutable.
/// Returns `true` if the set is a subset of another, i.e., `other`
/// contains at least all the values in `self`.
///
/// # Examples
///
/// ```
/// use objc2_foundation::{NSSet, NSString};
///
/// let set1 = NSSet::from_id_slice(&["one", "two"].map(NSString::from_str));
/// let set2 = NSSet::from_id_slice(&["one", "two", "three"].map(NSString::from_str));
///
/// assert!(set1.is_subset(&set2));
/// assert!(!set2.is_subset(&set1));
/// ```
#[doc(alias = "isSubsetOfSet:")]
pub fn is_subset(&self, other: &NSSet<T>) -> bool {
unsafe { self.isSubsetOfSet(other) }
}
/// Returns `true` if the set is a superset of another, i.e., `self`
/// contains at least all the values in `other`.
///
/// # Examples
///
/// ```
/// use objc2_foundation::{NSSet, NSString};
///
/// let set1 = NSSet::from_id_slice(&["one", "two"].map(NSString::from_str));
/// let set2 = NSSet::from_id_slice(&["one", "two", "three"].map(NSString::from_str));
///
/// assert!(!set1.is_superset(&set2));
/// assert!(set2.is_superset(&set1));
/// ```
pub fn is_superset(&self, other: &NSSet<T>) -> bool {
other.is_subset(self)
}
/// Returns `true` if `self` has no elements in common with `other`.
///
/// # Examples
///
/// ```
/// use objc2_foundation::{NSSet, NSString};
///
/// let set1 = NSSet::from_id_slice(&["one", "two"].map(NSString::from_str));
/// let set2 = NSSet::from_id_slice(&["one", "two", "three"].map(NSString::from_str));
/// let set3 = NSSet::from_id_slice(&["four", "five", "six"].map(NSString::from_str));
///
/// assert!(!set1.is_disjoint(&set2));
/// assert!(set1.is_disjoint(&set3));
/// assert!(set2.is_disjoint(&set3));
/// ```
pub fn is_disjoint(&self, other: &NSSet<T>) -> bool {
!unsafe { self.intersectsSet(other) }
}
}
);
impl<T: Message + Eq + Hash> NSMutableSet<T> {
/// Add a value to the set. Returns whether the value was
/// newly inserted.
///
/// # Examples
///
#[cfg_attr(feature = "NSValue", doc = "```")]
#[cfg_attr(not(feature = "NSValue"), doc = "```ignore")]
/// use objc2_foundation::{NSNumber, NSMutableSet};
///
/// let mut set = NSMutableSet::new();
///
/// assert_eq!(set.insert(&*NSNumber::new_u32(42)), true);
/// assert_eq!(set.insert(&*NSNumber::new_u32(42)), false);
/// assert_eq!(set.len(), 1);
/// ```
#[doc(alias = "addObject:")]
pub fn insert(&mut self, value: &T) -> bool
where
T: HasStableHash + IsRetainable,
{
let contains_value = self.contains(value);
// SAFETY: Because of the `T: IsRetainable` bound, it is safe for the
// set to retain the object here.
unsafe { self.addObject(value) };
!contains_value
}
/// Add an `Retained` to the set. Returns whether the value was
/// newly inserted.
///
/// # Examples
///
/// ```
/// use objc2_foundation::{NSMutableSet, NSString};
///
/// let mut set = NSMutableSet::new();
///
/// assert_eq!(set.insert_id(NSString::from_str("one")), true);
/// assert_eq!(set.insert_id(NSString::from_str("one")), false);
/// assert_eq!(set.len(), 1);
/// ```
#[doc(alias = "addObject:")]
pub fn insert_id(&mut self, value: Retained<T>) -> bool
where
T: HasStableHash,
{
let contains_value = self.contains(&value);
// SAFETY: We've consumed ownership of the object.
unsafe { self.addObject(&value) };
!contains_value
}
/// Removes a value from the set. Returns whether the value was present
/// in the set.
///
/// # Examples
///
/// ```
/// use objc2_foundation::{ns_string, NSMutableSet, NSString};
///
/// let mut set = NSMutableSet::new();
///
/// set.insert_id(NSString::from_str("one"));
/// assert_eq!(set.remove(ns_string!("one")), true);
/// assert_eq!(set.remove(ns_string!("one")), false);
/// ```
#[doc(alias = "removeObject:")]
pub fn remove(&mut self, value: &T) -> bool
where
T: HasStableHash,
{
let contains_value = self.contains(value);
unsafe { self.removeObject(value) };
contains_value
}
}
// Iteration is not supposed to touch the elements, not even do comparisons.
//
// TODO: Verify that this is actually the case.
impl<T: Message> NSSet<T> {
/// An iterator visiting all elements in arbitrary order.
///
/// # Examples
///
/// ```
/// use objc2_foundation::{NSSet, NSString};
///
/// let strs = ["one", "two", "three"].map(NSString::from_str);
/// let set = NSSet::from_id_slice(&strs);
/// for s in &set {
/// println!("{s}");
/// }
/// ```
#[doc(alias = "objectEnumerator")]
#[cfg(feature = "NSEnumerator")]
#[inline]
pub fn iter(&self) -> Iter<'_, T> {
Iter(super::iter::Iter::new(self))
}
#[doc(alias = "objectEnumerator")]
#[cfg(feature = "NSEnumerator")]
#[inline]
pub fn iter_retained(&self) -> IterRetained<'_, T>
where
T: IsIdCloneable,
{
IterRetained(super::iter::IterRetained::new(self))
}
}
#[cfg(feature = "NSEnumerator")]
unsafe impl<T: Message> iter::FastEnumerationHelper for NSSet<T> {
type Item = T;
#[inline]
fn maybe_len(&self) -> Option<usize> {
Some(self.len())
}
}
#[cfg(feature = "NSEnumerator")]
unsafe impl<T: Message> iter::FastEnumerationHelper for NSMutableSet<T> {
type Item = T;
#[inline]
fn maybe_len(&self) -> Option<usize> {
Some(self.len())
}
}
/// An iterator over the items of a `NSSet`.
#[derive(Debug)]
#[cfg(feature = "NSEnumerator")]
pub struct Iter<'a, T: Message>(iter::Iter<'a, NSSet<T>>);
#[cfg(feature = "NSEnumerator")]
__impl_iter! {
impl<'a, T: Message> Iterator<Item = &'a T> for Iter<'a, T> { ... }
}
/// An iterator that retains the items of a `NSSet`.
#[derive(Debug)]
#[cfg(feature = "NSEnumerator")]
pub struct IterRetained<'a, T: Message>(iter::IterRetained<'a, NSSet<T>>);
#[cfg(feature = "NSEnumerator")]
__impl_iter! {
impl<'a, T: Message + IsIdCloneable> Iterator<Item = Retained<T>> for IterRetained<'a, T> { ... }
}
/// A consuming iterator over the items of a `NSSet`.
#[derive(Debug)]
#[cfg(feature = "NSEnumerator")]
pub struct IntoIter<T: Message>(iter::IntoIter<NSSet<T>>);
#[cfg(feature = "NSEnumerator")]
__impl_iter! {
impl<'a, T: Message> Iterator<Item = Retained<T>> for IntoIter<T> { ... }
}
#[cfg(feature = "NSEnumerator")]
__impl_into_iter! {
impl<T: Message> IntoIterator for &NSSet<T> {
type IntoIter = Iter<'_, T>;
}
impl<T: Message> IntoIterator for &NSMutableSet<T> {
type IntoIter = Iter<'_, T>;
}
impl<T: Message + IsIdCloneable> IntoIterator for Retained<NSSet<T>> {
type IntoIter = IntoIter<T>;
}
impl<T: Message> IntoIterator for Retained<NSMutableSet<T>> {
type IntoIter = IntoIter<T>;
}
}
#[cfg(feature = "NSEnumerator")]
impl<T: fmt::Debug + Message> fmt::Debug for NSSet<T> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_set().entries(self).finish()
}
}
#[cfg(feature = "NSEnumerator")]
impl<T: fmt::Debug + Message> fmt::Debug for NSMutableSet<T> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
#[cfg(feature = "NSEnumerator")]
impl<T: fmt::Debug + Message> fmt::Debug for crate::Foundation::NSCountedSet<T> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<T: Message + Eq + Hash + HasStableHash> Extend<Retained<T>> for NSMutableSet<T> {
fn extend<I: IntoIterator<Item = Retained<T>>>(&mut self, iter: I) {
iter.into_iter().for_each(move |item| {
self.insert_id(item);
});
}
}
impl<'a, T: Message + Eq + Hash + HasStableHash + IsRetainable> Extend<&'a T> for NSMutableSet<T> {
fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) {
iter.into_iter().for_each(move |item| {
self.insert(item);
});
}
}
impl<'a, T: Message + Eq + Hash + HasStableHash + IsRetainable + 'a> RetainedFromIterator<&'a T>
for NSSet<T>
{
fn id_from_iter<I: IntoIterator<Item = &'a T>>(iter: I) -> Retained<Self> {
let vec = Vec::from_iter(iter);
Self::from_slice(&vec)
}
}
impl<T: Message + Eq + Hash + HasStableHash> RetainedFromIterator<Retained<T>> for NSSet<T> {
fn id_from_iter<I: IntoIterator<Item = Retained<T>>>(iter: I) -> Retained<Self> {
let vec = Vec::from_iter(iter);
Self::from_vec(vec)
}
}
impl<'a, T: Message + Eq + Hash + HasStableHash + IsRetainable + 'a> RetainedFromIterator<&'a T>
for NSMutableSet<T>
{
fn id_from_iter<I: IntoIterator<Item = &'a T>>(iter: I) -> Retained<Self> {
let vec = Vec::from_iter(iter);
Self::from_slice(&vec)
}
}
impl<T: Message + Eq + Hash + HasStableHash> RetainedFromIterator<Retained<T>> for NSMutableSet<T> {
fn id_from_iter<I: IntoIterator<Item = Retained<T>>>(iter: I) -> Retained<Self> {
let vec = Vec::from_iter(iter);
Self::from_vec(vec)
}
}