//! A wrapper for `NSError`, which can be (and is) bubbled up for certain calls in this library. It //! attempts to be thread safe where possible, and extract the "default" usable information out of //! an `NSError`. This might not be what you need, though, so if it's missing something... well, //! it's up for discussion. use std::error; use std::fmt; use objc::{class, msg_send, sel, sel_impl}; use crate::foundation::{id, nil, NSInteger, NSString}; /// A wrapper around pieces of data extracted from `NSError`. This could be improved: right now, it /// allocates `String` instances when theoretically it could be avoided, and we might be erasing /// certain parts of the `NSError` object that are useful. #[derive(Clone, Debug)] pub struct AppKitError { pub code: usize, pub domain: String, pub description: String } impl AppKitError { /// Given an `NSError` (i.e, an id reference) we'll pull out the relevant information and /// configure this. We pull out the information as it makes the error thread safe this way, /// which is... easier, in some cases. pub fn new(error: id) -> Self { let (code, domain, description) = unsafe { let code: usize = msg_send![error, code]; let domain = NSString::wrap(msg_send![error, domain]); let description = NSString::wrap(msg_send![error, localizedDescription]); (code, domain, description) }; AppKitError { code: code, domain: domain.to_str().to_string(), description: description.to_str().to_string() } } pub fn boxed(error: id) -> Box { Box::new(AppKitError::new(error)) } /// Used for cases where we need to return an `NSError` back to the system (e.g, top-level /// error handling). We just create a new `NSError` so the `AppKitError` crate can be mostly /// thread safe. pub fn into_nserror(self) -> id { unsafe { let domain = NSString::new(&self.domain); let code = self.code as NSInteger; msg_send![class!(NSError), errorWithDomain:domain code:code userInfo:nil] } } } impl fmt::Display for AppKitError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.description) } } impl error::Error for AppKitError {}