Fix BOOL return values from NSUserDefaults, improve documentation
This commit is contained in:
parent
8c39ea6f94
commit
db4da24268
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use cacao::macos::app::{App, AppDelegate};
|
use cacao::macos::{App, AppDelegate};
|
||||||
use cacao::defaults::{UserDefaults, Value};
|
use cacao::defaults::{UserDefaults, Value};
|
||||||
use cacao::foundation::NSData;
|
use cacao::foundation::NSData;
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ impl AppDelegate for DefaultsTest {
|
||||||
//map.insert("LOL", Value::string("laugh"));
|
//map.insert("LOL", Value::string("laugh"));
|
||||||
//map.insert("X", Value::Integer(1));
|
//map.insert("X", Value::Integer(1));
|
||||||
//map.insert("X2", Value::Float(1.0));
|
//map.insert("X2", Value::Float(1.0));
|
||||||
map.insert("BOOL", Value::bool(true));
|
map.insert("BOOL", Value::Bool(false));
|
||||||
|
|
||||||
println!("Test equivalency:");
|
println!("Test equivalency:");
|
||||||
let s = "BYTES TEST".to_string().into_bytes();
|
let s = "BYTES TEST".to_string().into_bytes();
|
||||||
|
@ -34,7 +34,7 @@ impl AppDelegate for DefaultsTest {
|
||||||
|
|
||||||
//println!("Retrieved LOL: {:?}", defaults.get("LOL"));
|
//println!("Retrieved LOL: {:?}", defaults.get("LOL"));
|
||||||
//println!("Retrieved LOL: {:?}", defaults.get("X"));
|
//println!("Retrieved LOL: {:?}", defaults.get("X"));
|
||||||
//println!("Retrieved LOL: {:?}", defaults.get("X2"));
|
println!("Retrieved LOL: {:?}", defaults.get("BOOL"));
|
||||||
|
|
||||||
let bytes = defaults.get("BYTES").unwrap();
|
let bytes = defaults.get("BYTES").unwrap();
|
||||||
println!("Bytes: {:?}", bytes);
|
println!("Bytes: {:?}", bytes);
|
||||||
|
|
|
@ -197,15 +197,24 @@ impl UserDefaults {
|
||||||
// `NSNumber` returned and see what the wrapped encoding type is. `q` and `d` represent
|
// `NSNumber` returned and see what the wrapped encoding type is. `q` and `d` represent
|
||||||
// `NSInteger` (platform specific) and `double` (f64) respectively, but conceivably we
|
// `NSInteger` (platform specific) and `double` (f64) respectively, but conceivably we
|
||||||
// might need others.
|
// might need others.
|
||||||
|
//
|
||||||
|
// BOOL returns as "c", which... something makes me feel weird there, but testing it seems
|
||||||
|
// reliable.
|
||||||
|
//
|
||||||
|
// For context: https://nshipster.com/type-encodings/
|
||||||
if NSNumber::is(result) {
|
if NSNumber::is(result) {
|
||||||
let number = NSNumber::wrap(result);
|
let number = NSNumber::wrap(result);
|
||||||
|
|
||||||
return match number.objc_type() {
|
return match number.objc_type() {
|
||||||
"q" => Some(Value::Integer(number.as_i64())),
|
"c" => Some(Value::Bool(number.as_bool())),
|
||||||
"d" => Some(Value::Float(number.as_f64())),
|
"d" => Some(Value::Float(number.as_f64())),
|
||||||
|
"q" => Some(Value::Integer(number.as_i64())),
|
||||||
|
|
||||||
|
x => {
|
||||||
|
// Debugging code that should be removed at some point.
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
println!("Code: {}", x);
|
||||||
|
|
||||||
_ => {
|
|
||||||
// @TODO: Verify this area.
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
//! A wrapper for `NSDictionary`, which aims to make dealing with the class throughout this
|
|
||||||
//! framework a tad bit simpler.
|
|
||||||
|
|
||||||
use objc::{class, msg_send, sel, sel_impl};
|
use objc::{class, msg_send, sel, sel_impl};
|
||||||
use objc::runtime::Object;
|
use objc::runtime::Object;
|
||||||
use objc_id::Id;
|
use objc_id::Id;
|
||||||
|
@ -19,18 +16,28 @@ impl Default for NSDictionary {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NSDictionary {
|
impl NSDictionary {
|
||||||
|
/// Constructs an `NSMutableDictionary` and retains it.
|
||||||
|
///
|
||||||
|
/// Why mutable? It's just easier for working with it, as they're (mostly) interchangeable when
|
||||||
|
/// passed around in Objective-C. We guard against mutation on our side using the standard Rust
|
||||||
|
/// object model. You can, of course, bypass it and `msg_send![]` yourself, but it'd require an
|
||||||
|
/// `unsafe {}` block... so you'll know you're in special territory then.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
NSDictionary(unsafe {
|
NSDictionary(unsafe {
|
||||||
Id::from_ptr(msg_send![class!(NSMutableDictionary), new])
|
Id::from_ptr(msg_send![class!(NSMutableDictionary), new])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Inserts an object into the backing NSMutablyDictionary.
|
||||||
|
///
|
||||||
|
/// This intentionally requires `NSString` be allocated ahead of time.
|
||||||
pub fn insert(&mut self, key: NSString, object: id) {
|
pub fn insert(&mut self, key: NSString, object: id) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let _: () = msg_send![&*self.0, setObject:object forKey:key.into_inner()];
|
let _: () = msg_send![&*self.0, setObject:object forKey:key.into_inner()];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Consumes and returns the underlying `NSMutableDictionary`.
|
||||||
pub fn into_inner(mut self) -> id {
|
pub fn into_inner(mut self) -> id {
|
||||||
&mut *self.0
|
&mut *self.0
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,3 @@
|
||||||
//! A wrapper for `NSNumber`.
|
|
||||||
//!
|
|
||||||
//! There are a few places where we have to interact with this type (e.g, `NSUserDefaults`) and so
|
|
||||||
//! this type exists to wrap those unsafe operations.
|
|
||||||
|
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
|
@ -13,10 +8,22 @@ use objc_id::Id;
|
||||||
use crate::foundation::{id, BOOL, YES, NO, NSInteger};
|
use crate::foundation::{id, BOOL, YES, NO, NSInteger};
|
||||||
|
|
||||||
/// Wrapper for a retained `NSNumber` object.
|
/// 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)]
|
#[derive(Debug)]
|
||||||
pub struct NSNumber(pub Id<Object>);
|
pub struct NSNumber(pub Id<Object>);
|
||||||
|
|
||||||
impl NSNumber {
|
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 {
|
pub fn bool(value: bool) -> Self {
|
||||||
NSNumber(unsafe {
|
NSNumber(unsafe {
|
||||||
Id::from_ptr(msg_send![class!(NSNumber), numberWithBool:match value {
|
Id::from_ptr(msg_send![class!(NSNumber), numberWithBool:match value {
|
||||||
|
@ -26,18 +33,25 @@ impl NSNumber {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Constructs a `numberWithInteger` instance of `NSNumber` and retains it.
|
||||||
pub fn integer(value: i64) -> Self {
|
pub fn integer(value: i64) -> Self {
|
||||||
NSNumber(unsafe {
|
NSNumber(unsafe {
|
||||||
Id::from_ptr(msg_send![class!(NSNumber), numberWithInteger:value as NSInteger])
|
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 {
|
pub fn float(value: f64) -> Self {
|
||||||
NSNumber(unsafe {
|
NSNumber(unsafe {
|
||||||
Id::from_ptr(msg_send![class!(NSNumber), numberWithDouble:value])
|
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 {
|
pub fn objc_type(&self) -> &str {
|
||||||
unsafe {
|
unsafe {
|
||||||
let t: *const c_char = msg_send![&*self.0, objCType];
|
let t: *const c_char = msg_send![&*self.0, objCType];
|
||||||
|
@ -46,6 +60,10 @@ impl NSNumber {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
pub fn as_i64(&self) -> i64 {
|
||||||
unsafe {
|
unsafe {
|
||||||
let i: NSInteger = msg_send![&*self.0, integerValue];
|
let i: NSInteger = msg_send![&*self.0, integerValue];
|
||||||
|
@ -53,18 +71,30 @@ impl NSNumber {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
pub fn as_f64(&self) -> f64 {
|
||||||
unsafe {
|
unsafe {
|
||||||
msg_send![&*self.0, doubleValue]
|
msg_send![&*self.0, doubleValue]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If we're vended an NSNumber from a method (e.g, `NSUserDefaults` querying) we might want to
|
/// Pulls the underlying `BOOL` value out and passes it back as a `bool`.
|
||||||
/// wrap it while we figure out what to do with it. This does that.
|
///
|
||||||
pub fn wrap(data: id) -> Self {
|
/// Note that this _does not check_ if the underlying type is actually this. You are
|
||||||
NSNumber(unsafe {
|
/// responsible for doing so via the `objc_type()` method.
|
||||||
Id::from_ptr(data)
|
pub fn as_bool(&self) -> bool {
|
||||||
})
|
let result: BOOL = unsafe {
|
||||||
|
msg_send![&*self.0, boolValue]
|
||||||
|
};
|
||||||
|
|
||||||
|
match result {
|
||||||
|
YES => true,
|
||||||
|
NO => false,
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A helper method for determining if a given `NSObject` is an `NSNumber`.
|
/// A helper method for determining if a given `NSObject` is an `NSNumber`.
|
||||||
|
|
Loading…
Reference in a new issue