Skip to content

File tree

3 files changed

+131
-0
lines changed

3 files changed

+131
-0
lines changed

objc2-foundation/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1212
`NSData` and `NSMutableData`.
1313
* Implemented `Extend` for `NSMutableArray`.
1414
* Add extra `Extend<&u8>` impl for `NSMutableData`.
15+
* Added `NSError`.
1516

1617
### Changed
1718
* Change selector syntax in `declare_class!` macro to be more Rust-like.

objc2-foundation/src/error.rs

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
use core::fmt;
2+
use core::panic::{RefUnwindSafe, UnwindSafe};
3+
4+
use objc2::ffi::NSInteger;
5+
use objc2::rc::{Id, Shared};
6+
use objc2::{msg_send, msg_send_id};
7+
8+
use crate::{extern_class, NSCopying, NSDictionary, NSObject, NSString};
9+
10+
extern_class! {
11+
/// Information about an error condition including a domain, a
12+
/// domain-specific error code, and application-specific information.
13+
///
14+
/// See also [Apple's documentation][doc].
15+
///
16+
/// [doc]: https://developer.apple.com/documentation/foundation/nserror?language=objc
17+
#[derive(PartialEq, Eq, Hash)]
18+
unsafe pub struct NSError: NSObject;
19+
}
20+
21+
// SAFETY: Error objects are immutable data containers.
22+
unsafe impl Sync for NSError {}
23+
unsafe impl Send for NSError {}
24+
25+
impl UnwindSafe for NSError {}
26+
impl RefUnwindSafe for NSError {}
27+
28+
pub type NSErrorUserInfoKey = NSString;
29+
pub type NSErrorDomain = NSString;
30+
31+
/// Creation methods.
32+
impl NSError {
33+
/// Construct a new [`NSError`] with the given code in the given domain.
34+
pub fn new(code: NSInteger, domain: &NSString) -> Id<Self, Shared> {
35+
unsafe { Self::with_user_info(code, domain, None) }
36+
}
37+
38+
// TODO: Figure out safety of `user_info` dict!
39+
unsafe fn with_user_info(
40+
code: NSInteger,
41+
domain: &NSString,
42+
user_info: Option<&NSDictionary<NSErrorUserInfoKey, NSObject>>,
43+
) -> Id<Self, Shared> {
44+
// SAFETY: `domain` and `user_info` are copied to the error object, so
45+
// even if the `&NSString` came from a `&mut NSMutableString`, we're
46+
// still good!
47+
unsafe {
48+
msg_send_id![
49+
msg_send_id![Self::class(), alloc],
50+
initWithDomain: domain,
51+
code: code,
52+
userInfo: user_info,
53+
]
54+
.expect("unexpected NULL NSError")
55+
}
56+
}
57+
}
58+
59+
/// Accessor methods.
60+
impl NSError {
61+
pub fn domain(&self) -> Id<NSString, Shared> {
62+
unsafe { msg_send_id![self, domain].expect("unexpected NULL NSError domain") }
63+
}
64+
65+
pub fn code(&self) -> NSInteger {
66+
unsafe { msg_send![self, code] }
67+
}
68+
69+
pub fn user_info(&self) -> Option<Id<NSDictionary<NSErrorUserInfoKey, NSObject>, Shared>> {
70+
unsafe { msg_send_id![self, userInfo] }
71+
}
72+
73+
pub fn localized_description(&self) -> Id<NSString, Shared> {
74+
unsafe {
75+
msg_send_id![self, localizedDescription].expect(
76+
"unexpected NULL localized description; a default should have been generated!",
77+
)
78+
}
79+
}
80+
81+
// TODO: localizedRecoveryOptions
82+
// TODO: localizedRecoverySuggestion
83+
// TODO: localizedFailureReason
84+
// TODO: helpAnchor
85+
// TODO: +setUserInfoValueProviderForDomain:provider:
86+
// TODO: +userInfoValueProviderForDomain:
87+
88+
// TODO: recoveryAttempter
89+
// TODO: attemptRecoveryFromError:...
90+
91+
// TODO: Figure out if this is a good design, or if we should do something
92+
// differently (like a Rusty name for the function, or putting a bunch of
93+
// statics in a module instead)?
94+
#[allow(non_snake_case)]
95+
pub fn NSLocalizedDescriptionKey() -> &'static NSErrorUserInfoKey {
96+
extern "C" {
97+
#[link_name = "NSLocalizedDescriptionKey"]
98+
static VALUE: &'static NSErrorUserInfoKey;
99+
}
100+
unsafe { VALUE }
101+
}
102+
103+
// TODO: Other NSErrorUserInfoKey values
104+
// TODO: NSErrorDomain values
105+
}
106+
107+
impl fmt::Debug for NSError {
108+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109+
f.debug_struct("NSError")
110+
.field("domain", &self.domain())
111+
.field("code", &self.code())
112+
.field("user_info", &self.user_info())
113+
.finish()
114+
}
115+
}
116+
117+
impl fmt::Display for NSError {
118+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119+
write!(f, "{}", self.localized_description())
120+
}
121+
}
122+
123+
impl std::error::Error for NSError {}
124+
125+
unsafe impl NSCopying for NSError {
126+
type Ownership = Shared;
127+
type Output = Self;
128+
}

objc2-foundation/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ pub use self::copying::{NSCopying, NSMutableCopying};
5555
pub use self::data::NSData;
5656
pub use self::dictionary::NSDictionary;
5757
pub use self::enumerator::{NSEnumerator, NSFastEnumeration, NSFastEnumerator};
58+
pub use self::error::{NSError, NSErrorUserInfoKey, NSLocalizedDescriptionKey};
5859
pub use self::exception::NSException;
5960
pub use self::geometry::{CGFloat, NSPoint, NSRect, NSSize};
6061
pub use self::mutable_array::NSMutableArray;
@@ -103,6 +104,7 @@ mod data;
103104
mod declare_macro;
104105
mod dictionary;
105106
mod enumerator;
107+
mod error;
106108
mod exception;
107109
mod geometry;
108110
mod macros;

0 commit comments

Comments
 (0)