diff --git a/crates/icrate/src/Foundation/additions/array.rs b/crates/icrate/src/Foundation/additions/array.rs index e7be2b668..1c01f4118 100644 --- a/crates/icrate/src/Foundation/additions/array.rs +++ b/crates/icrate/src/Foundation/additions/array.rs @@ -10,65 +10,130 @@ use objc2::mutability::{IsMutable, IsRetainable}; use super::util; use crate::common::*; +#[cfg(feature = "Foundation_NSMutableArray")] +use crate::Foundation::NSMutableArray; use crate::Foundation::{self, NSArray}; -extern_methods!( - /// Creation methods. - unsafe impl NSArray { - pub fn from_vec(mut vec: Vec>) -> Id { - // We intentionally extract the length before we access the - // pointer as mutable, to not invalidate that mutable pointer. - let len = vec.len(); - let ptr = util::id_ptr_cast(vec.as_mut_ptr()); - // SAFETY: We've consumed the `Id`s, which means that we can - // now safely take ownership (even if `T` is mutable). - unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } - // The drop of `Vec` here would invalidate our mutable pointer, - // except for the fact that we're using `UnsafeCell` in `Object`. - } +impl NSArray { + pub fn from_vec(mut vec: Vec>) -> Id { + // We intentionally extract the length before we access the + // pointer as mutable, to not invalidate that mutable pointer. + let len = vec.len(); + let ptr = util::id_ptr_cast(vec.as_mut_ptr()); + // SAFETY: We've consumed the `Id`s, which means that we can + // now safely take ownership (even if `T` is mutable). + unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } + // The drop of `Vec` here would invalidate our mutable pointer, + // except for the fact that we're using `UnsafeCell` in `Object`. + } - pub fn from_id_slice(slice: &[Id]) -> Id - where - T: IsIdCloneable, - { - let len = slice.len(); - let ptr = util::id_ptr_cast_const(slice.as_ptr()); - // SAFETY: Because of the `T: IsIdCloneable` bound, and since we - // take `&[Id]` (effectively `&Id`), we are allowed to give - // the slice to Objective-C, which will retain it internally. - // - // Faster version of: - // Self::from_vec(slice.iter().map(|obj| obj.clone()).collect()) - unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } - } + pub fn from_id_slice(slice: &[Id]) -> Id + where + T: IsIdCloneable, + { + let len = slice.len(); + let ptr = util::id_ptr_cast_const(slice.as_ptr()); + // SAFETY: Because of the `T: IsIdCloneable` bound, and since we + // take `&[Id]` (effectively `&Id`), we are allowed to give + // the slice to Objective-C, which will retain it internally. + // + // Faster version of: + // Self::from_vec(slice.iter().map(|obj| obj.clone()).collect()) + unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } + } - pub fn from_slice(slice: &[&T]) -> Id - where - T: IsRetainable, - { - let len = slice.len(); - let ptr = util::ref_ptr_cast_const(slice.as_ptr()); - // SAFETY: Because of the `T: IsRetainable` bound, we are allowed - // to give the slice to Objective-C, which will retain it - // internally. - // - // Faster version of: - // Self::from_vec(slice.iter().map(|obj| obj.retain()).collect()) - unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } - } + pub fn from_slice(slice: &[&T]) -> Id + where + T: IsRetainable, + { + let len = slice.len(); + let ptr = util::ref_ptr_cast_const(slice.as_ptr()); + // SAFETY: Because of the `T: IsRetainable` bound, we are allowed + // to give the slice to Objective-C, which will retain it + // internally. + // + // Faster version of: + // Self::from_vec(slice.iter().map(|obj| obj.retain()).collect()) + unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } } - /// Accessor methods. - unsafe impl NSArray { - #[doc(alias = "count")] - pub fn len(&self) -> usize { - self.count() - } + #[doc(alias = "getObjects:range:")] + pub fn to_vec(&self) -> Vec<&T> { + // SAFETY: The range is know to be in bounds + unsafe { self.objects_in_range_unchecked(0..self.len()) } + } - pub fn is_empty(&self) -> bool { - self.len() == 0 - } + #[doc(alias = "getObjects:range:")] + pub fn to_vec_retained(&self) -> Vec> + where + T: IsIdCloneable, + { + // SAFETY: The objects are stored in the array + self.to_vec() + .into_iter() + .map(|obj| unsafe { util::collection_retain_id(obj) }) + .collect() + } + + // `fn into_vec(Id) -> Vec>` would not be safe, since + // the array itself is unconditionally `IsIdCloneable`, even when + // containing mutable elements, and hence we would be able to + // duplicate those. +} + +#[cfg(feature = "Foundation_NSMutableArray")] +impl NSMutableArray { + pub fn from_vec(mut vec: Vec>) -> Id { + let len = vec.len(); + let ptr = util::id_ptr_cast(vec.as_mut_ptr()); + // SAFETY: Same as `NSArray::from_vec`. + unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } + } + pub fn from_id_slice(slice: &[Id]) -> Id + where + T: IsIdCloneable, + { + let len = slice.len(); + let ptr = util::id_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]) -> Id + where + T: 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) } + } + + pub fn into_vec(array: Id) -> Vec> { + // SAFETY: We've consumed the array, so taking ownership of the + // returned values is safe. + array + .to_vec() + .into_iter() + .map(|obj| unsafe { util::mutable_collection_retain_removed_id(obj) }) + .collect() + } +} + +impl NSArray { + #[doc(alias = "count")] + pub fn len(&self) -> usize { + self.count() + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +extern_methods!( + unsafe impl NSArray { #[method(objectAtIndex:)] unsafe fn get_unchecked(&self, index: usize) -> &T; @@ -151,64 +216,139 @@ extern_methods!( pub fn last_mut(&mut self) -> Option<&mut T> where T: IsMutable; + } +); - #[doc(alias = "objectEnumerator")] - #[cfg(feature = "Foundation_NSEnumerator")] - pub fn iter(&self) -> Foundation::NSEnumerator2<'_, T> { - unsafe { - let result: *mut Object = msg_send![self, objectEnumerator]; - Foundation::NSEnumerator2::from_ptr(result) - } +impl NSArray { + #[doc(alias = "objectEnumerator")] + #[cfg(feature = "Foundation_NSEnumerator")] + pub fn iter(&self) -> Foundation::NSEnumerator2<'_, T> { + unsafe { + let result: *mut Object = msg_send![self, objectEnumerator]; + Foundation::NSEnumerator2::from_ptr(result) } + } - unsafe fn objects_in_range_unchecked(&self, range: Range) -> Vec<&T> { - let range = Foundation::NSRange::from(range); - let mut vec: Vec> = Vec::with_capacity(range.length); - unsafe { - self.getObjects_range(NonNull::new(vec.as_mut_ptr()).unwrap(), range); - vec.set_len(range.length); - mem::transmute(vec) - } + unsafe fn objects_in_range_unchecked(&self, range: Range) -> Vec<&T> { + let range = Foundation::NSRange::from(range); + let mut vec: Vec> = Vec::with_capacity(range.length); + unsafe { + self.getObjects_range(NonNull::new(vec.as_mut_ptr()).unwrap(), range); + vec.set_len(range.length); + mem::transmute(vec) } + } - #[doc(alias = "getObjects:range:")] - pub fn objects_in_range(&self, range: Range) -> Option> { - if range.end > self.len() { - return None; - } - // SAFETY: Just checked that the range is in bounds - Some(unsafe { self.objects_in_range_unchecked(range) }) + #[doc(alias = "getObjects:range:")] + pub fn objects_in_range(&self, range: Range) -> Option> { + if range.end > self.len() { + return None; } + // SAFETY: Just checked that the range is in bounds + Some(unsafe { self.objects_in_range_unchecked(range) }) + } +} - #[doc(alias = "getObjects:range:")] - pub fn to_vec(&self) -> Vec<&T> { - // SAFETY: The range is know to be in bounds - unsafe { self.objects_in_range_unchecked(0..self.len()) } +#[cfg(feature = "Foundation_NSMutableArray")] +impl NSMutableArray { + #[doc(alias = "addObject:")] + pub fn push(&mut self, obj: Id) { + // SAFETY: We've consumed ownership of the object. + unsafe { self.addObject(&obj) } + } + + #[doc(alias = "insertObject:atIndex:")] + pub fn insert(&mut self, index: usize, obj: Id) { + // TODO: Replace this check with catching the thrown NSRangeException + let len = self.len(); + if index < len { + // SAFETY: We've consumed ownership of the object, and the + // index is checked to be in bounds. + unsafe { self.insertObject_atIndex(&obj, index) } + } else { + panic!( + "insertion index (is {}) should be <= len (is {})", + index, len + ); } + } - #[doc(alias = "getObjects:range:")] - pub fn to_vec_retained(&self) -> Vec> - where - T: IsIdCloneable, - { - // SAFETY: The objects are stored in the array - self.to_vec() - .into_iter() - .map(|obj| unsafe { util::collection_retain_id(obj) }) - .collect() + #[doc(alias = "replaceObjectAtIndex:withObject:")] + pub fn replace(&mut self, index: usize, obj: Id) -> Result, Id> { + if let Some(old_obj) = self.get(index) { + // SAFETY: We remove the object from the array below. + let old_obj = unsafe { util::mutable_collection_retain_removed_id(old_obj) }; + // SAFETY: The index is checked to be in bounds, and we've + // consumed ownership of the new object. + unsafe { self.replaceObjectAtIndex_withObject(index, &obj) }; + Ok(old_obj) + } else { + Err(obj) } + } - // `fn into_vec(Id) -> Vec>` would not be safe, since - // the array itself is unconditionally `IsIdCloneable`, even when - // containing mutable elements, and hence we would be able to - // duplicate those. + #[doc(alias = "removeObjectAtIndex:")] + pub fn remove(&mut self, index: usize) -> Option> { + let obj = self.get(index)?; + // SAFETY: We remove the object from the array below. + let obj = unsafe { util::mutable_collection_retain_removed_id(obj) }; + // SAFETY: The index is checked to be in bounds. + unsafe { self.removeObjectAtIndex(index) }; + Some(obj) } -); + + #[doc(alias = "removeLastObject")] + pub fn pop(&mut self) -> Option> { + let obj = self.last()?; + // SAFETY: We remove the object from the array below. + let obj = unsafe { util::mutable_collection_retain_removed_id(obj) }; + // SAFETY: Just checked that there is an object. + unsafe { self.removeLastObject() }; + Some(obj) + } + + #[doc(alias = "sortUsingFunction:context:")] + pub fn sort_by core::cmp::Ordering>(&mut self, compare: F) { + // TODO: "C-unwind" + unsafe extern "C" fn compare_with_closure core::cmp::Ordering>( + obj1: NonNull, + obj2: NonNull, + context: *mut c_void, + ) -> isize { + let context: *mut F = context.cast(); + // Bring back a reference to the closure. + // Guaranteed to be unique, we gave `sortUsingFunction` unique is + // ownership, and that method only runs one function at a time. + let closure: &mut F = unsafe { context.as_mut().unwrap_unchecked() }; + + // SAFETY: The objects are guaranteed to be valid + let (obj1, obj2) = unsafe { (obj1.as_ref(), obj2.as_ref()) }; + + Foundation::NSComparisonResult::from((*closure)(obj1, obj2)) as _ + } + + // Create function pointer + let f: unsafe extern "C" fn(_, _, _) -> _ = compare_with_closure::; + + // Grab a type-erased pointer to the closure (a pointer to stack). + let mut closure = compare; + let context: *mut F = &mut closure; + + unsafe { self.sortUsingFunction_context(f, context.cast()) }; + // Keep the closure alive until the function has run. + drop(closure); + } +} unsafe impl Foundation::NSFastEnumeration2 for NSArray { type Item = T; } +#[cfg(feature = "Foundation_NSMutableArray")] +unsafe impl Foundation::NSFastEnumeration2 for NSMutableArray { + type Item = T; +} + impl<'a, T: Message> IntoIterator for &'a NSArray { type Item = &'a T; type IntoIter = Foundation::NSFastEnumerator2<'a, NSArray>; @@ -219,6 +359,17 @@ impl<'a, T: Message> IntoIterator for &'a NSArray { } } +#[cfg(feature = "Foundation_NSMutableArray")] +impl<'a, T: Message> IntoIterator for &'a NSMutableArray { + type Item = &'a T; + type IntoIter = Foundation::NSFastEnumerator2<'a, NSMutableArray>; + + fn into_iter(self) -> Self::IntoIter { + use Foundation::NSFastEnumeration2; + self.iter_fast() + } +} + impl Index for NSArray { type Output = T; @@ -227,12 +378,28 @@ impl Index for NSArray { } } +#[cfg(feature = "Foundation_NSMutableArray")] +impl Index for NSMutableArray { + type Output = T; + + fn index(&self, index: usize) -> &T { + self.get(index).unwrap() + } +} + impl IndexMut for NSArray { fn index_mut(&mut self, index: usize) -> &mut T { self.get_mut(index).unwrap() } } +#[cfg(feature = "Foundation_NSMutableArray")] +impl IndexMut for NSMutableArray { + fn index_mut(&mut self, index: usize) -> &mut T { + self.get_mut(index).unwrap() + } +} + impl fmt::Debug for NSArray { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -240,3 +407,20 @@ impl fmt::Debug for NSArray { f.debug_list().entries(self.iter_fast()).finish() } } + +#[cfg(feature = "Foundation_NSMutableArray")] +impl Extend> for NSMutableArray { + fn extend>>(&mut self, iter: I) { + iter.into_iter().for_each(move |item| self.push(item)) + } +} + +#[cfg(feature = "Foundation_NSMutableArray")] +impl<'a, T: IsRetainable> Extend<&'a T> for NSMutableArray { + fn extend>(&mut self, iter: I) { + // SAFETY: Because of the `T: IsRetainable` bound, it is safe for the + // array to retain the object here. + iter.into_iter() + .for_each(move |item| unsafe { self.addObject(item) }) + } +} diff --git a/crates/icrate/src/Foundation/additions/attributed_string.rs b/crates/icrate/src/Foundation/additions/attributed_string.rs index bb51c593b..100bcc787 100644 --- a/crates/icrate/src/Foundation/additions/attributed_string.rs +++ b/crates/icrate/src/Foundation/additions/attributed_string.rs @@ -15,32 +15,45 @@ unsafe impl Send for NSAttributedString {} impl UnwindSafe for NSAttributedString {} impl RefUnwindSafe for NSAttributedString {} -extern_methods!( - /// Creating attributed strings. - unsafe impl NSAttributedString { - /// Creates a new attributed string from the given string and attributes. - /// - /// The attributes are associated with every UTF-16 code unit in the - /// string. - /// - /// # Safety - /// - /// The attributes must be valid. - #[doc(alias = "initWithString:")] - #[cfg(feature = "Foundation_NSDictionary")] - #[cfg(feature = "Foundation_NSString")] - pub unsafe fn new_with_attributes( - string: &Foundation::NSString, - attributes: &Foundation::NSDictionary, - ) -> Id { - unsafe { Self::initWithString_attributes(Self::alloc(), string, Some(attributes)) } - } - - /// Creates a new attributed string without any attributes. - #[doc(alias = "initWithString:")] - #[cfg(feature = "Foundation_NSString")] - pub fn from_nsstring(string: &Foundation::NSString) -> Id { - Self::initWithString(Self::alloc(), string) - } +impl NSAttributedString { + /// Creates a new attributed string from the given string and attributes. + /// + /// The attributes are associated with every UTF-16 code unit in the + /// string. + /// + /// # Safety + /// + /// The attributes must be valid. + #[doc(alias = "initWithString:")] + #[cfg(feature = "Foundation_NSDictionary")] + #[cfg(feature = "Foundation_NSString")] + pub unsafe fn new_with_attributes( + string: &Foundation::NSString, + attributes: &Foundation::NSDictionary, + ) -> Id { + unsafe { Self::initWithString_attributes(Self::alloc(), string, Some(attributes)) } } -); + + /// Creates a new attributed string without any attributes. + #[doc(alias = "initWithString:")] + #[cfg(feature = "Foundation_NSString")] + pub fn from_nsstring(string: &Foundation::NSString) -> Id { + Self::initWithString(Self::alloc(), string) + } +} + +#[cfg(feature = "Foundation_NSMutableAttributedString")] +impl Foundation::NSMutableAttributedString { + // TODO: new_with_attributes + + #[doc(alias = "initWithString:")] + #[cfg(feature = "Foundation_NSString")] + pub fn from_nsstring(string: &Foundation::NSString) -> Id { + Self::initWithString(Self::alloc(), string) + } + + #[doc(alias = "initWithAttributedString:")] + pub fn from_attributed_nsstring(attributed_string: &NSAttributedString) -> Id { + Self::initWithAttributedString(Self::alloc(), attributed_string) + } +} diff --git a/crates/icrate/src/Foundation/additions/data.rs b/crates/icrate/src/Foundation/additions/data.rs index 113c10929..38e3c777c 100644 --- a/crates/icrate/src/Foundation/additions/data.rs +++ b/crates/icrate/src/Foundation/additions/data.rs @@ -3,10 +3,14 @@ use alloc::vec::Vec; use core::fmt; use core::ops::Index; +#[cfg(feature = "Foundation_NSMutableData")] +use core::ops::{IndexMut, Range}; use core::panic::{RefUnwindSafe, UnwindSafe}; use core::slice::{self, SliceIndex}; use crate::common::*; +#[cfg(feature = "Foundation_NSMutableData")] +use crate::Foundation::NSMutableData; use crate::Foundation::{self, NSData}; // SAFETY: `NSData` is immutable and `NSMutableData` can only be mutated from @@ -17,7 +21,6 @@ unsafe impl Send for NSData {} impl UnwindSafe for NSData {} impl RefUnwindSafe for NSData {} -/// Creation methods. impl NSData { pub fn with_bytes(bytes: &[u8]) -> Id { let bytes_ptr = bytes.as_ptr() as *mut c_void; @@ -34,15 +37,29 @@ impl NSData { // // NSMutableData does not have this problem. #[cfg(feature = "gnustep-1-7")] - let cls = objc2::class!(NSDataWithDeallocatorBlock); + let obj = unsafe { objc2::msg_send_id![objc2::class!(NSDataWithDeallocatorBlock), alloc] }; #[cfg(not(feature = "gnustep-1-7"))] - let cls = Self::class(); + let obj = Self::alloc(); - unsafe { Id::cast(with_vec(cls, bytes)) } + unsafe { with_vec(obj, bytes) } + } +} + +#[cfg(feature = "Foundation_NSMutableData")] +impl NSMutableData { + pub fn with_bytes(bytes: &[u8]) -> Id { + let bytes_ptr = bytes.as_ptr() as *mut c_void; + // SAFETY: Same as `NSData::with_bytes` + unsafe { Self::initWithBytes_length(Self::alloc(), bytes_ptr, bytes.len()) } + } + + #[cfg(feature = "block")] + pub fn from_vec(bytes: Vec) -> Id { + // SAFETY: Same as `NSData::from_vec` + unsafe { with_vec(Self::alloc(), bytes) } } } -/// Accessor methods. impl NSData { pub fn len(&self) -> usize { self.length() @@ -53,15 +70,54 @@ impl NSData { } pub fn bytes(&self) -> &[u8] { - let ptr = self.bytes_raw(); - let ptr: *const u8 = ptr.cast(); - // The bytes pointer may be null for length zero - if ptr.is_null() { + if let Some(ptr) = self.bytes_raw() { + let ptr: *const u8 = ptr.as_ptr().cast(); + // SAFETY: The pointer is checked to not be NULL, and since we're + // working with raw bytes (`u8`), the alignment is also correct. + unsafe { slice::from_raw_parts(ptr, self.len()) } + } else { + // The bytes pointer may be null for length zero on GNUStep &[] + } + } +} + +#[cfg(feature = "Foundation_NSMutableData")] +impl NSMutableData { + #[doc(alias = "mutableBytes")] + pub fn bytes_mut(&mut self) -> &mut [u8] { + if let Some(ptr) = self.mutable_bytes_raw() { + let ptr: *mut u8 = ptr.as_ptr().cast(); + // SAFETY: Same as `NSData::bytes`, with the addition that a + // mutable slice is safe, since we take `&mut NSMutableData`. + unsafe { slice::from_raw_parts_mut(ptr, self.len()) } } else { - unsafe { slice::from_raw_parts(ptr, self.len()) } + &mut [] } } + + #[doc(alias = "appendBytes:length:")] + pub fn extend_from_slice(&mut self, bytes: &[u8]) { + let bytes_ptr: NonNull = NonNull::new(bytes.as_ptr() as *mut u8).unwrap().cast(); + unsafe { self.appendBytes_length(bytes_ptr, bytes.len()) } + } + + pub fn push(&mut self, byte: u8) { + self.extend_from_slice(&[byte]) + } + + #[doc(alias = "replaceBytesInRange:withBytes:length:")] + pub fn replace_range(&mut self, range: Range, bytes: &[u8]) { + // No need to verify the length of the range here, + // `replaceBytesInRange:` just zero-fills if out of bounds. + let ptr = bytes.as_ptr() as *mut c_void; + unsafe { self.replaceBytesInRange_withBytes_length(range.into(), ptr, bytes.len()) } + } + + pub fn set_bytes(&mut self, bytes: &[u8]) { + let len = self.len(); + self.replace_range(0..len, bytes); + } } impl AsRef<[u8]> for NSData { @@ -70,6 +126,20 @@ impl AsRef<[u8]> for NSData { } } +#[cfg(feature = "Foundation_NSMutableData")] +impl AsRef<[u8]> for NSMutableData { + fn as_ref(&self) -> &[u8] { + self.bytes() + } +} + +#[cfg(feature = "Foundation_NSMutableData")] +impl AsMut<[u8]> for NSMutableData { + fn as_mut(&mut self) -> &mut [u8] { + self.bytes_mut() + } +} + // Note: We don't implement `Borrow<[u8]>` since we can't guarantee that `Eq`, // `Ord` and `Hash` are equal for `NSData` vs. `[u8]`! @@ -83,6 +153,24 @@ impl> Index for NSData { } } +#[cfg(feature = "Foundation_NSMutableData")] +impl> Index for NSMutableData { + type Output = I::Output; + + #[inline] + fn index(&self, index: I) -> &Self::Output { + Index::index(self.bytes(), index) + } +} + +#[cfg(feature = "Foundation_NSMutableData")] +impl> IndexMut for NSMutableData { + #[inline] + fn index_mut(&mut self, index: I) -> &mut Self::Output { + IndexMut::index_mut(self.bytes_mut(), index) + } +} + impl fmt::Debug for NSData { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -100,12 +188,82 @@ impl<'a> IntoIterator for &'a NSData { } } +#[cfg(feature = "Foundation_NSMutableData")] +impl<'a> IntoIterator for &'a NSMutableData { + type Item = &'a u8; + type IntoIter = core::slice::Iter<'a, u8>; + + fn into_iter(self) -> Self::IntoIter { + self.bytes().iter() + } +} + +#[cfg(feature = "Foundation_NSMutableData")] +impl<'a> IntoIterator for &'a mut NSMutableData { + type Item = &'a mut u8; + type IntoIter = core::slice::IterMut<'a, u8>; + + fn into_iter(self) -> Self::IntoIter { + self.bytes_mut().iter_mut() + } +} + +#[cfg(feature = "Foundation_NSMutableData")] +impl Extend for NSMutableData { + /// You should use [`extend_from_slice`] whenever possible, it is more + /// performant. + /// + /// [`extend_from_slice`]: Self::extend_from_slice + fn extend>(&mut self, iter: T) { + let iterator = iter.into_iter(); + iterator.for_each(move |item| self.push(item)); + } +} + +// Vec also has this impl +#[cfg(feature = "Foundation_NSMutableData")] +impl<'a> Extend<&'a u8> for NSMutableData { + fn extend>(&mut self, iter: T) { + let iterator = iter.into_iter(); + iterator.for_each(move |item| self.push(*item)); + } +} + +#[cfg(feature = "Foundation_NSMutableData")] +impl std::io::Write for NSMutableData { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.extend_from_slice(buf); + Ok(buf.len()) + } + + fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { + self.extend_from_slice(buf); + Ok(()) + } + + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } +} + +// #[cfg(feature = "Foundation_NSMutableData")] +// impl FromIterator for Id { +// fn from_iter>(iter: T) -> Self { +// let iter = iter.into_iter(); +// let (lower, _) = iter.size_hint(); +// let data = Self::with_capacity(lower); +// for item in iter { +// data.push(item); +// } +// data +// } +// } + #[cfg(feature = "block")] -pub(crate) unsafe fn with_vec(cls: &Class, bytes: Vec) -> Id { +unsafe fn with_vec(obj: Option>, bytes: Vec) -> Id { use core::mem::ManuallyDrop; use block2::{Block, ConcreteBlock}; - use objc2::msg_send_id; let capacity = bytes.capacity(); @@ -120,8 +278,8 @@ pub(crate) unsafe fn with_vec(cls: &Class, bytes: Vec) -> Id { let bytes_ptr: *mut c_void = bytes.as_mut_ptr().cast(); unsafe { - msg_send_id![ - msg_send_id![cls, alloc], + objc2::msg_send_id![ + obj, initWithBytesNoCopy: bytes_ptr, length: bytes.len(), deallocator: dealloc, diff --git a/crates/icrate/src/Foundation/additions/dictionary.rs b/crates/icrate/src/Foundation/additions/dictionary.rs index 1f591e9fe..0ac8ad738 100644 --- a/crates/icrate/src/Foundation/additions/dictionary.rs +++ b/crates/icrate/src/Foundation/additions/dictionary.rs @@ -3,7 +3,7 @@ use alloc::vec::Vec; use core::cmp::min; use core::fmt; use core::mem; -use core::ops::Index; +use core::ops::{Index, IndexMut}; use core::panic::{RefUnwindSafe, UnwindSafe}; use core::ptr::{self, NonNull}; @@ -13,24 +13,45 @@ use objc2::runtime::Object; use super::util; use crate::common::*; +#[cfg(feature = "Foundation_NSMutableDictionary")] +use crate::Foundation::NSMutableDictionary; use crate::Foundation::{self, Copyhelper, NSDictionary}; -extern_methods!( - unsafe impl NSDictionary { - pub fn from_keys_and_objects(keys: &[&T], mut vals: Vec>) -> Id - where - T: ClassType + Foundation::NSCopying, - T::Mutability: Copyhelper, - { - let count = min(keys.len(), vals.len()); +impl NSDictionary { + pub fn from_keys_and_objects(keys: &[&T], mut vals: Vec>) -> Id + where + T: ClassType + Foundation::NSCopying, + T::Mutability: Copyhelper, + { + let count = min(keys.len(), vals.len()); - let keys: *mut NonNull = util::ref_ptr_cast_const(keys.as_ptr()); - let keys: *mut NonNull = keys.cast(); - let vals = util::id_ptr_cast(vals.as_mut_ptr()); + let keys: *mut NonNull = util::ref_ptr_cast_const(keys.as_ptr()); + let keys: *mut NonNull = keys.cast(); + let vals = util::id_ptr_cast(vals.as_mut_ptr()); - unsafe { Self::initWithObjects_forKeys_count(Self::alloc(), vals, keys, count) } - } + unsafe { Self::initWithObjects_forKeys_count(Self::alloc(), vals, keys, count) } + } +} + +#[cfg(feature = "Foundation_NSMutableDictionary")] +impl NSMutableDictionary { + pub fn from_keys_and_objects(keys: &[&T], mut vals: Vec>) -> Id + where + T: ClassType + Foundation::NSCopying, + T::Mutability: Copyhelper, + { + let count = min(keys.len(), vals.len()); + + let keys: *mut NonNull = util::ref_ptr_cast_const(keys.as_ptr()); + let keys: *mut NonNull = keys.cast(); + let vals = util::id_ptr_cast(vals.as_mut_ptr()); + unsafe { Self::initWithObjects_forKeys_count(Self::alloc(), vals, keys, count) } + } +} + +extern_methods!( + unsafe impl NSDictionary { pub fn len(&self) -> usize { self.count() } @@ -83,137 +104,222 @@ extern_methods!( pub fn get_mut(&mut self, key: &K) -> Option<&mut V> where V: IsMutable; + } +); - #[doc(alias = "getObjects:andKeys:")] - pub fn keys(&self) -> Vec<&K> { - let len = self.len(); - let mut keys: Vec> = Vec::with_capacity(len); - unsafe { - #[allow(deprecated)] - self.getObjects_andKeys(ptr::null_mut(), keys.as_mut_ptr()); - keys.set_len(len); - mem::transmute(keys) - } +impl NSDictionary { + #[doc(alias = "getObjects:andKeys:")] + pub fn keys(&self) -> Vec<&K> { + let len = self.len(); + let mut keys: Vec> = Vec::with_capacity(len); + unsafe { + #[allow(deprecated)] + self.getObjects_andKeys(ptr::null_mut(), keys.as_mut_ptr()); + keys.set_len(len); + mem::transmute(keys) } + } - // We don't provide `keys_mut`, since keys are expected to be - // immutable. + // We don't provide `keys_mut`, since keys are expected to be + // immutable. - #[doc(alias = "getObjects:andKeys:")] - pub fn keys_retained(&self) -> Vec> - where - K: IsIdCloneable, - { - // SAFETY: The keys are stored in the array - self.keys() - .into_iter() - .map(|obj| unsafe { util::collection_retain_id(obj) }) - .collect() - } + #[doc(alias = "getObjects:andKeys:")] + pub fn keys_retained(&self) -> Vec> + where + K: IsIdCloneable, + { + // SAFETY: The keys are stored in the array + self.keys() + .into_iter() + .map(|obj| unsafe { util::collection_retain_id(obj) }) + .collect() + } - #[doc(alias = "getObjects:andKeys:")] - pub fn values(&self) -> Vec<&V> { - let len = self.len(); - let mut vals: Vec> = Vec::with_capacity(len); - unsafe { - #[allow(deprecated)] - self.getObjects_andKeys(vals.as_mut_ptr(), ptr::null_mut()); - vals.set_len(len); - mem::transmute(vals) - } + #[doc(alias = "getObjects:andKeys:")] + pub fn values(&self) -> Vec<&V> { + let len = self.len(); + let mut vals: Vec> = Vec::with_capacity(len); + unsafe { + #[allow(deprecated)] + self.getObjects_andKeys(vals.as_mut_ptr(), ptr::null_mut()); + vals.set_len(len); + mem::transmute(vals) } + } - #[doc(alias = "getObjects:andKeys:")] - pub fn values_retained(&self) -> Vec> - where - V: IsIdCloneable, - { - // SAFETY: The values are stored in the array - self.values() - .into_iter() - .map(|obj| unsafe { util::collection_retain_id(obj) }) - .collect() - } + #[doc(alias = "getObjects:andKeys:")] + pub fn values_retained(&self) -> Vec> + where + V: IsIdCloneable, + { + // SAFETY: The values are stored in the array + self.values() + .into_iter() + .map(|obj| unsafe { util::collection_retain_id(obj) }) + .collect() + } - /// Returns a vector of mutable references to the values in the dictionary. - /// - /// # Examples - /// - #[cfg_attr( - all( - feature = "Foundation_NSMutableDictionary", - feature = "Foundation_NSMutableString" - ), - doc = "```" - )] - #[cfg_attr( - not(all( - feature = "Foundation_NSMutableDictionary", - feature = "Foundation_NSMutableString" - )), - doc = "```ignore" - )] - /// use icrate::Foundation::{NSMutableDictionary, NSMutableString, NSString}; - /// - /// let mut dict = NSMutableDictionary::new(); - /// dict.insert(NSString::from_str("one"), NSMutableString::from_str("two")); - /// for val in dict.values_mut() { - /// println!("{:?}", val); - /// } - /// ``` - #[doc(alias = "getObjects:andKeys:")] - pub fn values_mut(&mut self) -> Vec<&mut V> - where - V: IsMutable, - { - let len = self.len(); - let mut vals: Vec> = Vec::with_capacity(len); - unsafe { - #[allow(deprecated)] - self.getObjects_andKeys(vals.as_mut_ptr(), ptr::null_mut()); - vals.set_len(len); - mem::transmute(vals) - } + /// Returns a vector of mutable references to the values in the dictionary. + /// + /// # Examples + /// + #[cfg_attr( + all( + feature = "Foundation_NSMutableDictionary", + feature = "Foundation_NSMutableString" + ), + doc = "```" + )] + #[cfg_attr( + not(all( + feature = "Foundation_NSMutableDictionary", + feature = "Foundation_NSMutableString" + )), + doc = "```ignore" + )] + /// use icrate::Foundation::{NSMutableDictionary, NSMutableString, NSString}; + /// + /// let mut dict = NSMutableDictionary::new(); + /// dict.insert(NSString::from_str("one"), NSMutableString::from_str("two")); + /// for val in dict.values_mut() { + /// println!("{:?}", val); + /// } + /// ``` + #[doc(alias = "getObjects:andKeys:")] + pub fn values_mut(&mut self) -> Vec<&mut V> + where + V: IsMutable, + { + let len = self.len(); + let mut vals: Vec> = Vec::with_capacity(len); + unsafe { + #[allow(deprecated)] + self.getObjects_andKeys(vals.as_mut_ptr(), ptr::null_mut()); + vals.set_len(len); + mem::transmute(vals) } + } - #[doc(alias = "getObjects:andKeys:")] - pub fn keys_and_objects(&self) -> (Vec<&K>, Vec<&V>) { - let len = self.len(); - let mut keys: Vec> = Vec::with_capacity(len); - let mut objs: Vec> = Vec::with_capacity(len); - unsafe { - #[allow(deprecated)] - self.getObjects_andKeys(objs.as_mut_ptr(), keys.as_mut_ptr()); - keys.set_len(len); - objs.set_len(len); - (mem::transmute(keys), mem::transmute(objs)) - } + #[doc(alias = "getObjects:andKeys:")] + pub fn keys_and_objects(&self) -> (Vec<&K>, Vec<&V>) { + let len = self.len(); + let mut keys: Vec> = Vec::with_capacity(len); + let mut objs: Vec> = Vec::with_capacity(len); + unsafe { + #[allow(deprecated)] + self.getObjects_andKeys(objs.as_mut_ptr(), keys.as_mut_ptr()); + keys.set_len(len); + objs.set_len(len); + (mem::transmute(keys), mem::transmute(objs)) } + } - #[doc(alias = "keyEnumerator")] - #[cfg(feature = "Foundation_NSEnumerator")] - pub fn iter_keys(&self) -> Foundation::NSEnumerator2<'_, K> { - unsafe { - let result = msg_send![self, keyEnumerator]; - Foundation::NSEnumerator2::from_ptr(result) - } + #[doc(alias = "keyEnumerator")] + #[cfg(feature = "Foundation_NSEnumerator")] + pub fn iter_keys(&self) -> Foundation::NSEnumerator2<'_, K> { + unsafe { + let result = msg_send![self, keyEnumerator]; + Foundation::NSEnumerator2::from_ptr(result) } + } - #[doc(alias = "objectEnumerator")] - #[cfg(feature = "Foundation_NSEnumerator")] - pub fn iter_values(&self) -> Foundation::NSEnumerator2<'_, V> { - unsafe { - let result = msg_send![self, objectEnumerator]; - Foundation::NSEnumerator2::from_ptr(result) - } + #[doc(alias = "objectEnumerator")] + #[cfg(feature = "Foundation_NSEnumerator")] + pub fn iter_values(&self) -> Foundation::NSEnumerator2<'_, V> { + unsafe { + let result = msg_send![self, objectEnumerator]; + Foundation::NSEnumerator2::from_ptr(result) } } -); +} + +#[cfg(feature = "Foundation_NSMutableDictionary")] +impl NSMutableDictionary { + /// Inserts a key-value pair into the dictionary. + /// + /// If the dictionary did not have this key present, None is returned. + /// If the dictionary did have this key present, the value is updated, + /// and the old value is returned. + /// + /// # Examples + /// + /// ``` + /// use icrate::Foundation::{NSMutableDictionary, NSObject, NSString}; + /// + /// let mut dict = NSMutableDictionary::new(); + /// dict.insert(NSString::from_str("one"), NSObject::new()); + /// ``` + #[doc(alias = "setObject:forKey:")] + pub fn insert(&mut self, key: Id, value: Id) -> Option> { + // SAFETY: We remove the object from the dictionary below + let old_obj = self + .get(&key) + .map(|old_obj| unsafe { util::mutable_collection_retain_removed_id(old_obj) }); + + // SAFETY: It is always safe to transmute an `Id` to `Object`. + let key: Id = unsafe { Id::cast(key) }; + // SAFETY: We have ownership over both the key and the value. + unsafe { self.setObject_forKey(&value, &key) }; + old_obj + } + + /// Removes a key from the dictionary, returning the value at the key + /// if the key was previously in the dictionary. + /// + /// # Examples + /// + /// ``` + /// use icrate::Foundation::{NSMutableDictionary, NSObject, NSString}; + /// use icrate::ns_string; + /// + /// let mut dict = NSMutableDictionary::new(); + /// dict.insert(NSString::from_str("one"), NSObject::new()); + /// dict.remove(ns_string!("one")); + /// assert!(dict.is_empty()); + /// ``` + #[doc(alias = "removeObjectForKey:")] + pub fn remove(&mut self, key: &K) -> Option> { + // SAFETY: We remove the object from the dictionary below + let old_obj = self + .get(key) + .map(|old_obj| unsafe { util::mutable_collection_retain_removed_id(old_obj) }); + self.removeObjectForKey(key); + old_obj + } + + /// Returns an [`NSArray`] containing the dictionary's values, + /// consuming the dictionary. + /// + /// [`NSArray`]: crate::Foundation::NSArray + /// + /// + /// # Examples + /// + /// ``` + /// use icrate::Foundation::{NSMutableDictionary, NSObject, NSString}; + /// + /// let mut dict = NSMutableDictionary::new(); + /// dict.insert(NSString::from_str("one"), NSObject::new()); + /// let array = NSMutableDictionary::into_values_array(dict); + /// println!("{:?}", array); + /// ``` + #[cfg(feature = "Foundation_NSArray")] + pub fn into_values_array(this: Id) -> Id> { + // SAFETY: We've consumed the dictionary, so getting an array from + // it is safe. + unsafe { this.allValues() } + } +} unsafe impl Foundation::NSFastEnumeration2 for NSDictionary { type Item = K; } +#[cfg(feature = "Foundation_NSMutableDictionary")] +unsafe impl Foundation::NSFastEnumeration2 for NSMutableDictionary { + type Item = K; +} + impl<'a, K: Message, V: Message> Index<&'a K> for NSDictionary { type Output = V; @@ -222,6 +328,28 @@ impl<'a, K: Message, V: Message> Index<&'a K> for NSDictionary { } } +#[cfg(feature = "Foundation_NSMutableDictionary")] +impl<'a, K: Message, V: Message> Index<&'a K> for NSMutableDictionary { + type Output = V; + + fn index<'s>(&'s self, index: &'a K) -> &'s V { + self.get(index).unwrap() + } +} + +impl<'a, K: Message, V: IsMutable> IndexMut<&'a K> for NSDictionary { + fn index_mut<'s>(&'s mut self, index: &'a K) -> &'s mut V { + self.get_mut(index).unwrap() + } +} + +#[cfg(feature = "Foundation_NSMutableDictionary")] +impl<'a, K: Message, V: IsMutable> IndexMut<&'a K> for NSMutableDictionary { + fn index_mut<'s>(&'s mut self, index: &'a K) -> &'s mut V { + self.get_mut(index).unwrap() + } +} + #[cfg(feature = "Foundation_NSEnumerator")] impl fmt::Debug for NSDictionary { #[inline] diff --git a/crates/icrate/src/Foundation/additions/mod.rs b/crates/icrate/src/Foundation/additions/mod.rs index b4f5b875c..b93841732 100644 --- a/crates/icrate/src/Foundation/additions/mod.rs +++ b/crates/icrate/src/Foundation/additions/mod.rs @@ -21,12 +21,6 @@ mod enumerator; mod error; mod exception; mod geometry; -mod mutable_array; -mod mutable_attributed_string; -mod mutable_data; -mod mutable_dictionary; -mod mutable_set; -mod mutable_string; mod number; mod process_info; mod range; diff --git a/crates/icrate/src/Foundation/additions/mutable_array.rs b/crates/icrate/src/Foundation/additions/mutable_array.rs deleted file mode 100644 index 21981b521..000000000 --- a/crates/icrate/src/Foundation/additions/mutable_array.rs +++ /dev/null @@ -1,176 +0,0 @@ -#![cfg(feature = "Foundation_NSMutableArray")] -use alloc::vec::Vec; -use core::cmp::Ordering; -use core::ops::{Index, IndexMut}; - -use objc2::mutability::{IsMutable, IsRetainable}; - -use super::util; -use crate::common::*; -use crate::Foundation::{self, NSArray, NSComparisonResult, NSInteger, NSMutableArray}; - -/// Creation methods. -impl NSMutableArray { - pub fn from_vec(mut vec: Vec>) -> Id { - let len = vec.len(); - let ptr = util::id_ptr_cast(vec.as_mut_ptr()); - // SAFETY: Same as `NSArray::from_vec`. - unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } - } - - pub fn from_id_slice(slice: &[Id]) -> Id - where - T: IsIdCloneable, - { - let len = slice.len(); - let ptr = util::id_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]) -> Id - where - T: 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) } - } -} - -/// Accessor methods. -impl NSMutableArray { - #[doc(alias = "addObject:")] - pub fn push(&mut self, obj: Id) { - // SAFETY: We've consumed ownership of the object. - unsafe { self.addObject(&obj) } - } - - #[doc(alias = "insertObject:atIndex:")] - pub fn insert(&mut self, index: usize, obj: Id) { - // TODO: Replace this check with catching the thrown NSRangeException - let len = self.len(); - if index < len { - // SAFETY: We've consumed ownership of the object, and the - // index is checked to be in bounds. - unsafe { self.insertObject_atIndex(&obj, index) } - } else { - panic!( - "insertion index (is {}) should be <= len (is {})", - index, len - ); - } - } - - #[doc(alias = "replaceObjectAtIndex:withObject:")] - pub fn replace(&mut self, index: usize, obj: Id) -> Result, Id> { - if let Some(old_obj) = self.get(index) { - // SAFETY: We remove the object from the array below. - let old_obj = unsafe { util::mutable_collection_retain_removed_id(old_obj) }; - // SAFETY: The index is checked to be in bounds, and we've - // consumed ownership of the new object. - unsafe { self.replaceObjectAtIndex_withObject(index, &obj) }; - Ok(old_obj) - } else { - Err(obj) - } - } - - #[doc(alias = "removeObjectAtIndex:")] - pub fn remove(&mut self, index: usize) -> Option> { - let obj = self.get(index)?; - // SAFETY: We remove the object from the array below. - let obj = unsafe { util::mutable_collection_retain_removed_id(obj) }; - // SAFETY: The index is checked to be in bounds. - unsafe { self.removeObjectAtIndex(index) }; - Some(obj) - } - - #[doc(alias = "removeLastObject")] - pub fn pop(&mut self) -> Option> { - let obj = self.last()?; - // SAFETY: We remove the object from the array below. - let obj = unsafe { util::mutable_collection_retain_removed_id(obj) }; - // SAFETY: Just checked that there is an object. - unsafe { self.removeLastObject() }; - Some(obj) - } - - #[doc(alias = "sortUsingFunction:context:")] - pub fn sort_by Ordering>(&mut self, compare: F) { - // TODO: "C-unwind" - unsafe extern "C" fn compare_with_closure Ordering>( - obj1: NonNull, - obj2: NonNull, - context: *mut c_void, - ) -> NSInteger { - let context: *mut F = context.cast(); - // Bring back a reference to the closure. - // Guaranteed to be unique, we gave `sortUsingFunction` unique is - // ownership, and that method only runs one function at a time. - let closure: &mut F = unsafe { context.as_mut().unwrap_unchecked() }; - - // SAFETY: The objects are guaranteed to be valid - let (obj1, obj2) = unsafe { (obj1.as_ref(), obj2.as_ref()) }; - - NSComparisonResult::from((*closure)(obj1, obj2)) as _ - } - - // Create function pointer - let f: unsafe extern "C" fn(_, _, _) -> _ = compare_with_closure::; - - // Grab a type-erased pointer to the closure (a pointer to stack). - let mut closure = compare; - let context: *mut F = &mut closure; - - unsafe { self.sortUsingFunction_context(f, context.cast()) }; - // Keep the closure alive until the function has run. - drop(closure); - } - - pub fn into_vec(array: Id) -> Vec> { - // SAFETY: We've consumed the array, so taking ownership of the - // returned values is safe. - array - .to_vec() - .into_iter() - .map(|obj| unsafe { util::mutable_collection_retain_removed_id(obj) }) - .collect() - } -} - -unsafe impl Foundation::NSFastEnumeration2 for NSMutableArray { - type Item = T; -} - -impl<'a, T: Message> IntoIterator for &'a NSMutableArray { - type Item = &'a T; - type IntoIter = Foundation::NSFastEnumerator2<'a, NSMutableArray>; - - fn into_iter(self) -> Self::IntoIter { - use Foundation::NSFastEnumeration2; - self.iter_fast() - } -} - -impl Extend> for NSMutableArray { - fn extend>>(&mut self, iter: I) { - let iterator = iter.into_iter(); - iterator.for_each(move |item| self.push(item)); - } -} - -impl Index for NSMutableArray { - type Output = T; - - fn index(&self, index: usize) -> &T { - self.get(index).unwrap() - } -} - -impl IndexMut for NSMutableArray { - fn index_mut(&mut self, index: usize) -> &mut T { - self.get_mut(index).unwrap() - } -} diff --git a/crates/icrate/src/Foundation/additions/mutable_attributed_string.rs b/crates/icrate/src/Foundation/additions/mutable_attributed_string.rs deleted file mode 100644 index cc09ae042..000000000 --- a/crates/icrate/src/Foundation/additions/mutable_attributed_string.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![cfg(feature = "Foundation_NSMutableAttributedString")] -use crate::common::*; -use crate::Foundation::{self, NSAttributedString, NSMutableAttributedString}; - -/// Creating mutable attributed strings. -impl NSMutableAttributedString { - // TODO: new_with_attributes - - #[doc(alias = "initWithString:")] - #[cfg(feature = "Foundation_NSString")] - pub fn from_nsstring(string: &Foundation::NSString) -> Id { - Self::initWithString(Self::alloc(), string) - } - - #[doc(alias = "initWithAttributedString:")] - pub fn from_attributed_nsstring(attributed_string: &NSAttributedString) -> Id { - Self::initWithAttributedString(Self::alloc(), attributed_string) - } -} diff --git a/crates/icrate/src/Foundation/additions/mutable_data.rs b/crates/icrate/src/Foundation/additions/mutable_data.rs deleted file mode 100644 index ff8c28809..000000000 --- a/crates/icrate/src/Foundation/additions/mutable_data.rs +++ /dev/null @@ -1,153 +0,0 @@ -#![cfg(feature = "Foundation_NSMutableData")] -#[cfg(feature = "block")] -use alloc::vec::Vec; -use core::ops::{Index, IndexMut, Range}; -use core::slice::{self, SliceIndex}; -use std::io; - -use crate::common::*; -use crate::Foundation::{NSMutableData, NSRange}; - -/// Creation methods -impl NSMutableData { - pub fn with_bytes(bytes: &[u8]) -> Id { - let bytes_ptr = bytes.as_ptr() as *mut c_void; - unsafe { Self::initWithBytes_length(Self::alloc(), bytes_ptr, bytes.len()) } - } - - #[cfg(feature = "block")] - pub fn from_vec(bytes: Vec) -> Id { - unsafe { Id::cast(super::data::with_vec(Self::class(), bytes)) } - } -} - -/// Mutation methods -impl NSMutableData { - #[doc(alias = "mutableBytes")] - pub fn bytes_mut(&mut self) -> &mut [u8] { - if let Some(ptr) = self.mutableBytes() { - let ptr: *mut u8 = ptr.as_ptr().cast(); - unsafe { slice::from_raw_parts_mut(ptr, self.len()) } - } else { - // The bytes pointer may be null for length zero on GNUStep - &mut [] - } - } - - #[doc(alias = "appendBytes:length:")] - pub fn extend_from_slice(&mut self, bytes: &[u8]) { - let bytes_ptr: NonNull = NonNull::new(bytes.as_ptr() as *mut u8).unwrap().cast(); - unsafe { self.appendBytes_length(bytes_ptr, bytes.len()) } - } - - pub fn push(&mut self, byte: u8) { - self.extend_from_slice(&[byte]) - } - - #[doc(alias = "replaceBytesInRange:withBytes:length:")] - pub fn replace_range(&mut self, range: Range, bytes: &[u8]) { - let range = NSRange::from(range); - // No need to verify the length of the range here, - // `replaceBytesInRange:` just zero-fills if out of bounds. - let ptr = bytes.as_ptr() as *mut c_void; - unsafe { self.replaceBytesInRange_withBytes_length(range, ptr, bytes.len()) } - } - - pub fn set_bytes(&mut self, bytes: &[u8]) { - let len = self.len(); - self.replace_range(0..len, bytes); - } -} - -impl AsRef<[u8]> for NSMutableData { - fn as_ref(&self) -> &[u8] { - self.bytes() - } -} - -impl AsMut<[u8]> for NSMutableData { - fn as_mut(&mut self) -> &mut [u8] { - self.bytes_mut() - } -} - -impl> Index for NSMutableData { - type Output = I::Output; - - #[inline] - fn index(&self, index: I) -> &Self::Output { - Index::index(self.bytes(), index) - } -} - -impl> IndexMut for NSMutableData { - #[inline] - fn index_mut(&mut self, index: I) -> &mut Self::Output { - IndexMut::index_mut(self.bytes_mut(), index) - } -} - -// impl FromIterator for Id { -// fn from_iter>(iter: T) -> Self { -// let iter = iter.into_iter(); -// let (lower, _) = iter.size_hint(); -// let data = Self::with_capacity(lower); -// for item in iter { -// data.push(item); -// } -// data -// } -// } - -impl Extend for NSMutableData { - /// You should use [`extend_from_slice`] whenever possible, it is more - /// performant. - /// - /// [`extend_from_slice`]: Self::extend_from_slice - fn extend>(&mut self, iter: T) { - let iterator = iter.into_iter(); - iterator.for_each(move |item| self.push(item)); - } -} - -// Vec also has this impl -impl<'a> Extend<&'a u8> for NSMutableData { - fn extend>(&mut self, iter: T) { - let iterator = iter.into_iter(); - iterator.for_each(move |item| self.push(*item)); - } -} - -impl io::Write for NSMutableData { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.extend_from_slice(buf); - Ok(buf.len()) - } - - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - self.extend_from_slice(buf); - Ok(()) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl<'a> IntoIterator for &'a NSMutableData { - type Item = &'a u8; - type IntoIter = core::slice::Iter<'a, u8>; - - fn into_iter(self) -> Self::IntoIter { - self.bytes().iter() - } -} - -impl<'a> IntoIterator for &'a mut NSMutableData { - type Item = &'a mut u8; - type IntoIter = core::slice::IterMut<'a, u8>; - - fn into_iter(self) -> Self::IntoIter { - self.bytes_mut().iter_mut() - } -} diff --git a/crates/icrate/src/Foundation/additions/mutable_dictionary.rs b/crates/icrate/src/Foundation/additions/mutable_dictionary.rs deleted file mode 100644 index e7084c8a7..000000000 --- a/crates/icrate/src/Foundation/additions/mutable_dictionary.rs +++ /dev/null @@ -1,139 +0,0 @@ -#![cfg(feature = "Foundation_NSMutableDictionary")] -use alloc::vec::Vec; -use core::cmp::min; -use core::mem; -use core::ops::{Index, IndexMut}; -use core::ptr; - -use objc2::mutability::{IsMutable, IsRetainable}; -use objc2::runtime::Object; - -use super::util; -use crate::common::*; -use crate::Foundation::{self, Copyhelper, NSDictionary, NSMutableDictionary}; - -impl NSMutableDictionary { - /// Creates an [`NSMutableDictionary`] from a slice of keys and a - /// vector of values. - /// - /// # Examples - /// - /// ``` - /// use icrate::Foundation::{NSMutableDictionary, NSNumber, NSObject}; - /// - /// let dict = NSMutableDictionary::from_keys_and_objects( - /// &[ - /// &*NSNumber::new_i32(1), - /// &*NSNumber::new_i32(2), - /// &*NSNumber::new_i32(3), - /// ], - /// vec![NSObject::new(), NSObject::new(), NSObject::new()], - /// ); - /// ``` - pub fn from_keys_and_objects(keys: &[&T], mut vals: Vec>) -> Id - where - T: ClassType + Foundation::NSCopying, - T::Mutability: Copyhelper, - { - let count = min(keys.len(), vals.len()); - - let keys: *mut NonNull = util::ref_ptr_cast_const(keys.as_ptr()); - let keys: *mut NonNull = keys.cast(); - let vals = util::id_ptr_cast(vals.as_mut_ptr()); - - unsafe { Self::initWithObjects_forKeys_count(Self::alloc(), vals, keys, count) } - } - - /// Inserts a key-value pair into the dictionary. - /// - /// If the dictionary did not have this key present, None is returned. - /// If the dictionary did have this key present, the value is updated, - /// and the old value is returned. - /// - /// # Examples - /// - /// ``` - /// use icrate::Foundation::{NSMutableDictionary, NSObject, NSString}; - /// - /// let mut dict = NSMutableDictionary::new(); - /// dict.insert(NSString::from_str("one"), NSObject::new()); - /// ``` - #[doc(alias = "setObject:forKey:")] - pub fn insert(&mut self, key: Id, value: Id) -> Option> { - // SAFETY: We remove the object from the dictionary below - let old_obj = self - .get(&key) - .map(|old_obj| unsafe { util::mutable_collection_retain_removed_id(old_obj) }); - - // SAFETY: It is always safe to transmute an `Id` to `Object`. - let key: Id = unsafe { Id::cast(key) }; - // SAFETY: We have ownership over both the key and the value. - unsafe { self.setObject_forKey(&value, &key) }; - old_obj - } - - /// Removes a key from the dictionary, returning the value at the key - /// if the key was previously in the dictionary. - /// - /// # Examples - /// - /// ``` - /// use icrate::Foundation::{NSMutableDictionary, NSObject, NSString}; - /// use icrate::ns_string; - /// - /// let mut dict = NSMutableDictionary::new(); - /// dict.insert(NSString::from_str("one"), NSObject::new()); - /// dict.remove(ns_string!("one")); - /// assert!(dict.is_empty()); - /// ``` - #[doc(alias = "removeObjectForKey:")] - pub fn remove(&mut self, key: &K) -> Option> { - // SAFETY: We remove the object from the dictionary below - let old_obj = self - .get(key) - .map(|old_obj| unsafe { util::mutable_collection_retain_removed_id(old_obj) }); - self.removeObjectForKey(key); - old_obj - } - - /// Returns an [`NSArray`] containing the dictionary's values, - /// consuming the dictionary. - /// - /// [`NSArray`]: crate::Foundation::NSArray - /// - /// - /// # Examples - /// - /// ``` - /// use icrate::Foundation::{NSMutableDictionary, NSObject, NSString}; - /// - /// let mut dict = NSMutableDictionary::new(); - /// dict.insert(NSString::from_str("one"), NSObject::new()); - /// let array = NSMutableDictionary::into_values_array(dict); - /// println!("{:?}", array); - /// ``` - #[cfg(feature = "Foundation_NSArray")] - pub fn into_values_array(this: Id) -> Id> { - // SAFETY: We've consumed the dictionary, so getting an array from - // it is safe. - unsafe { this.allValues() } - } -} - -unsafe impl Foundation::NSFastEnumeration2 for NSMutableDictionary { - type Item = K; -} - -impl<'a, K: Message, V: Message> Index<&'a K> for NSMutableDictionary { - type Output = V; - - fn index<'s>(&'s self, index: &'a K) -> &'s V { - self.get(index).unwrap() - } -} - -impl<'a, K: Message, V: IsMutable> IndexMut<&'a K> for NSMutableDictionary { - fn index_mut<'s>(&'s mut self, index: &'a K) -> &'s mut V { - self.get_mut(index).unwrap() - } -} diff --git a/crates/icrate/src/Foundation/additions/mutable_set.rs b/crates/icrate/src/Foundation/additions/mutable_set.rs deleted file mode 100644 index ee5a37cc6..000000000 --- a/crates/icrate/src/Foundation/additions/mutable_set.rs +++ /dev/null @@ -1,151 +0,0 @@ -#![cfg(feature = "Foundation_NSMutableSet")] -use alloc::vec::Vec; - -use objc2::mutability::IsRetainable; - -use super::util; -use crate::common::*; -use crate::Foundation::{self, NSMutableSet}; - -impl NSMutableSet { - /// Creates an [`NSMutableSet`] from a vector. - /// - /// # Examples - /// - /// ``` - /// use icrate::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>) -> Id { - let len = vec.len(); - let ptr = util::id_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 `Id`s. - /// - /// # Examples - /// - /// ``` - /// use icrate::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: &[Id]) -> Id - where - T: IsIdCloneable, - { - let len = slice.len(); - let ptr = util::id_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]) -> Id - where - T: 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 icrate::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); - /// ``` - pub fn into_vec(set: Id) -> Vec> { - // SAFETY: Same as `NSMutableArray::into_vec` - set.into_iter() - .map(|obj| unsafe { util::mutable_collection_retain_removed_id(obj) }) - .collect() - } -} - -// We're explicit about `T` being `PartialEq` for these methods because the -// set compares the input value with elements in the set -// For comparison: Rust's HashSet requires similar methods to be `Hash` + `Eq` -impl NSMutableSet { - /// Adds a value to the set. Returns whether the value was - /// newly inserted. - /// - /// # Examples - /// - /// ``` - /// use icrate::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(&mut self, value: Id) -> bool { - 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 icrate::Foundation::{NSMutableSet, NSString}; - /// use icrate::ns_string; - /// - /// let mut set = NSMutableSet::new(); - /// - /// set.insert(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 { - let contains_value = self.contains(value); - unsafe { self.removeObject(value) }; - contains_value - } -} - -unsafe impl Foundation::NSFastEnumeration2 for NSMutableSet { - type Item = T; -} - -impl<'a, T: Message> IntoIterator for &'a NSMutableSet { - type Item = &'a T; - type IntoIter = Foundation::NSFastEnumerator2<'a, NSMutableSet>; - - fn into_iter(self) -> Self::IntoIter { - use Foundation::NSFastEnumeration2; - self.iter_fast() - } -} - -impl Extend> for NSMutableSet { - fn extend>>(&mut self, iter: I) { - for item in iter { - self.insert(item); - } - } -} diff --git a/crates/icrate/src/Foundation/additions/mutable_string.rs b/crates/icrate/src/Foundation/additions/mutable_string.rs deleted file mode 100644 index 67d616309..000000000 --- a/crates/icrate/src/Foundation/additions/mutable_string.rs +++ /dev/null @@ -1,85 +0,0 @@ -#![cfg(feature = "Foundation_NSMutableString")] -use core::cmp; -use core::fmt; -use core::ops::AddAssign; -use core::str; - -use crate::common::*; -use crate::Foundation::{NSMutableString, NSString}; - -/// Creating mutable strings. -impl NSMutableString { - /// Creates a new [`NSMutableString`] by copying the given string slice. - #[doc(alias = "initWithBytes:length:encoding:")] - #[allow(clippy::should_implement_trait)] // Not really sure of a better name - pub fn from_str(string: &str) -> Id { - unsafe { - let obj = super::string::from_str(Self::class(), string); - Id::new(obj.cast()).unwrap() - } - } -} - -impl AddAssign<&NSString> for NSMutableString { - #[inline] - fn add_assign(&mut self, other: &NSString) { - self.appendString(other) - } -} - -impl PartialEq for NSMutableString { - #[inline] - fn eq(&self, other: &NSString) -> bool { - PartialEq::eq(&**self, other) - } -} - -impl PartialEq for NSString { - #[inline] - fn eq(&self, other: &NSMutableString) -> bool { - PartialEq::eq(self, &**other) - } -} - -impl PartialOrd for NSMutableString { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - PartialOrd::partial_cmp(&**self, &**other) - } -} - -impl PartialOrd for NSMutableString { - #[inline] - fn partial_cmp(&self, other: &NSString) -> Option { - PartialOrd::partial_cmp(&**self, other) - } -} - -impl PartialOrd for NSString { - #[inline] - fn partial_cmp(&self, other: &NSMutableString) -> Option { - PartialOrd::partial_cmp(self, &**other) - } -} - -impl Ord for NSMutableString { - #[inline] - fn cmp(&self, other: &Self) -> cmp::Ordering { - Ord::cmp(&**self, &**other) - } -} - -impl fmt::Write for NSMutableString { - fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { - let nsstring = NSString::from_str(s); - self.appendString(&nsstring); - Ok(()) - } -} - -impl fmt::Display for NSMutableString { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&**self, f) - } -} diff --git a/crates/icrate/src/Foundation/additions/set.rs b/crates/icrate/src/Foundation/additions/set.rs index 248584802..101150094 100644 --- a/crates/icrate/src/Foundation/additions/set.rs +++ b/crates/icrate/src/Foundation/additions/set.rs @@ -8,57 +8,195 @@ use objc2::mutability::IsRetainable; use super::util; use crate::common::*; +#[cfg(feature = "Foundation_NSMutableSet")] +use crate::Foundation::NSMutableSet; use crate::Foundation::{self, NSSet}; -extern_methods!( - unsafe impl NSSet { - /// Creates an [`NSSet`] from a vector. - /// - /// # Examples - /// - /// ``` - /// use icrate::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>) -> Id { - let len = vec.len(); - let ptr = util::id_ptr_cast(vec.as_mut_ptr()); - // SAFETY: Same as `NSArray::from_vec`. - unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } - } +impl NSSet { + /// Creates an [`NSSet`] from a vector. + /// + /// # Examples + /// + /// ``` + /// use icrate::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>) -> Id { + let len = vec.len(); + let ptr = util::id_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 `Id`s. - /// - /// # Examples - /// - /// ``` - /// use icrate::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: &[Id]) -> Id - where - T: IsIdCloneable, - { - let len = slice.len(); - let ptr = util::id_ptr_cast_const(slice.as_ptr()); - // SAFETY: Same as `NSArray::from_id_slice` - unsafe { Self::initWithObjects_count(Self::alloc(), ptr, len) } - } + /// Creates an [`NSSet`] from a slice of `Id`s. + /// + /// # Examples + /// + /// ``` + /// use icrate::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: &[Id]) -> Id + where + T: IsIdCloneable, + { + let len = slice.len(); + let ptr = util::id_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]) -> Id - where - T: 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) } - } + pub fn from_slice(slice: &[&T]) -> Id + where + T: 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 icrate::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); + /// ``` + pub fn to_vec(&self) -> Vec<&T> { + self.into_iter().collect() + } + + #[doc(alias = "getObjects:range:")] + pub fn to_vec_retained(&self) -> Vec> + where + T: IsIdCloneable, + { + // SAFETY: The objects are stored in the set + self.into_iter() + .map(|obj| unsafe { util::collection_retain_id(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 icrate::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 = "Foundation_NSArray")] + pub fn to_array(&self) -> Id> + where + T: IsIdCloneable, + { + // SAFETY: The `T: IsIdCloneable` bound ensures that it is safe to + // create what is effectively a copy from an `&self` reference. + // + // Could be implemented as: + // NSArray::from_vec(self.to_vec_retained()) + unsafe { self.allObjects() } + } +} + +#[cfg(feature = "Foundation_NSMutableSet")] +impl NSMutableSet { + /// Creates an [`NSMutableSet`] from a vector. + /// + /// # Examples + /// + /// ``` + /// use icrate::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>) -> Id { + let len = vec.len(); + let ptr = util::id_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 `Id`s. + /// + /// # Examples + /// + /// ``` + /// use icrate::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: &[Id]) -> Id + where + T: IsIdCloneable, + { + let len = slice.len(); + let ptr = util::id_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]) -> Id + where + T: 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 icrate::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); + /// ``` + pub fn into_vec(set: Id) -> Vec> { + // SAFETY: Same as `NSMutableArray::into_vec` + set.into_iter() + .map(|obj| unsafe { util::mutable_collection_retain_removed_id(obj) }) + .collect() + } +} +extern_methods!( + unsafe impl NSSet { /// Returns the number of elements in the set. /// /// # Examples @@ -127,72 +265,11 @@ extern_methods!( Foundation::NSEnumerator2::from_ptr(result) } } - - /// Returns a [`Vec`] containing the set's elements. - /// - /// # Examples - /// - /// ``` - /// use icrate::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); - /// ``` - pub fn to_vec(&self) -> Vec<&T> { - self.into_iter().collect() - } - - #[doc(alias = "getObjects:range:")] - pub fn to_vec_retained(&self) -> Vec> - where - T: IsIdCloneable, - { - // SAFETY: The objects are stored in the set - self.into_iter() - .map(|obj| unsafe { util::collection_retain_id(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 icrate::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 = "Foundation_NSArray")] - pub fn to_array(&self) -> Id> - where - T: IsIdCloneable, - { - // SAFETY: The `T: IsIdCloneable` bound ensures that it is safe to - // create what is effectively a copy from an `&self` reference. - // - // Could be implemented as: - // NSArray::from_vec(self.to_vec_retained()) - unsafe { self.allObjects() } - } } - // We're explicit about `T` being `PartialEq` for these methods because the - // set compares the input value(s) with elements in the set - // For comparison: Rust's HashSet requires similar methods to be `Hash` + `Eq` + // We're explicit about `T` being `PartialEq` for these methods because + // the set compares the input value(s) with elements in the set. For + // comparison: Rust's HashSet requires similar methods to be `Hash` + `Eq` unsafe impl NSSet { /// Returns `true` if the set contains a value. /// @@ -256,8 +333,9 @@ extern_methods!( /// assert!(!set2.is_subset(&set1)); /// ``` #[doc(alias = "isSubsetOfSet:")] - #[method(isSubsetOfSet:)] - pub fn is_subset(&self, other: &NSSet) -> bool; + pub fn is_subset(&self, other: &NSSet) -> 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`. @@ -277,9 +355,6 @@ extern_methods!( other.is_subset(self) } - #[method(intersectsSet:)] - fn intersects_set(&self, other: &NSSet) -> bool; - /// Returns `true` if `self` has no elements in common with `other`. /// /// # Examples @@ -296,15 +371,67 @@ extern_methods!( /// assert!(set2.is_disjoint(&set3)); /// ``` pub fn is_disjoint(&self, other: &NSSet) -> bool { - !self.intersects_set(other) + !unsafe { self.intersectsSet(other) } } } ); +#[cfg(feature = "Foundation_NSMutableSet")] +impl NSMutableSet { + /// Adds a value to the set. Returns whether the value was + /// newly inserted. + /// + /// # Examples + /// + /// ``` + /// use icrate::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(&mut self, value: Id) -> bool { + 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 icrate::Foundation::{NSMutableSet, NSString}; + /// use icrate::ns_string; + /// + /// let mut set = NSMutableSet::new(); + /// + /// set.insert(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 { + let contains_value = self.contains(value); + unsafe { self.removeObject(value) }; + contains_value + } +} + unsafe impl Foundation::NSFastEnumeration2 for NSSet { type Item = T; } +#[cfg(feature = "Foundation_NSMutableSet")] +unsafe impl Foundation::NSFastEnumeration2 for NSMutableSet { + type Item = T; +} + impl<'a, T: Message> IntoIterator for &'a NSSet { type Item = &'a T; type IntoIter = Foundation::NSFastEnumerator2<'a, NSSet>; @@ -315,6 +442,17 @@ impl<'a, T: Message> IntoIterator for &'a NSSet { } } +#[cfg(feature = "Foundation_NSMutableSet")] +impl<'a, T: Message> IntoIterator for &'a NSMutableSet { + type Item = &'a T; + type IntoIter = Foundation::NSFastEnumerator2<'a, NSMutableSet>; + + fn into_iter(self) -> Self::IntoIter { + use Foundation::NSFastEnumeration2; + self.iter_fast() + } +} + #[cfg(feature = "Foundation_NSEnumerator")] impl fmt::Debug for NSSet { #[inline] @@ -323,3 +461,22 @@ impl fmt::Debug for NSSet { f.debug_set().entries(self.iter_fast()).finish() } } + +#[cfg(feature = "Foundation_NSMutableSet")] +impl Extend> for NSMutableSet { + fn extend>>(&mut self, iter: I) { + iter.into_iter().for_each(move |item| { + self.insert(item); + }) + } +} + +#[cfg(feature = "Foundation_NSMutableSet")] +impl<'a, T: IsRetainable + PartialEq> Extend<&'a T> for NSMutableSet { + fn extend>(&mut self, iter: I) { + // SAFETY: Because of the `T: IsRetainable` bound, it is safe for the + // set to retain the object here. + iter.into_iter() + .for_each(move |item| unsafe { self.addObject(item) }) + } +} diff --git a/crates/icrate/src/Foundation/additions/string.rs b/crates/icrate/src/Foundation/additions/string.rs index 823c2bad8..41e2e042c 100644 --- a/crates/icrate/src/Foundation/additions/string.rs +++ b/crates/icrate/src/Foundation/additions/string.rs @@ -2,6 +2,8 @@ use core::cmp; use core::ffi::c_void; use core::fmt; +#[cfg(feature = "Foundation_NSMutableString")] +use core::ops::AddAssign; use core::panic::RefUnwindSafe; use core::panic::UnwindSafe; #[cfg(feature = "apple")] @@ -12,11 +14,13 @@ use core::str; #[cfg(feature = "apple")] use std::os::raw::c_char; -use objc2::msg_send; +use objc2::msg_send_id; use objc2::rc::{autoreleasepool_leaking, AutoreleasePool}; use objc2::runtime::__nsstring::{nsstring_len, nsstring_to_str, UTF8_ENCODING}; use crate::common::*; +#[cfg(feature = "Foundation_NSMutableString")] +use crate::Foundation::NSMutableString; use crate::Foundation::{self, NSString}; // SAFETY: `NSString` is immutable and `NSMutableString` can only be mutated @@ -120,10 +124,7 @@ impl NSString { #[doc(alias = "initWithBytes:length:encoding:")] #[allow(clippy::should_implement_trait)] // Not really sure of a better name pub fn from_str(string: &str) -> Id { - unsafe { - let obj = from_str(Self::class(), string); - Id::new(obj.cast()).unwrap() - } + unsafe { init_with_str(Self::alloc(), string) } } // TODO: initWithBytesNoCopy:, maybe add lifetime parameter to NSString? @@ -133,11 +134,23 @@ impl NSString { // See https://github.com/drewcrawford/foundationr/blob/b27683417a35510e8e5d78a821f081905b803de6/src/nsstring.rs } -pub(crate) fn from_str(cls: &Class, string: &str) -> *mut Object { +#[cfg(feature = "Foundation_NSMutableString")] +impl NSMutableString { + /// Creates a new [`NSMutableString`] by copying the given string slice. + #[doc(alias = "initWithBytes:length:encoding:")] + #[allow(clippy::should_implement_trait)] // Not really sure of a better name + pub fn from_str(string: &str) -> Id { + unsafe { init_with_str(Self::alloc(), string) } + } +} + +unsafe fn init_with_str(obj: Option>, string: &str) -> Id { let bytes: *const c_void = string.as_ptr().cast(); + // We use `msg_send_id` instead of the generated method from `icrate`, + // since that assumes the encoding is `usize`, whereas GNUStep assumes + // `i32`. unsafe { - let obj: *mut Object = msg_send![cls, alloc]; - msg_send![ + msg_send_id![ obj, initWithBytes: bytes, length: string.len(), @@ -146,6 +159,22 @@ pub(crate) fn from_str(cls: &Class, string: &str) -> *mut Object { } } +#[cfg(feature = "Foundation_NSMutableString")] +impl PartialEq for NSMutableString { + #[inline] + fn eq(&self, other: &NSString) -> bool { + PartialEq::eq(&**self, other) + } +} + +#[cfg(feature = "Foundation_NSMutableString")] +impl PartialEq for NSString { + #[inline] + fn eq(&self, other: &NSMutableString) -> bool { + PartialEq::eq(self, &**other) + } +} + impl PartialOrd for NSString { #[inline] fn partial_cmp(&self, other: &Self) -> Option { @@ -159,18 +188,75 @@ impl Ord for NSString { } } +#[cfg(feature = "Foundation_NSMutableString")] +impl PartialOrd for NSMutableString { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + PartialOrd::partial_cmp(&**self, &**other) + } +} + +#[cfg(feature = "Foundation_NSMutableString")] +impl PartialOrd for NSMutableString { + #[inline] + fn partial_cmp(&self, other: &NSString) -> Option { + PartialOrd::partial_cmp(&**self, other) + } +} + +#[cfg(feature = "Foundation_NSMutableString")] +impl PartialOrd for NSString { + #[inline] + fn partial_cmp(&self, other: &NSMutableString) -> Option { + PartialOrd::partial_cmp(self, &**other) + } +} + +#[cfg(feature = "Foundation_NSMutableString")] +impl Ord for NSMutableString { + #[inline] + fn cmp(&self, other: &Self) -> cmp::Ordering { + Ord::cmp(&**self, &**other) + } +} + // TODO: PartialEq and PartialOrd against &str // See `fruity`'s implementation: // https://github.com/nvzqz/fruity/blob/320efcf715c2c5fbd2f3084f671f2be2e03a6f2b/src/foundation/ns_string/mod.rs#L69-L163 +#[cfg(feature = "Foundation_NSMutableString")] +impl AddAssign<&NSString> for NSMutableString { + #[inline] + fn add_assign(&mut self, other: &NSString) { + self.appendString(other) + } +} + impl fmt::Display for NSString { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { autoreleasepool_leaking(|pool| fmt::Display::fmt(self.as_str(pool), f)) } } +#[cfg(feature = "Foundation_NSMutableString")] +impl fmt::Display for NSMutableString { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} + impl fmt::Debug for NSString { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { autoreleasepool_leaking(|pool| fmt::Debug::fmt(self.as_str(pool), f)) } } + +#[cfg(feature = "Foundation_NSMutableString")] +impl fmt::Write for NSMutableString { + fn write_str(&mut self, s: &str) -> fmt::Result { + let nsstring = NSString::from_str(s); + self.appendString(&nsstring); + Ok(()) + } +} diff --git a/crates/icrate/src/Foundation/fixes/data.rs b/crates/icrate/src/Foundation/fixes/data.rs deleted file mode 100644 index 86662a3c5..000000000 --- a/crates/icrate/src/Foundation/fixes/data.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![cfg(feature = "Foundation_NSData")] -use crate::common::*; -use crate::Foundation::NSData; - -extern_methods!( - unsafe impl NSData { - #[method(bytes)] - pub(crate) fn bytes_raw(&self) -> *const c_void; - } -); diff --git a/crates/icrate/src/Foundation/fixes/gnustep.rs b/crates/icrate/src/Foundation/fixes/gnustep.rs index 8ebd31151..bd4c524c8 100644 --- a/crates/icrate/src/Foundation/fixes/gnustep.rs +++ b/crates/icrate/src/Foundation/fixes/gnustep.rs @@ -2,9 +2,15 @@ use crate::common::*; use crate::Foundation; extern_methods!( + #[cfg(feature = "Foundation_NSData")] + unsafe impl Foundation::NSData { + #[method(bytes)] + pub(crate) fn bytes_raw(&self) -> Option>; + } + #[cfg(feature = "Foundation_NSMutableData")] unsafe impl Foundation::NSMutableData { #[method(mutableBytes)] - pub fn mutableBytes(&mut self) -> Option>; + pub(crate) fn mutable_bytes_raw(&mut self) -> Option>; } ); diff --git a/crates/icrate/src/Foundation/fixes/mod.rs b/crates/icrate/src/Foundation/fixes/mod.rs index c93100602..8d3684dba 100644 --- a/crates/icrate/src/Foundation/fixes/mod.rs +++ b/crates/icrate/src/Foundation/fixes/mod.rs @@ -4,7 +4,6 @@ mod __NSDecimal; #[path = "NSNotFound.rs"] mod __NSNotFound; mod copy; -mod data; mod debug; mod exception; mod generics;