Skip to content

Commit bfa9d16

Browse files
committed
Add NSSet and NSMutableSet
1 parent 096ba02 commit bfa9d16

File tree

3 files changed

+872
-0
lines changed

3 files changed

+872
-0
lines changed

objc2/src/foundation/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,13 @@ pub use self::geometry::{CGFloat, NSPoint, NSRect, NSSize};
6464
pub use self::mutable_array::NSMutableArray;
6565
pub use self::mutable_attributed_string::NSMutableAttributedString;
6666
pub use self::mutable_data::NSMutableData;
67+
pub use self::mutable_set::NSMutableSet;
6768
pub use self::mutable_string::NSMutableString;
6869
pub use self::number::NSNumber;
6970
pub use self::object::NSObject;
7071
pub use self::process_info::NSProcessInfo;
7172
pub use self::range::NSRange;
73+
pub use self::set::NSSet;
7274
pub use self::string::NSString;
7375
pub use self::thread::{is_main_thread, is_multi_threaded, MainThreadMarker, NSThread};
7476
#[cfg(not(macos_10_7))] // Temporary
@@ -104,11 +106,13 @@ mod geometry;
104106
mod mutable_array;
105107
mod mutable_attributed_string;
106108
mod mutable_data;
109+
mod mutable_set;
107110
mod mutable_string;
108111
mod number;
109112
mod object;
110113
mod process_info;
111114
mod range;
115+
mod set;
112116
mod string;
113117
mod thread;
114118
// Temporarily disable testing UUID on macOS 10.7 until
@@ -158,6 +162,7 @@ mod tests {
158162
assert_auto_traits::<NSComparisonResult>();
159163
assert_auto_traits::<NSData>();
160164
assert_auto_traits::<NSDictionary<NSString, NSString>>();
165+
assert_auto_traits::<NSSet<NSString>>();
161166
// TODO: Figure out if Send + Sync is safe?
162167
// assert_auto_traits::<NSEnumerator<NSString>>();
163168
// assert_auto_traits::<NSFastEnumerator<NSArray<NSString, Shared>>>();
@@ -170,6 +175,7 @@ mod tests {
170175
assert_auto_traits::<NSMutableArray<NSString, Shared>>();
171176
assert_auto_traits::<NSMutableAttributedString>();
172177
assert_auto_traits::<NSMutableData>();
178+
assert_auto_traits::<NSMutableSet<NSString>>();
173179
assert_auto_traits::<NSMutableString>();
174180
assert_auto_traits::<NSNumber>();
175181
// assert_auto_traits::<NSObject>(); // Intentional

objc2/src/foundation/mutable_set.rs

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
use alloc::vec::Vec;
2+
use core::fmt;
3+
use core::marker::PhantomData;
4+
5+
use super::set::with_objects;
6+
use super::{NSCopying, NSFastEnumeration, NSFastEnumerator, NSMutableCopying, NSObject, NSSet};
7+
use crate::rc::{DefaultId, Id, Owned, Ownership, Shared, SliceId};
8+
use crate::{ClassType, Message, __inner_extern_class, extern_methods, msg_send, msg_send_id};
9+
10+
__inner_extern_class!(
11+
/// A growable unordered collection of unique objects.
12+
///
13+
/// See the documentation for [`NSSet`] and/or [Apple's
14+
/// documentation][apple-doc] for more information.
15+
///
16+
/// [apple-doc]: https://developer.apple.com/documentation/foundation/nsmutableset?language=objc
17+
#[derive(PartialEq, Eq, Hash)]
18+
pub struct NSMutableSet<T: Message> {
19+
item: PhantomData<Id<T, Shared>>,
20+
}
21+
22+
unsafe impl<T: Message> ClassType for NSMutableSet<T> {
23+
#[inherits(NSObject)]
24+
type Super = NSSet<T>;
25+
}
26+
);
27+
28+
// SAFETY: Same as NSSet<T>
29+
unsafe impl<T: Message + Sync + Send> Sync for NSMutableSet<T> {}
30+
unsafe impl<T: Message + Sync + Send> Send for NSMutableSet<T> {}
31+
32+
extern_methods!(
33+
unsafe impl<T: Message> NSMutableSet<T> {
34+
/// Creates an empty `NSMutableSet`.
35+
///
36+
/// # Examples
37+
///
38+
/// ```
39+
/// use objc2::foundation::{NSMutableSet, NSString};
40+
///
41+
/// let set = NSMutableSet::<NSString>::new();
42+
/// ```
43+
pub fn new() -> Id<Self, Owned> {
44+
// SAFETY:
45+
// Same as `NSSet::new`, except mutable sets are always unique.
46+
unsafe { msg_send_id![Self::class(), new] }
47+
}
48+
49+
/// Creates an `NSMutableSet` from a vector.
50+
///
51+
/// # Examples
52+
///
53+
/// ```
54+
/// use objc2::foundation::{NSMutableSet, NSString};
55+
///
56+
/// let strs = ["one", "two", "three"].map(NSString::from_str).to_vec();
57+
/// let set = NSMutableSet::from_vec(strs);
58+
/// ```
59+
pub fn from_vec<O: Ownership>(vec: Vec<Id<T, O>>) -> Id<Self, Owned> {
60+
// SAFETY:
61+
// We always return `Id<NSMutableSet<T, Shared>, Owned>` because
62+
// mutable sets are always unique and allow adding/removing elements
63+
// but prevent modifying elements.
64+
unsafe { with_objects(Self::class(), vec.as_slice_ref()) }
65+
}
66+
67+
/// Creates an `NSMutableSet` from a slice.
68+
///
69+
/// # Examples
70+
///
71+
/// ```
72+
/// use objc2::foundation::{NSMutableSet, NSString};
73+
///
74+
/// let strs = ["one", "two", "three"].map(NSString::from_str);
75+
/// let set = NSMutableSet::from_slice(&strs);
76+
/// ```
77+
pub fn from_slice(slice: &[Id<T, Shared>]) -> Id<Self, Owned> {
78+
// SAFETY:
79+
// Taking `&T` would not be sound, since the `&T` could come
80+
// from an `Id<T, Owned>` that would now no longer be owned!
81+
//
82+
// We always return `Id<NSMutableSet<T, Shared>, Owned>` because
83+
// mutable sets are always unique and allow adding/removing elements
84+
// but prevent modifying elements.
85+
unsafe { with_objects(Self::class(), slice.as_slice_ref()) }
86+
}
87+
88+
/// Adds a value to the set. Returns whether the value was
89+
/// newly inserted.
90+
///
91+
/// # Examples
92+
///
93+
/// ```
94+
/// use objc2::foundation::{NSMutableSet, NSString};
95+
///
96+
/// let mut set = NSMutableSet::new();
97+
///
98+
/// assert_eq!(set.insert(NSString::from_str("one")), true);
99+
/// assert_eq!(set.insert(NSString::from_str("one")), false);
100+
/// assert_eq!(set.len(), 1);
101+
/// ```
102+
#[doc(alias = "addObject:")]
103+
pub fn insert<O: Ownership>(&mut self, value: Id<T, O>) -> bool {
104+
let contains_value = self.contains(&value);
105+
// SAFETY: The object is not nil
106+
unsafe { msg_send![self, addObject: &*value] }
107+
!contains_value
108+
}
109+
110+
/// Removes a value from the set. Returns whether the value was
111+
/// present in the set.
112+
///
113+
/// # Examples
114+
///
115+
/// ```
116+
/// use objc2::foundation::{NSMutableSet, NSString};
117+
///
118+
/// let mut set = NSMutableSet::new();
119+
///
120+
/// set.insert(NSString::from_str("one"));
121+
/// assert_eq!(set.remove(&NSString::from_str("one")), true);
122+
/// assert_eq!(set.remove(&NSString::from_str("one")), false);
123+
/// ```
124+
#[doc(alias = "removeObject:")]
125+
pub fn remove(&mut self, value: &T) -> bool {
126+
let contains_value = self.contains(value);
127+
unsafe { msg_send![self, removeObject: value] }
128+
contains_value
129+
}
130+
131+
/// Clears the set, removing all values.
132+
///
133+
/// # Examples
134+
///
135+
/// ```
136+
/// use objc2::foundation::{NSMutableSet, NSString};
137+
///
138+
/// let mut set = NSMutableSet::new();
139+
/// set.insert(NSString::from_str("one"));
140+
/// set.clear();
141+
/// assert!(set.is_empty());
142+
/// ```
143+
#[doc(alias = "removeAllObjects")]
144+
#[sel(removeAllObjects)]
145+
pub fn clear(&mut self);
146+
}
147+
);
148+
149+
unsafe impl<T: Message> NSCopying for NSMutableSet<T> {
150+
type Ownership = Shared;
151+
type Output = NSSet<T>;
152+
}
153+
154+
unsafe impl<T: Message> NSMutableCopying for NSMutableSet<T> {
155+
type Output = NSMutableSet<T>;
156+
}
157+
158+
impl<T: Message> alloc::borrow::ToOwned for NSMutableSet<T> {
159+
type Owned = Id<NSMutableSet<T>, Owned>;
160+
fn to_owned(&self) -> Self::Owned {
161+
self.mutable_copy()
162+
}
163+
}
164+
165+
unsafe impl<T: Message> NSFastEnumeration for NSMutableSet<T> {
166+
type Item = T;
167+
}
168+
169+
impl<'a, T: Message> IntoIterator for &'a NSMutableSet<T> {
170+
type Item = &'a T;
171+
type IntoIter = NSFastEnumerator<'a, NSMutableSet<T>>;
172+
173+
fn into_iter(self) -> Self::IntoIter {
174+
self.iter_fast()
175+
}
176+
}
177+
178+
impl<T: Message, O: Ownership> Extend<Id<T, O>> for NSMutableSet<T> {
179+
fn extend<I: IntoIterator<Item = Id<T, O>>>(&mut self, iter: I) {
180+
for item in iter {
181+
self.insert(item);
182+
}
183+
}
184+
}
185+
186+
impl<T: Message> DefaultId for NSMutableSet<T> {
187+
type Ownership = Owned;
188+
189+
#[inline]
190+
fn default_id() -> Id<Self, Self::Ownership> {
191+
Self::new()
192+
}
193+
}
194+
195+
impl<T: fmt::Debug + Message> fmt::Debug for NSMutableSet<T> {
196+
#[inline]
197+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198+
fmt::Debug::fmt(&**self, f)
199+
}
200+
}
201+
202+
#[cfg(test)]
203+
mod tests {
204+
use super::*;
205+
use crate::foundation::NSString;
206+
use crate::rc::{RcTestObject, ThreadTestData};
207+
208+
#[test]
209+
fn test_insert() {
210+
let mut set = NSMutableSet::new();
211+
assert!(set.is_empty());
212+
213+
assert!(set.insert(NSString::from_str("one")));
214+
assert!(!set.insert(NSString::from_str("one")));
215+
assert!(set.insert(NSString::from_str("two")));
216+
}
217+
218+
#[test]
219+
fn test_remove() {
220+
let strs = ["one", "two", "three"].map(NSString::from_str);
221+
let mut set = NSMutableSet::from_slice(&strs);
222+
223+
assert!(set.remove(&NSString::from_str("one")));
224+
assert!(!set.remove(&NSString::from_str("one")));
225+
}
226+
227+
#[test]
228+
fn test_clear() {
229+
let strs = ["one", "two", "three"].map(NSString::from_str);
230+
let mut set = NSMutableSet::from_slice(&strs);
231+
assert_eq!(set.len(), 3);
232+
233+
set.clear();
234+
assert!(set.is_empty());
235+
}
236+
237+
#[test]
238+
fn test_extend() {
239+
let mut set = NSMutableSet::new();
240+
assert!(set.is_empty());
241+
242+
set.extend(["one", "two", "three"].map(NSString::from_str));
243+
assert_eq!(set.len(), 3);
244+
}
245+
246+
#[test]
247+
fn test_mutable_copy() {
248+
let set1 = NSSet::from_slice(&["one", "two", "three"].map(NSString::from_str));
249+
let mut set2 = set1.mutable_copy();
250+
set2.insert(NSString::from_str("four"));
251+
252+
assert!(set1.is_subset(&set2));
253+
assert_ne!(set1.mutable_copy(), set2);
254+
}
255+
256+
#[test]
257+
fn test_insert_retain_release() {
258+
let mut set = NSMutableSet::new();
259+
let obj1 = RcTestObject::new();
260+
let obj2 = RcTestObject::new();
261+
let mut expected = ThreadTestData::current();
262+
263+
set.insert(obj1);
264+
expected.retain += 1;
265+
expected.release += 1;
266+
expected.assert_current();
267+
assert_eq!(set.len(), 1);
268+
assert_eq!(set.get_any(), set.get_any());
269+
270+
set.insert(obj2);
271+
expected.retain += 1;
272+
expected.release += 1;
273+
expected.assert_current();
274+
assert_eq!(set.len(), 2);
275+
}
276+
277+
#[test]
278+
fn test_clear_release_dealloc() {
279+
let mut set = NSMutableSet::new();
280+
for _ in 0..4 {
281+
set.insert(RcTestObject::new());
282+
}
283+
let mut expected = ThreadTestData::current();
284+
285+
set.clear();
286+
expected.release += 4;
287+
expected.dealloc += 4;
288+
expected.assert_current();
289+
assert_eq!(set.len(), 0);
290+
}
291+
}

0 commit comments

Comments
 (0)