Skip to content

Commit 282b0f7

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

File tree

3 files changed

+919
-0
lines changed

3 files changed

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

0 commit comments

Comments
 (0)