-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
872 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
Oops, something went wrong.