use core::fmt;
use core::hint::unreachable_unchecked;
use core::panic::{RefUnwindSafe, UnwindSafe};
use objc2::exception::Exception;
use objc2::rc::Retained;
use objc2::runtime::{AnyObject, NSObject, NSObjectProtocol};
use objc2::{extern_methods, msg_send, sel, ClassType};
use crate::{util, NSException};
unsafe impl Sync for NSException {}
unsafe impl Send for NSException {}
impl UnwindSafe for NSException {}
impl RefUnwindSafe for NSException {}
impl NSException {
extern_methods!(
#[unsafe(method(raise))]
unsafe fn raise_raw(&self);
);
}
impl NSException {
#[cfg(all(feature = "NSObjCRuntime", feature = "NSString"))]
#[cfg(feature = "NSDictionary")]
pub fn new(
name: &crate::NSExceptionName,
reason: Option<&crate::NSString>,
user_info: Option<&crate::NSDictionary>,
) -> Option<Retained<Self>> {
use objc2::AllocAnyThread;
unsafe {
objc2::msg_send![
Self::alloc(),
initWithName: name,
reason: reason,
userInfo: user_info,
]
}
}
pub fn raise(&self) -> ! {
unsafe { self.raise_raw() };
unsafe { unreachable_unchecked() }
}
pub fn into_exception(this: Retained<Self>) -> Retained<Exception> {
unsafe { Retained::cast_unchecked(this) }
}
fn is_nsexception(obj: &Exception) -> bool {
if obj.class().responds_to(sel!(isKindOfClass:)) {
let obj: *const Exception = obj;
let obj = unsafe { obj.cast::<NSObject>().as_ref().unwrap() };
obj.isKindOfClass(Self::class())
} else {
false
}
}
pub fn from_exception(obj: Retained<Exception>) -> Result<Retained<Self>, Retained<Exception>> {
if Self::is_nsexception(&obj) {
Ok(unsafe { Retained::cast_unchecked::<Self>(obj) })
} else {
Err(obj)
}
}
}
impl fmt::Debug for NSException {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let obj: &AnyObject = self.as_ref();
write!(f, "{obj:?}")?;
write!(f, " '")?;
let name: Retained<NSObject> = unsafe { msg_send![self, name] };
unsafe { util::display_string(&name, f)? };
write!(f, "'")?;
write!(f, " reason: ")?;
let reason: Option<Retained<NSObject>> = unsafe { msg_send![self, reason] };
if let Some(reason) = reason {
unsafe { util::display_string(&reason, f)? };
} else {
write!(f, "(NULL)")?;
}
Ok(())
}
}