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
use std::cell::RefCell;
use std::rc::Rc;

use objc::runtime::Object;
use objc_id::Id;

use crate::foundation::id;

/// A wrapper for single-threaded `ObjcProperty` types.
///
/// An `ObjcProperty` is something that exists on the Objective-C side that we want to interact with, and
/// support cloning with respect to our side and the general Rust rules. Thus, we do a layer of
/// Rc/RefCell to shield things and make life easier.
///
/// It is possible we could remove the `Id` wrapper in here if we're just doing this ourselves, and
/// is probably worth investigating at some point.
#[derive(Clone, Debug)]
pub struct ObjcProperty(Rc<RefCell<Id<Object>>>);

impl ObjcProperty {
    /// Given an Objective-C object, retains it and wraps it as a `Property`.
    pub fn retain(obj: id) -> Self {
        ObjcProperty(Rc::new(RefCell::new(unsafe { Id::from_ptr(obj) })))
    }

    /// Given an Objective-C object, retains it and wraps it as a `Property`.
    pub fn from_retained(obj: id) -> Self {
        ObjcProperty(Rc::new(RefCell::new(unsafe { Id::from_retained_ptr(obj) })))
    }

    /// Runs a handler with mutable access for the underlying Objective-C object.
    ///
    /// Note that this is mutable access from the Rust side; we make every effort to ensure things are valid
    /// on the Objective-C side as well, but there be dragons.
    pub fn with_mut<F: Fn(id)>(&self, handler: F) {
        let mut obj = self.0.borrow_mut();
        handler(&mut **obj);
    }

    /// Runs a handler with the underlying Objective-C type.
    ///
    /// The handler can return whatever; this is primarily intended for dynamically calling getters
    /// on the underlying type.
    pub fn get<R, F: Fn(&Object) -> R>(&self, handler: F) -> R {
        let obj = self.0.borrow();
        handler(&**obj)
    }
}

/// A wrapper for a single-threaded nullable `Property`.
#[derive(Debug, Default)]
pub struct PropertyNullable<T>(Rc<RefCell<Option<T>>>);

impl<T> PropertyNullable<T> {
    pub fn new(obj: T) -> Self {
        Self(Rc::new(RefCell::new(Some(obj))))
    }

    pub fn clone(&self) -> Self {
        Self(Rc::clone(&self.0))
    }

    pub fn with<F>(&self, handler: F)
    where
        F: Fn(&T)
    {
        let borrow = self.0.borrow();
        if let Some(s) = &*borrow {
            handler(s);
        }
    }

    pub fn set(&self, obj: T) {
        let mut borrow = self.0.borrow_mut();
        *borrow = Some(obj);
    }
}