Skip to content

Commit

Permalink
Refactor CopyHelper and NS[Mutable]Copying setup
Browse files Browse the repository at this point in the history
Add CounterpartOrSelf instead of CopyHelper, which lives in objc2 and is much nicer to use.

This also allows us to move NSCopying and NSMutableCopying back to reside in Foundation (note: We might have to at some point implement a small hack for these traits acting as if they contain the `copy` method, while it's actually `NSObject` that does).
  • Loading branch information
madsmtm committed Sep 7, 2023
1 parent 112d7b0 commit ee58ea3
Show file tree
Hide file tree
Showing 25 changed files with 761 additions and 685 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions crates/icrate/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* Added `Send` and `Sync` implementations for a bunch more types (same as the
ones Swift marks as `@Sendable`).
* Made some common methods in `AppKit` safe.
* Added missing `NSCopying` and `NSMutableCopying` zone methods.

### Changed
* Moved the `ns_string!` macro to `icrate::Foundation::ns_string`. The old
Expand Down Expand Up @@ -45,6 +46,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Removed
* **BREAKING**: Removed the `MainThreadMarker` argument from the closure
passed to `MainThreadBound::get_on_main`.
* **BREAKING**: Removed `Foundation::CopyHelper` since it is superseded by
`objc2::mutability::CounterpartOrSelf`


## icrate 0.0.4 - 2023-07-31
Expand Down
10 changes: 4 additions & 6 deletions crates/icrate/src/additions/Foundation/dictionary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,19 @@ use core::ops::{Index, IndexMut};
use core::panic::{RefUnwindSafe, UnwindSafe};
use core::ptr::{self, NonNull};

use objc2::mutability::IsMutable;
use objc2::mutability::{CounterpartOrSelf, IsMutable};

use super::iter;
use super::util;
use crate::common::*;
#[cfg(feature = "Foundation_NSMutableDictionary")]
use crate::Foundation::NSMutableDictionary;
use crate::Foundation::{self, Copyhelper, NSCopying, NSDictionary};
use crate::Foundation::{self, NSCopying, NSDictionary};

impl<K: Message, V: Message> NSDictionary<K, V> {
pub fn from_keys_and_objects<T>(keys: &[&T], mut vals: Vec<Id<V>>) -> Id<Self>
where
T: ClassType + NSCopying,
T::Mutability: Copyhelper<T, CopyOutput = K>,
T: ClassType + NSCopying + CounterpartOrSelf<Immutable = K>,
{
let count = min(keys.len(), vals.len());

Expand All @@ -37,8 +36,7 @@ impl<K: Message, V: Message> NSDictionary<K, V> {
impl<K: Message, V: Message> NSMutableDictionary<K, V> {
pub fn from_keys_and_objects<T>(keys: &[&T], mut vals: Vec<Id<V>>) -> Id<Self>
where
T: ClassType + NSCopying,
T::Mutability: Copyhelper<T, CopyOutput = K>,
T: ClassType + NSCopying + CounterpartOrSelf<Immutable = K>,
{
let count = min(keys.len(), vals.len());

Expand Down
4 changes: 0 additions & 4 deletions crates/icrate/src/additions/Foundation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,3 @@ pub use objc2::ffi::{NSInteger, NSUInteger};
#[cfg(feature = "Foundation_NSProxy")]
pub use objc2::runtime::__NSProxy as NSProxy;
pub use objc2::runtime::{NSObject, NSObjectProtocol, NSZone};
#[doc(inline)]
pub use objc2::runtime::{
__Copyhelper as Copyhelper, __NSCopying as NSCopying, __NSMutableCopying as NSMutableCopying,
};
89 changes: 89 additions & 0 deletions crates/icrate/src/fixes/Foundation/copying.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use objc2::mutability::CounterpartOrSelf;
use objc2::rc::Id;
use objc2::runtime::NSZone;
use objc2::{extern_protocol, ProtocolType};

extern_protocol!(
/// A protocol to provide functional copies of objects.
///
/// This is similar to Rust's [`Clone`] trait, along with sharing a few
/// similarities to the [`std::borrow::ToOwned`] trait with regards to the
/// output type.
///
/// See [Apple's documentation][apple-doc] for details.
///
/// [apple-doc]: https://developer.apple.com/documentation/foundation/nscopying
pub unsafe trait NSCopying {
/// Returns a new instance that's a copy of the receiver.
///
/// The output type is the immutable counterpart of the object, which is
/// usually `Self`, but e.g. `NSMutableString` returns `NSString`.
#[method_id(copy)]
#[optional]
fn copy(&self) -> Id<Self::Immutable>
where
Self: Sized,
Self: CounterpartOrSelf;

/// Returns a new instance that's a copy of the receiver.
///
/// This is only used when implementing `NSCopying`, use
/// [`copy`][NSCopying::copy] instead.
///
///
/// # Safety
///
/// The zone pointer must be valid or NULL.
#[method_id(copyWithZone:)]
unsafe fn copyWithZone(&self, zone: *mut NSZone) -> Id<Self::Immutable>
where
Self: Sized,
Self: CounterpartOrSelf;
}

unsafe impl ProtocolType for dyn NSCopying {
const NAME: &'static str = "NSCopying";
}
);

extern_protocol!(
/// A protocol to provide mutable copies of objects.
///
/// Only classes that have an “immutable vs. mutable” distinction should adopt
/// this protocol.
///
/// See [Apple's documentation][apple-doc] for details.
///
/// [apple-doc]: https://developer.apple.com/documentation/foundation/nsmutablecopying
pub unsafe trait NSMutableCopying {
/// Returns a new instance that's a mutable copy of the receiver.
///
/// The output type is the mutable counterpart of the object. E.g. both
/// `NSString` and `NSMutableString` return `NSMutableString`.
#[method_id(mutableCopy)]
#[optional]
fn mutableCopy(&self) -> Id<Self::Mutable>
where
Self: Sized,
Self: CounterpartOrSelf;

/// Returns a new instance that's a mutable copy of the receiver.
///
/// This is only used when implementing `NSMutableCopying`, use
/// [`mutableCopy`][NSMutableCopying::mutableCopy] instead.
///
///
/// # Safety
///
/// The zone pointer must be valid or NULL.
#[method_id(mutableCopyWithZone:)]
unsafe fn mutableCopyWithZone(&self, zone: *mut NSZone) -> Id<Self::Mutable>
where
Self: Sized,
Self: CounterpartOrSelf;
}

unsafe impl ProtocolType for dyn NSMutableCopying {
const NAME: &'static str = "NSMutableCopying";
}
);
2 changes: 2 additions & 0 deletions crates/icrate/src/fixes/Foundation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod __NSDecimal;
#[path = "NSNotFound.rs"]
mod __NSNotFound;
mod copy;
mod copying;
mod debug;
mod enumerator;
mod exception;
Expand All @@ -13,6 +14,7 @@ mod ns_consumed;

pub use self::__NSDecimal::NSDecimal;
pub use self::__NSNotFound::NSNotFound;
pub use self::copying::{NSCopying, NSMutableCopying};
pub use self::enumerator::NSFastEnumerationState;
pub use self::generics::*;
#[cfg(feature = "Foundation_NSMapTable")]
Expand Down
41 changes: 40 additions & 1 deletion crates/icrate/tests/mutable_string.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
#![cfg(feature = "Foundation_NSMutableString")]
use core::any::TypeId;
use std::ptr;

use objc2::mutability::CounterpartOrSelf;
use objc2::rc::Id;

use icrate::Foundation::{self, NSMutableString, NSObjectProtocol, NSString};
use icrate::Foundation::{
self, NSCopying, NSMutableCopying, NSMutableString, NSObjectProtocol, NSString,
};

#[test]
fn display_debug() {
Expand Down Expand Up @@ -53,3 +59,36 @@ fn test_copy() {
assert_ne!(Id::as_ptr(&s1), Id::as_ptr(&s3));
assert!(s3.is_kind_of::<NSMutableString>());
}

#[test]
fn counterpart() {
assert_eq!(
TypeId::of::<<NSString as CounterpartOrSelf>::Immutable>(),
TypeId::of::<NSString>(),
);
assert_eq!(
TypeId::of::<<NSString as CounterpartOrSelf>::Mutable>(),
TypeId::of::<NSMutableString>(),
);

assert_eq!(
TypeId::of::<<NSMutableString as CounterpartOrSelf>::Immutable>(),
TypeId::of::<NSString>(),
);
assert_eq!(
TypeId::of::<<NSMutableString as CounterpartOrSelf>::Mutable>(),
TypeId::of::<NSMutableString>(),
);
}

#[test]
fn test_copy_with_zone() {
let s1 = NSString::from_str("abc");
let s2 = unsafe { s1.copyWithZone(ptr::null_mut()) };
assert_eq!(Id::as_ptr(&s1), Id::as_ptr(&s2));
assert!(s2.is_kind_of::<NSString>());

let s3 = unsafe { s1.mutableCopyWithZone(ptr::null_mut()) };
assert_ne!(Id::as_ptr(&s1).cast::<NSMutableString>(), Id::as_ptr(&s3));
assert!(s3.is_kind_of::<Foundation::NSMutableString>());
}
1 change: 1 addition & 0 deletions crates/objc2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

### Added
* Added `mutability::IsMainThreadOnly`.
* Added `mutability::CounterpartOrSelf`.
* Added new `encode` traits `EncodeReturn`, `EncodeArgument` and
`EncodeArguments`.

Expand Down
Loading

0 comments on commit ee58ea3

Please sign in to comment.