cacao/src/foundation/number.rs
Ryan McGrath 9511a5a82c BOOL handling differs on M1 vs x64.
The M1 ruined me. Fixes building and running on Intel-based Macs.
2021-02-08 11:54:35 -08:00

110 lines
3.5 KiB
Rust

use std::ffi::CStr;
use std::os::raw::c_char;
use objc::{class, msg_send, sel, sel_impl};
use objc::runtime::Object;
use objc_id::Id;
use crate::foundation::{id, to_bool, BOOL, YES, NO, NSInteger};
/// Wrapper for a retained `NSNumber` object.
///
/// In general we strive to avoid using this in the codebase, but it's a requirement for moving
/// objects in and out of certain situations (e.g, `UserDefaults`).
#[derive(Debug)]
pub struct NSNumber(pub Id<Object>);
impl NSNumber {
/// If we're vended an NSNumber from a method (e.g, `NSUserDefaults` querying) we might want to
/// wrap it while we figure out what to do with it. This does that.
pub fn wrap(data: id) -> Self {
NSNumber(unsafe {
Id::from_ptr(data)
})
}
/// Constructs a `numberWithBool` instance of `NSNumber` and retains it.
pub fn bool(value: bool) -> Self {
NSNumber(unsafe {
Id::from_ptr(msg_send![class!(NSNumber), numberWithBool:match value {
true => YES,
false => NO
}])
})
}
/// Constructs a `numberWithInteger` instance of `NSNumber` and retains it.
pub fn integer(value: i64) -> Self {
NSNumber(unsafe {
Id::from_ptr(msg_send![class!(NSNumber), numberWithInteger:value as NSInteger])
})
}
/// Constructs a `numberWithDouble` instance of `NSNumber` and retains it.
pub fn float(value: f64) -> Self {
NSNumber(unsafe {
Id::from_ptr(msg_send![class!(NSNumber), numberWithDouble:value])
})
}
/// Returns the `objCType` of the underlying `NSNumber` as a Rust `&str`. This flag can be used
/// to inform you how you should pull the underlying data out of the `NSNumber`.
///
/// For more information:
/// [https://nshipster.com/type-encodings/](https://nshipster.com/type-encodings/)
pub fn objc_type(&self) -> &str {
unsafe {
let t: *const c_char = msg_send![&*self.0, objCType];
let slice = CStr::from_ptr(t);
slice.to_str().unwrap()
}
}
/// Pulls the underlying `NSInteger` value out and passes it back as an `i64`.
///
/// Note that this _does not check_ if the underlying type is actually this. You are
/// responsible for doing so via the `objc_type()` method.
pub fn as_i64(&self) -> i64 {
unsafe {
let i: NSInteger = msg_send![&*self.0, integerValue];
i as i64
}
}
/// Pulls the underlying `double` value out and passes it back as an `f64`.
///
/// Note that this _does not check_ if the underlying type is actually this. You are
/// responsible for doing so via the `objc_type()` method.
pub fn as_f64(&self) -> f64 {
unsafe {
msg_send![&*self.0, doubleValue]
}
}
/// Pulls the underlying `BOOL` value out and passes it back as a `bool`.
///
/// Note that this _does not check_ if the underlying type is actually this. You are
/// responsible for doing so via the `objc_type()` method.
pub fn as_bool(&self) -> bool {
let result: BOOL = unsafe {
msg_send![&*self.0, boolValue]
};
to_bool(result)
}
/// A helper method for determining if a given `NSObject` is an `NSNumber`.
pub fn is(obj: id) -> bool {
let result: BOOL = unsafe {
msg_send![obj, isKindOfClass:class!(NSNumber)]
};
to_bool(result)
}
/// Consumes and returns the underlying `NSNumber`.
pub fn into_inner(mut self) -> id {
&mut *self.0
}
}