From a5e13c56369170edcee07b95175464bf64183bbd Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 31 Aug 2022 20:26:10 +0200 Subject: [PATCH 1/6] Fix Foundation changelog --- objc2/CHANGELOG_FOUNDATION.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/objc2/CHANGELOG_FOUNDATION.md b/objc2/CHANGELOG_FOUNDATION.md index b290e2c5b..75554e605 100644 --- a/objc2/CHANGELOG_FOUNDATION.md +++ b/objc2/CHANGELOG_FOUNDATION.md @@ -8,6 +8,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Unreleased - YYYY-MM-DD +### Added +* Added `NSSet`. +* Added `NSMutableSet`. +* Added `NSMutableDictionary`. + + +## objc2 0.3.0-beta.2 - 2022-08-28 + ### Added * Added `NSNumber`. * Added `NSError`. From 7c7efbd7687eb3362f40b45a6eebe579c0c98098 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 31 Aug 2022 19:59:53 +0200 Subject: [PATCH 2/6] Add NSNotFound --- objc2/CHANGELOG_FOUNDATION.md | 1 + objc2/src/foundation/mod.rs | 6 ++++++ objc2/src/foundation/string.rs | 5 ----- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/objc2/CHANGELOG_FOUNDATION.md b/objc2/CHANGELOG_FOUNDATION.md index 75554e605..d7c8ed078 100644 --- a/objc2/CHANGELOG_FOUNDATION.md +++ b/objc2/CHANGELOG_FOUNDATION.md @@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Added `NSSet`. * Added `NSMutableSet`. * Added `NSMutableDictionary`. +* Added `NSNotFound`. ## objc2 0.3.0-beta.2 - 2022-08-28 diff --git a/objc2/src/foundation/mod.rs b/objc2/src/foundation/mod.rs index f5a0af87a..7e67b5c86 100644 --- a/objc2/src/foundation/mod.rs +++ b/objc2/src/foundation/mod.rs @@ -84,6 +84,12 @@ pub use self::zone::NSZone; #[doc(no_inline)] pub use crate::ffi::{NSInteger, NSUInteger}; +/// A value indicating that a requested item couldn’t be found or doesn’t exist. +/// +/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsnotfound?language=objc). +#[allow(non_upper_case_globals)] +pub const NSNotFound: NSInteger = crate::ffi::NSIntegerMax; + #[cfg(feature = "apple")] #[link(name = "Foundation", kind = "framework")] extern "C" {} diff --git a/objc2/src/foundation/string.rs b/objc2/src/foundation/string.rs index 3b98d252e..5d6ce799c 100644 --- a/objc2/src/foundation/string.rs +++ b/objc2/src/foundation/string.rs @@ -10,7 +10,6 @@ use core::str; use std::os::raw::c_char; use super::{NSComparisonResult, NSCopying, NSMutableCopying, NSMutableString, NSObject}; -use crate::ffi; use crate::rc::{autoreleasepool, AutoreleasePool, DefaultId, Id, Shared}; use crate::runtime::{Class, Object}; use crate::{extern_class, extern_methods, msg_send, msg_send_id, ClassType}; @@ -20,10 +19,6 @@ const UTF8_ENCODING: usize = 4; #[cfg(feature = "gnustep-1-7")] const UTF8_ENCODING: i32 = 4; -#[allow(unused)] -#[allow(non_upper_case_globals)] -const NSNotFound: ffi::NSInteger = ffi::NSIntegerMax; - extern_class!( /// An immutable, plain-text Unicode string object. /// From 406cc7dc420a24417cb6bf8e24395e26b234f4fb Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 31 Aug 2022 21:07:28 +0200 Subject: [PATCH 3/6] Add initial NSBundle --- objc2/CHANGELOG_FOUNDATION.md | 1 + objc2/src/foundation/bundle.rs | 73 +++++++++++++++++++ objc2/src/foundation/mod.rs | 2 + test-ui/ui/msg_send_id_invalid_return.stderr | 12 +-- .../ui/msg_send_super_not_classtype.stderr | 8 +- 5 files changed, 86 insertions(+), 10 deletions(-) create mode 100644 objc2/src/foundation/bundle.rs diff --git a/objc2/CHANGELOG_FOUNDATION.md b/objc2/CHANGELOG_FOUNDATION.md index d7c8ed078..fe5b92c3d 100644 --- a/objc2/CHANGELOG_FOUNDATION.md +++ b/objc2/CHANGELOG_FOUNDATION.md @@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Added `NSMutableSet`. * Added `NSMutableDictionary`. * Added `NSNotFound`. +* Added `NSBundle`. ## objc2 0.3.0-beta.2 - 2022-08-28 diff --git a/objc2/src/foundation/bundle.rs b/objc2/src/foundation/bundle.rs new file mode 100644 index 000000000..c946545b1 --- /dev/null +++ b/objc2/src/foundation/bundle.rs @@ -0,0 +1,73 @@ +use core::fmt; +use core::panic::{RefUnwindSafe, UnwindSafe}; + +use super::{NSCopying, NSDictionary, NSObject, NSString}; +use crate::rc::{Id, Shared}; +use crate::{extern_class, extern_methods, msg_send_id, ns_string, ClassType}; + +extern_class!( + /// A representation of the code and resources stored in a bundle + /// directory on disk. + /// + /// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nsbundle?language=objc). + #[derive(PartialEq, Eq, Hash)] + pub struct NSBundle; + + unsafe impl ClassType for NSBundle { + type Super = NSObject; + } +); + +// SAFETY: Bundles are documented as thread-safe. +unsafe impl Sync for NSBundle {} +unsafe impl Send for NSBundle {} + +impl UnwindSafe for NSBundle {} +impl RefUnwindSafe for NSBundle {} + +extern_methods!( + unsafe impl NSBundle { + pub fn main() -> Id { + unsafe { msg_send_id![Self::class(), mainBundle] } + } + + pub fn info(&self) -> Id, Shared> { + unsafe { msg_send_id![self, infoDictionary] } + } + + pub fn name(&self) -> Option> { + self.info().get(ns_string!("CFBundleName")).map(|name| { + let ptr: *const NSObject = name; + let ptr: *const NSString = ptr.cast(); + // SAFETY: TODO + let name = unsafe { ptr.as_ref().unwrap_unchecked() }; + name.copy() + }) + } + } +); + +impl fmt::Debug for NSBundle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Delegate to NSObject + (**self).fmt(f) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloc::format; + use std::println; + + #[test] + #[cfg_attr(not(target_os = "macos"), ignore = "varies between platforms")] + fn try_running_functions() { + // This is mostly empty since cargo doesn't bundle the application + // before executing. + let bundle = NSBundle::main(); + println!("{:?}", bundle); + assert_eq!(format!("{:?}", bundle.info()), "{}"); + assert_eq!(bundle.name(), None); + } +} diff --git a/objc2/src/foundation/mod.rs b/objc2/src/foundation/mod.rs index 7e67b5c86..752f77fcc 100644 --- a/objc2/src/foundation/mod.rs +++ b/objc2/src/foundation/mod.rs @@ -53,6 +53,7 @@ pub use self::array::NSArray; pub use self::attributed_string::{NSAttributedString, NSAttributedStringKey}; +pub use self::bundle::NSBundle; pub use self::comparison_result::NSComparisonResult; pub use self::copying::{NSCopying, NSMutableCopying}; pub use self::data::NSData; @@ -102,6 +103,7 @@ extern "C" {} pub mod __ns_string; mod array; mod attributed_string; +mod bundle; mod comparison_result; mod copying; mod data; diff --git a/test-ui/ui/msg_send_id_invalid_return.stderr b/test-ui/ui/msg_send_id_invalid_return.stderr index 44e2d1943..bf28ddc3e 100644 --- a/test-ui/ui/msg_send_id_invalid_return.stderr +++ b/test-ui/ui/msg_send_id_invalid_return.stderr @@ -27,12 +27,12 @@ error[E0277]: the trait bound `objc2::runtime::Class: Message` is not satisfied Exception NSArray NSAttributedString + NSBundle NSData NSDictionary NSError NSException - NSMutableArray - and 14 others + and 15 others = note: required for `RetainSemantics` to implement `MsgSendId<&objc2::runtime::Class, objc2::runtime::Class, Shared>` error[E0277]: the trait bound `objc2::runtime::Class: Message` is not satisfied @@ -48,12 +48,12 @@ error[E0277]: the trait bound `objc2::runtime::Class: Message` is not satisfied Exception NSArray NSAttributedString + NSBundle NSData NSDictionary NSError NSException - NSMutableArray - and 14 others + and 15 others = note: required for `RetainSemantics` to implement `MsgSendId<&objc2::runtime::Class, objc2::runtime::Class, Shared>` error[E0277]: the trait bound `&objc2::runtime::Object: MaybeUnwrap, _>` is not satisfied @@ -85,12 +85,12 @@ error[E0277]: the trait bound `objc2::runtime::Class: Message` is not satisfied Exception NSArray NSAttributedString + NSBundle NSData NSDictionary NSError NSException - NSMutableArray - and 14 others + and 15 others = note: required for `RetainSemantics` to implement `MsgSendId<&objc2::runtime::Class, Allocated, Shared>` error[E0277]: the trait bound `Id: MaybeUnwrap, _>` is not satisfied diff --git a/test-ui/ui/msg_send_super_not_classtype.stderr b/test-ui/ui/msg_send_super_not_classtype.stderr index ea06183aa..91ab9b909 100644 --- a/test-ui/ui/msg_send_super_not_classtype.stderr +++ b/test-ui/ui/msg_send_super_not_classtype.stderr @@ -10,13 +10,13 @@ error[E0277]: the trait bound `objc2::runtime::Object: ClassType` is not satisfi = help: the following other types implement trait `ClassType`: NSArray NSAttributedString + NSBundle NSData NSDictionary NSError NSException NSMutableArray - NSMutableAttributedString - and 12 others + and 13 others note: required by a bound in `__send_super_message_static` --> $WORKSPACE/objc2/src/message/mod.rs | @@ -35,13 +35,13 @@ error[E0277]: the trait bound `objc2::runtime::Object: ClassType` is not satisfi = help: the following other types implement trait `ClassType`: NSArray NSAttributedString + NSBundle NSData NSDictionary NSError NSException NSMutableArray - NSMutableAttributedString - and 12 others + and 13 others note: required by a bound in `__send_super_message_static` --> $WORKSPACE/objc2/src/message/mod.rs | From 246fe9456fbb4f36db8f290b7acdcc48d395fd7c Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 31 Aug 2022 20:01:20 +0200 Subject: [PATCH 4/6] Add NSTimeInterval --- objc2/CHANGELOG_FOUNDATION.md | 1 + objc2/src/foundation/mod.rs | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/objc2/CHANGELOG_FOUNDATION.md b/objc2/CHANGELOG_FOUNDATION.md index fe5b92c3d..f8e734ab2 100644 --- a/objc2/CHANGELOG_FOUNDATION.md +++ b/objc2/CHANGELOG_FOUNDATION.md @@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Added `NSMutableDictionary`. * Added `NSNotFound`. * Added `NSBundle`. +* Added `NSTimeInterval`. ## objc2 0.3.0-beta.2 - 2022-08-28 diff --git a/objc2/src/foundation/mod.rs b/objc2/src/foundation/mod.rs index 752f77fcc..32cfb135a 100644 --- a/objc2/src/foundation/mod.rs +++ b/objc2/src/foundation/mod.rs @@ -51,6 +51,8 @@ #![allow(missing_docs)] #![allow(clippy::missing_safety_doc)] +use std::os::raw::c_double; + pub use self::array::NSArray; pub use self::attributed_string::{NSAttributedString, NSAttributedStringKey}; pub use self::bundle::NSBundle; @@ -91,6 +93,11 @@ pub use crate::ffi::{NSInteger, NSUInteger}; #[allow(non_upper_case_globals)] pub const NSNotFound: NSInteger = crate::ffi::NSIntegerMax; +/// A number of seconds. +/// +/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nstimeinterval?language=objc). +pub type NSTimeInterval = c_double; + #[cfg(feature = "apple")] #[link(name = "Foundation", kind = "framework")] extern "C" {} From 41e8f84db37e8d424c322e4b4fa019e7cdb3d21f Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 31 Aug 2022 20:07:18 +0200 Subject: [PATCH 5/6] Add len_utf16 methods --- objc2/CHANGELOG_FOUNDATION.md | 1 + objc2/src/foundation/attributed_string.rs | 4 +--- objc2/src/foundation/string.rs | 5 ++--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/objc2/CHANGELOG_FOUNDATION.md b/objc2/CHANGELOG_FOUNDATION.md index f8e734ab2..fbfff47fb 100644 --- a/objc2/CHANGELOG_FOUNDATION.md +++ b/objc2/CHANGELOG_FOUNDATION.md @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Added `NSNotFound`. * Added `NSBundle`. * Added `NSTimeInterval`. +* Added `NSString::len_utf16` and `NSAttributedString::len_utf16`. ## objc2 0.3.0-beta.2 - 2022-08-28 diff --git a/objc2/src/foundation/attributed_string.rs b/objc2/src/foundation/attributed_string.rs index 57cd8ec0d..98e7ed34d 100644 --- a/objc2/src/foundation/attributed_string.rs +++ b/objc2/src/foundation/attributed_string.rs @@ -85,9 +85,7 @@ extern_methods!( /// Alias for `self.string().len_utf16()`. #[doc(alias = "length")] #[sel(length)] - #[allow(unused)] - // TODO: Finish this - fn len_utf16(&self) -> usize; + pub fn len_utf16(&self) -> usize; // /// TODO // /// diff --git a/objc2/src/foundation/string.rs b/objc2/src/foundation/string.rs index 5d6ce799c..7e37274a8 100644 --- a/objc2/src/foundation/string.rs +++ b/objc2/src/foundation/string.rs @@ -63,13 +63,12 @@ extern_methods!( unsafe { msg_send![self, lengthOfBytesUsingEncoding: UTF8_ENCODING] } } - /// The number of UTF-16 code units in `self`. + /// The number of UTF-16 code units in the string. /// /// See also [`NSString::len`]. #[doc(alias = "length")] - // TODO: Finish this #[sel(length)] - fn len_utf16(&self) -> usize; + pub fn len_utf16(&self) -> usize; pub fn is_empty(&self) -> bool { // TODO: lengthOfBytesUsingEncoding: might sometimes return 0 for From 06f455cd33706bd7a6269f6508e65c396f691d10 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 31 Aug 2022 20:24:33 +0200 Subject: [PATCH 6/6] Add NSString::concat and NSString::join_path --- objc2/CHANGELOG_FOUNDATION.md | 1 + objc2/src/foundation/string.rs | 51 ++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/objc2/CHANGELOG_FOUNDATION.md b/objc2/CHANGELOG_FOUNDATION.md index fbfff47fb..c85a7672d 100644 --- a/objc2/CHANGELOG_FOUNDATION.md +++ b/objc2/CHANGELOG_FOUNDATION.md @@ -16,6 +16,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Added `NSBundle`. * Added `NSTimeInterval`. * Added `NSString::len_utf16` and `NSAttributedString::len_utf16`. +* Added `NSString::concat` and `NSString::join_path`. ## objc2 0.3.0-beta.2 - 2022-08-28 diff --git a/objc2/src/foundation/string.rs b/objc2/src/foundation/string.rs index 7e37274a8..3962c4f60 100644 --- a/objc2/src/foundation/string.rs +++ b/objc2/src/foundation/string.rs @@ -56,6 +56,57 @@ extern_methods!( unsafe { msg_send_id![Self::class(), new] } } + /// Create a new string by appending the given string to self. + /// + /// + /// # Example + /// + /// ``` + /// # #[cfg(feature = "gnustep-1-7")] + /// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() }; + /// use objc2::ns_string; + /// let error_tag = ns_string!("Error: "); + /// let error_string = ns_string!("premature end of file."); + /// let error_message = error_tag.concat(error_string); + /// assert_eq!(&*error_message, ns_string!("Error: premature end of file.")); + /// ``` + #[doc(alias = "stringByAppendingString")] + #[doc(alias = "stringByAppendingString:")] + pub fn concat(&self, other: &Self) -> Id { + // SAFETY: The other string is non-null, and won't be retained + // by the function. + unsafe { msg_send_id![self, stringByAppendingString: other] } + } + + /// Create a new string by appending the given string, separated by + /// a path separator. + /// + /// This is similar to [`Path::join`][std::path::Path::join]. + /// + /// Note that this method only works with file paths (not, for + /// example, string representations of URLs). + /// + /// + /// # Examples + /// + /// ``` + /// # #[cfg(feature = "gnustep-1-7")] + /// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() }; + /// use objc2::ns_string; + /// + /// let extension = ns_string!("scratch.tiff"); + /// assert_eq!(&*ns_string!("/tmp").join_path(extension), ns_string!("/tmp/scratch.tiff")); + /// assert_eq!(&*ns_string!("/tmp/").join_path(extension), ns_string!("/tmp/scratch.tiff")); + /// assert_eq!(&*ns_string!("/").join_path(extension), ns_string!("/scratch.tiff")); + /// assert_eq!(&*ns_string!("").join_path(extension), ns_string!("scratch.tiff")); + /// ``` + #[doc(alias = "stringByAppendingPathComponent")] + #[doc(alias = "stringByAppendingPathComponent:")] + pub fn join_path(&self, other: &Self) -> Id { + // SAFETY: Same as `Self::concat`. + unsafe { msg_send_id![self, stringByAppendingPathComponent: other] } + } + /// The number of UTF-8 code units in `self`. #[doc(alias = "lengthOfBytesUsingEncoding")] #[doc(alias = "lengthOfBytesUsingEncoding:")]