1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
use alloc::string::ToString;
use core::any::Any;
use core::ffi::c_void;
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::ptr::NonNull;
use core::str;
use std::ffi::{CStr, CString};
use std::os::raw::c_char;

use objc2::rc::{Id, Owned};
use objc2::runtime::Class;
use objc2::Encode;
use objc2::{class, msg_send};

use super::{INSCopying, INSObject};

pub trait INSValue: INSObject {
    type Value: 'static + Copy + Encode;

    fn value(&self) -> Self::Value {
        assert!(&Self::Value::ENCODING == self.encoding());
        let mut value = MaybeUninit::<Self::Value>::uninit();
        let ptr = value.as_mut_ptr() as *mut c_void;
        unsafe {
            let _: () = msg_send![self, getValue: ptr];
            value.assume_init()
        }
    }

    fn encoding(&self) -> &str {
        unsafe {
            let result: *const c_char = msg_send![self, objCType];
            let s = CStr::from_ptr(result);
            str::from_utf8(s.to_bytes()).unwrap()
        }
    }

    fn from_value(value: Self::Value) -> Id<Self, Owned> {
        let cls = Self::class();
        let value_ptr: *const Self::Value = &value;
        let bytes = value_ptr as *const c_void;
        let encoding = CString::new(Self::Value::ENCODING.to_string()).unwrap();
        unsafe {
            let obj: *mut Self = msg_send![cls, alloc];
            let obj: *mut Self = msg_send![
                obj,
                initWithBytes: bytes,
                objCType: encoding.as_ptr(),
            ];
            Id::new(NonNull::new_unchecked(obj))
        }
    }
}

pub struct NSValue<T> {
    value: PhantomData<T>,
}

object_impl!(NSValue<T>);

impl<T> INSObject for NSValue<T>
where
    T: Any,
{
    fn class() -> &'static Class {
        class!(NSValue)
    }
}

impl<T> INSValue for NSValue<T>
where
    T: Any + Copy + Encode,
{
    type Value = T;
}

impl<T> INSCopying for NSValue<T>
where
    T: Any,
{
    type Output = NSValue<T>;
}

#[cfg(test)]
mod tests {
    use crate::{INSValue, NSValue};
    use objc2::Encode;

    #[test]
    fn test_value() {
        let val = NSValue::from_value(13u32);
        assert!(val.value() == 13);
        assert!(&u32::ENCODING == val.encoding());
    }
}