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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// Take a look at the license at the top of the repository in the LICENSE file.

use crate as glib;
use crate::subclass::prelude::*;
use crate::{object_subclass, wrapper, Object};
use std::any::Any;
use std::cell::{Ref, RefCell, RefMut};

#[derive(thiserror::Error, Debug)]
pub enum BorrowError {
    #[error("type of the inner value is not as requested")]
    InvalidType,
    #[error("value is already mutably borrowed")]
    AlreadyBorrowed(#[from] std::cell::BorrowError),
}
#[derive(thiserror::Error, Debug)]
pub enum BorrowMutError {
    #[error("type of the inner value is not as requested")]
    InvalidType,
    #[error("value is already immutably borrowed")]
    AlreadyMutBorrowed(#[from] std::cell::BorrowMutError),
}

mod imp {
    use super::*;

    #[derive(Debug)]
    pub struct BoxedAnyObject {
        pub value: RefCell<Box<dyn Any>>,
    }
    #[object_subclass]
    impl ObjectSubclass for BoxedAnyObject {
        const NAME: &'static str = "BoxedAnyObject";
        type Type = super::BoxedAnyObject;
    }
    impl Default for BoxedAnyObject {
        fn default() -> Self {
            Self {
                value: RefCell::new(Box::new(None::<usize>)),
            }
        }
    }
    impl ObjectImpl for BoxedAnyObject {}
}

wrapper! {
    // rustdoc-stripper-ignore-next
    /// This is a subclass of `glib::object::Object` capable of storing any Rust type.
    /// It let's you insert a Rust type anywhere a `glib::object::Object` is needed.
    /// The inserted value can then be borrowed as a Rust type, by using the various
    /// provided methods.
    ///
    /// # Examples
    /// ```
    /// use glib::prelude::*;
    /// use glib::BoxedAnyObject;
    /// use std::cell::Ref;
    ///
    /// struct Author {
    ///     name: String,
    ///     subscribers: usize
    /// }
    /// // BoxedAnyObject can contain any custom type
    /// let boxed = BoxedAnyObject::new(Author {
    ///     name: String::from("GLibAuthor"),
    ///     subscribers: 1000
    /// });
    ///
    /// // The value can be retrieved with `borrow`
    /// let author: Ref<Author> = boxed.borrow();
    /// ```
    ///
    /// ```ignore
    /// use gio::ListStore;
    ///
    /// // The boxed data can be stored as a `glib::object::Object`
    /// let list = ListStore::new(BoxedAnyObject::static_type());
    /// list.append(&boxed);
    /// ```
    pub struct BoxedAnyObject(ObjectSubclass<imp::BoxedAnyObject>);
}

impl BoxedAnyObject {
    // rustdoc-stripper-ignore-next
    /// Creates a new `BoxedAnyObject` containing `value`
    pub fn new<T: 'static>(value: T) -> Self {
        let obj: Self = Object::new(&[]);
        obj.replace(value);
        obj
    }

    // rustdoc-stripper-ignore-next
    /// Replaces the wrapped value with a new one, returning the old value, without deinitializing either one.
    /// The returned value is inside a `Box` and must be manually downcasted if needed.
    pub fn replace<T: 'static>(&self, t: T) -> Box<dyn Any> {
        self.imp().value.replace(Box::new(t) as Box<dyn Any>)
    }

    // rustdoc-stripper-ignore-next
    /// Immutably borrows the wrapped value, returning an error if the value is currently mutably
    /// borrowed or if it's not of type `T`.
    ///
    /// The borrow lasts until the returned `Ref` exits scope. Multiple immutable borrows can be
    /// taken out at the same time.
    ///
    /// This is the non-panicking variant of [`borrow`](#method.borrow).
    pub fn try_borrow<T: 'static>(&self) -> Result<Ref<'_, T>, BorrowError> {
        // The required function is only available on nightly:
        // https://doc.rust-lang.org/std/cell/struct.Ref.html#method.filter_map.
        // As a workaround, I check if everything is safe, then I unwrap

        let borrowed = self.imp().value.try_borrow()?;
        borrowed
            .as_ref()
            .downcast_ref::<T>()
            .ok_or(BorrowError::InvalidType)?;
        Ok(self.borrow()) // Now this won't panic
    }

    // rustdoc-stripper-ignore-next
    /// Mutably borrows the wrapped value, returning an error if the value is currently borrowed.
    /// or if it's not of type `T`.
    ///
    /// The borrow lasts until the returned `RefMut` or all `RefMut`s derived
    /// from it exit scope. The value cannot be borrowed while this borrow is
    /// active.
    ///
    /// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut).
    pub fn try_borrow_mut<T: 'static>(&mut self) -> Result<RefMut<'_, T>, BorrowMutError> {
        // The required function is only available on nightly:
        // https://doc.rust-lang.org/std/cell/struct.Ref.html#method.filter_map
        // As a workaround, I check if everything is safe, then I unwrap.

        let mut borrowed_mut = self.imp().value.try_borrow_mut()?;
        borrowed_mut
            .as_mut()
            .downcast_mut::<T>()
            .ok_or(BorrowMutError::InvalidType)?;
        drop(borrowed_mut);
        Ok(self.borrow_mut()) // Now this won't panic
    }

    // rustdoc-stripper-ignore-next
    /// Immutably borrows the wrapped value.
    ///
    /// The borrow lasts until the returned `Ref` exits scope. Multiple
    /// immutable borrows can be taken out at the same time.
    ///
    /// # Panics
    ///
    /// Panics if the value is currently mutably borrowed or if it's not of type `T`.
    ///
    /// For a non-panicking variant, use
    /// [`try_borrow`](#method.try_borrow).
    pub fn borrow<T: 'static>(&self) -> Ref<'_, T> {
        Ref::map(self.imp().value.borrow(), |value| {
            value
                .as_ref()
                .downcast_ref::<T>()
                .expect("can't downcast value to requested type")
        })
    }

    // rustdoc-stripper-ignore-next
    /// Mutably borrows the wrapped value.
    ///
    /// The borrow lasts until the returned `RefMut` or all `RefMut`s derived
    /// from it exit scope. The value cannot be borrowed while this borrow is
    /// active.
    ///
    /// # Panics
    ///
    /// Panics if the value is currently borrowed or if it's not of type `T`.
    ///
    /// For a non-panicking variant, use
    /// [`try_borrow_mut`](#method.try_borrow_mut).
    pub fn borrow_mut<T: 'static>(&self) -> RefMut<'_, T> {
        RefMut::map(self.imp().value.borrow_mut(), |value| {
            value
                .as_mut()
                .downcast_mut::<T>()
                .expect("can't downcast value to requested type")
        })
    }
}