Skip to content

Commit

Permalink
Add NSSet and NSMutableSet
Browse files Browse the repository at this point in the history
  • Loading branch information
zleytus committed Aug 17, 2022
1 parent 096ba02 commit bfa9d16
Show file tree
Hide file tree
Showing 3 changed files with 872 additions and 0 deletions.
6 changes: 6 additions & 0 deletions objc2/src/foundation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,13 @@ pub use self::geometry::{CGFloat, NSPoint, NSRect, NSSize};
pub use self::mutable_array::NSMutableArray;
pub use self::mutable_attributed_string::NSMutableAttributedString;
pub use self::mutable_data::NSMutableData;
pub use self::mutable_set::NSMutableSet;
pub use self::mutable_string::NSMutableString;
pub use self::number::NSNumber;
pub use self::object::NSObject;
pub use self::process_info::NSProcessInfo;
pub use self::range::NSRange;
pub use self::set::NSSet;
pub use self::string::NSString;
pub use self::thread::{is_main_thread, is_multi_threaded, MainThreadMarker, NSThread};
#[cfg(not(macos_10_7))] // Temporary
Expand Down Expand Up @@ -104,11 +106,13 @@ mod geometry;
mod mutable_array;
mod mutable_attributed_string;
mod mutable_data;
mod mutable_set;
mod mutable_string;
mod number;
mod object;
mod process_info;
mod range;
mod set;
mod string;
mod thread;
// Temporarily disable testing UUID on macOS 10.7 until
Expand Down Expand Up @@ -158,6 +162,7 @@ mod tests {
assert_auto_traits::<NSComparisonResult>();
assert_auto_traits::<NSData>();
assert_auto_traits::<NSDictionary<NSString, NSString>>();
assert_auto_traits::<NSSet<NSString>>();
// TODO: Figure out if Send + Sync is safe?
// assert_auto_traits::<NSEnumerator<NSString>>();
// assert_auto_traits::<NSFastEnumerator<NSArray<NSString, Shared>>>();
Expand All @@ -170,6 +175,7 @@ mod tests {
assert_auto_traits::<NSMutableArray<NSString, Shared>>();
assert_auto_traits::<NSMutableAttributedString>();
assert_auto_traits::<NSMutableData>();
assert_auto_traits::<NSMutableSet<NSString>>();
assert_auto_traits::<NSMutableString>();
assert_auto_traits::<NSNumber>();
// assert_auto_traits::<NSObject>(); // Intentional
Expand Down
291 changes: 291 additions & 0 deletions objc2/src/foundation/mutable_set.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
use alloc::vec::Vec;
use core::fmt;
use core::marker::PhantomData;

use super::set::with_objects;
use super::{NSCopying, NSFastEnumeration, NSFastEnumerator, NSMutableCopying, NSObject, NSSet};
use crate::rc::{DefaultId, Id, Owned, Ownership, Shared, SliceId};
use crate::{ClassType, Message, __inner_extern_class, extern_methods, msg_send, msg_send_id};

__inner_extern_class!(
/// A growable unordered collection of unique objects.
///
/// See the documentation for [`NSSet`] and/or [Apple's
/// documentation][apple-doc] for more information.
///
/// [apple-doc]: https://developer.apple.com/documentation/foundation/nsmutableset?language=objc
#[derive(PartialEq, Eq, Hash)]
pub struct NSMutableSet<T: Message> {
item: PhantomData<Id<T, Shared>>,
}

unsafe impl<T: Message> ClassType for NSMutableSet<T> {
#[inherits(NSObject)]
type Super = NSSet<T>;
}
);

// SAFETY: Same as NSSet<T>
unsafe impl<T: Message + Sync + Send> Sync for NSMutableSet<T> {}
unsafe impl<T: Message + Sync + Send> Send for NSMutableSet<T> {}

extern_methods!(
unsafe impl<T: Message> NSMutableSet<T> {
/// Creates an empty `NSMutableSet`.
///
/// # Examples
///
/// ```
/// use objc2::foundation::{NSMutableSet, NSString};
///
/// let set = NSMutableSet::<NSString>::new();
/// ```
pub fn new() -> Id<Self, Owned> {
// SAFETY:
// Same as `NSSet::new`, except mutable sets are always unique.
unsafe { msg_send_id![Self::class(), new] }
}

/// 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<O: Ownership>(vec: Vec<Id<T, O>>) -> Id<Self, Owned> {
// SAFETY:
// We always return `Id<NSMutableSet<T, Shared>, Owned>` because
// mutable sets are always unique and allow adding/removing elements
// but prevent modifying elements.
unsafe { with_objects(Self::class(), vec.as_slice_ref()) }
}

/// Creates an `NSMutableSet` from a slice.
///
/// # Examples
///
/// ```
/// use objc2::foundation::{NSMutableSet, NSString};
///
/// let strs = ["one", "two", "three"].map(NSString::from_str);
/// let set = NSMutableSet::from_slice(&strs);
/// ```
pub fn from_slice(slice: &[Id<T, Shared>]) -> Id<Self, Owned> {
// SAFETY:
// Taking `&T` would not be sound, since the `&T` could come
// from an `Id<T, Owned>` that would now no longer be owned!
//
// We always return `Id<NSMutableSet<T, Shared>, Owned>` because
// mutable sets are always unique and allow adding/removing elements
// but prevent modifying elements.
unsafe { with_objects(Self::class(), slice.as_slice_ref()) }
}

/// Adds a value 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(NSString::from_str("one")), true);
/// assert_eq!(set.insert(NSString::from_str("one")), false);
/// assert_eq!(set.len(), 1);
/// ```
#[doc(alias = "addObject:")]
pub fn insert<O: Ownership>(&mut self, value: Id<T, O>) -> bool {
let contains_value = self.contains(&value);
// SAFETY: The object is not nil
unsafe { msg_send![self, addObject: &*value] }
!contains_value
}

/// Removes a value from the set. Returns whether the value was
/// present in the set.
///
/// # Examples
///
/// ```
/// use objc2::foundation::{NSMutableSet, NSString};
///
/// let mut set = NSMutableSet::new();
///
/// set.insert(NSString::from_str("one"));
/// assert_eq!(set.remove(&NSString::from_str("one")), true);
/// assert_eq!(set.remove(&NSString::from_str("one")), false);
/// ```
#[doc(alias = "removeObject:")]
pub fn remove(&mut self, value: &T) -> bool {
let contains_value = self.contains(value);
unsafe { msg_send![self, removeObject: value] }
contains_value
}

/// Clears the set, removing all values.
///
/// # Examples
///
/// ```
/// use objc2::foundation::{NSMutableSet, NSString};
///
/// let mut set = NSMutableSet::new();
/// set.insert(NSString::from_str("one"));
/// set.clear();
/// assert!(set.is_empty());
/// ```
#[doc(alias = "removeAllObjects")]
#[sel(removeAllObjects)]
pub fn clear(&mut self);
}
);

unsafe impl<T: Message> NSCopying for NSMutableSet<T> {
type Ownership = Shared;
type Output = NSSet<T>;
}

unsafe impl<T: Message> NSMutableCopying for NSMutableSet<T> {
type Output = NSMutableSet<T>;
}

impl<T: Message> alloc::borrow::ToOwned for NSMutableSet<T> {
type Owned = Id<NSMutableSet<T>, Owned>;
fn to_owned(&self) -> Self::Owned {
self.mutable_copy()
}
}

unsafe impl<T: Message> NSFastEnumeration for NSMutableSet<T> {
type Item = T;
}

impl<'a, T: Message> IntoIterator for &'a NSMutableSet<T> {
type Item = &'a T;
type IntoIter = NSFastEnumerator<'a, NSMutableSet<T>>;

fn into_iter(self) -> Self::IntoIter {
self.iter_fast()
}
}

impl<T: Message, O: Ownership> Extend<Id<T, O>> for NSMutableSet<T> {
fn extend<I: IntoIterator<Item = Id<T, O>>>(&mut self, iter: I) {
for item in iter {
self.insert(item);
}
}
}

impl<T: Message> DefaultId for NSMutableSet<T> {
type Ownership = Owned;

#[inline]
fn default_id() -> Id<Self, Self::Ownership> {
Self::new()
}
}

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(test)]
mod tests {
use super::*;
use crate::foundation::NSString;
use crate::rc::{RcTestObject, ThreadTestData};

#[test]
fn test_insert() {
let mut set = NSMutableSet::new();
assert!(set.is_empty());

assert!(set.insert(NSString::from_str("one")));
assert!(!set.insert(NSString::from_str("one")));
assert!(set.insert(NSString::from_str("two")));
}

#[test]
fn test_remove() {
let strs = ["one", "two", "three"].map(NSString::from_str);
let mut set = NSMutableSet::from_slice(&strs);

assert!(set.remove(&NSString::from_str("one")));
assert!(!set.remove(&NSString::from_str("one")));
}

#[test]
fn test_clear() {
let strs = ["one", "two", "three"].map(NSString::from_str);
let mut set = NSMutableSet::from_slice(&strs);
assert_eq!(set.len(), 3);

set.clear();
assert!(set.is_empty());
}

#[test]
fn test_extend() {
let mut set = NSMutableSet::new();
assert!(set.is_empty());

set.extend(["one", "two", "three"].map(NSString::from_str));
assert_eq!(set.len(), 3);
}

#[test]
fn test_mutable_copy() {
let set1 = NSSet::from_slice(&["one", "two", "three"].map(NSString::from_str));
let mut set2 = set1.mutable_copy();
set2.insert(NSString::from_str("four"));

assert!(set1.is_subset(&set2));
assert_ne!(set1.mutable_copy(), set2);
}

#[test]
fn test_insert_retain_release() {
let mut set = NSMutableSet::new();
let obj1 = RcTestObject::new();
let obj2 = RcTestObject::new();
let mut expected = ThreadTestData::current();

set.insert(obj1);
expected.retain += 1;
expected.release += 1;
expected.assert_current();
assert_eq!(set.len(), 1);
assert_eq!(set.get_any(), set.get_any());

set.insert(obj2);
expected.retain += 1;
expected.release += 1;
expected.assert_current();
assert_eq!(set.len(), 2);
}

#[test]
fn test_clear_release_dealloc() {
let mut set = NSMutableSet::new();
for _ in 0..4 {
set.insert(RcTestObject::new());
}
let mut expected = ThreadTestData::current();

set.clear();
expected.release += 4;
expected.dealloc += 4;
expected.assert_current();
assert_eq!(set.len(), 0);
}
}
Loading

0 comments on commit bfa9d16

Please sign in to comment.