glib/
boxed_any_object.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{
4    any::Any,
5    cell::{Ref, RefMut},
6    fmt,
7};
8
9use crate as glib;
10use crate::{subclass::prelude::*, Object};
11
12#[derive(Debug)]
13pub enum BorrowError {
14    InvalidType,
15    AlreadyBorrowed(std::cell::BorrowError),
16}
17
18impl std::error::Error for BorrowError {
19    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
20        match self {
21            Self::InvalidType => None,
22            Self::AlreadyBorrowed(err) => Some(err),
23        }
24    }
25}
26
27impl fmt::Display for BorrowError {
28    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
29        match self {
30            Self::InvalidType => fmt.write_str("type of the inner value is not as requested"),
31            Self::AlreadyBorrowed(_) => fmt.write_str("value is already mutably borrowed"),
32        }
33    }
34}
35
36impl From<std::cell::BorrowError> for BorrowError {
37    fn from(err: std::cell::BorrowError) -> Self {
38        Self::AlreadyBorrowed(err)
39    }
40}
41
42#[derive(Debug)]
43pub enum BorrowMutError {
44    InvalidType,
45    AlreadyMutBorrowed(std::cell::BorrowMutError),
46}
47
48impl std::error::Error for BorrowMutError {
49    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
50        match self {
51            Self::InvalidType => None,
52            Self::AlreadyMutBorrowed(err) => Some(err),
53        }
54    }
55}
56
57impl fmt::Display for BorrowMutError {
58    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
59        match self {
60            Self::InvalidType => fmt.write_str("type of the inner value is not as requested"),
61            Self::AlreadyMutBorrowed(_) => fmt.write_str("value is already immutably borrowed"),
62        }
63    }
64}
65
66impl From<std::cell::BorrowMutError> for BorrowMutError {
67    fn from(err: std::cell::BorrowMutError) -> Self {
68        Self::AlreadyMutBorrowed(err)
69    }
70}
71
72mod imp {
73    use std::{any::Any, cell::RefCell};
74
75    use crate as glib;
76    use crate::subclass::prelude::*;
77
78    #[derive(Debug)]
79    pub struct BoxedAnyObject {
80        pub value: RefCell<Box<dyn Any>>,
81    }
82
83    #[glib::object_subclass]
84    impl ObjectSubclass for BoxedAnyObject {
85        const NAME: &'static str = "BoxedAnyObject";
86        const ALLOW_NAME_CONFLICT: bool = true;
87        type Type = super::BoxedAnyObject;
88    }
89    impl Default for BoxedAnyObject {
90        fn default() -> Self {
91            Self {
92                value: RefCell::new(Box::new(None::<usize>)),
93            }
94        }
95    }
96    impl ObjectImpl for BoxedAnyObject {}
97}
98
99glib::wrapper! {
100    // rustdoc-stripper-ignore-next
101    /// This is a subclass of `glib::object::Object` capable of storing any Rust type.
102    /// It let's you insert a Rust type anywhere a `glib::object::Object` is needed.
103    /// The inserted value can then be borrowed as a Rust type, by using the various
104    /// provided methods.
105    ///
106    /// # Examples
107    /// ```
108    /// use glib::prelude::*;
109    /// use glib::BoxedAnyObject;
110    /// use std::cell::Ref;
111    ///
112    /// struct Author {
113    ///     name: String,
114    ///     subscribers: usize
115    /// }
116    /// // BoxedAnyObject can contain any custom type
117    /// let boxed = BoxedAnyObject::new(Author {
118    ///     name: String::from("GLibAuthor"),
119    ///     subscribers: 1000
120    /// });
121    ///
122    /// // The value can be retrieved with `borrow`
123    /// let author: Ref<Author> = boxed.borrow();
124    /// ```
125    ///
126    /// ```ignore
127    /// use gio::ListStore;
128    ///
129    /// // The boxed data can be stored as a `glib::object::Object`
130    /// let list = ListStore::new::<BoxedAnyObject>();
131    /// list.append(&boxed);
132    /// ```
133    pub struct BoxedAnyObject(ObjectSubclass<imp::BoxedAnyObject>);
134}
135
136impl BoxedAnyObject {
137    // rustdoc-stripper-ignore-next
138    /// Creates a new `BoxedAnyObject` containing `value`
139    pub fn new<T: 'static>(value: T) -> Self {
140        let obj: Self = Object::new();
141        obj.replace(value);
142        obj
143    }
144
145    // rustdoc-stripper-ignore-next
146    /// Replaces the wrapped value with a new one, returning the old value, without deinitializing either one.
147    /// The returned value is inside a `Box` and must be manually downcasted if needed.
148    #[track_caller]
149    pub fn replace<T: 'static>(&self, t: T) -> Box<dyn Any> {
150        self.imp().value.replace(Box::new(t) as Box<dyn Any>)
151    }
152
153    // rustdoc-stripper-ignore-next
154    /// Immutably borrows the wrapped value, returning an error if the value is currently mutably
155    /// borrowed or if it's not of type `T`.
156    ///
157    /// The borrow lasts until the returned `Ref` exits scope. Multiple immutable borrows can be
158    /// taken out at the same time.
159    ///
160    /// This is the non-panicking variant of [`borrow`](#method.borrow).
161    pub fn try_borrow<T: 'static>(&self) -> Result<Ref<'_, T>, BorrowError> {
162        // The required function is only available on nightly:
163        // https://doc.rust-lang.org/std/cell/struct.Ref.html#method.filter_map.
164        // As a workaround, I check if everything is safe, then I unwrap
165
166        let borrowed = self.imp().value.try_borrow()?;
167        borrowed
168            .as_ref()
169            .downcast_ref::<T>()
170            .ok_or(BorrowError::InvalidType)?;
171        Ok(self.borrow()) // Now this won't panic
172    }
173
174    // rustdoc-stripper-ignore-next
175    /// Mutably borrows the wrapped value, returning an error if the value is currently borrowed.
176    /// or if it's not of type `T`.
177    ///
178    /// The borrow lasts until the returned `RefMut` or all `RefMut`s derived
179    /// from it exit scope. The value cannot be borrowed while this borrow is
180    /// active.
181    ///
182    /// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut).
183    pub fn try_borrow_mut<T: 'static>(&mut self) -> Result<RefMut<'_, T>, BorrowMutError> {
184        // The required function is only available on nightly:
185        // https://doc.rust-lang.org/std/cell/struct.Ref.html#method.filter_map
186        // As a workaround, I check if everything is safe, then I unwrap.
187
188        let mut borrowed_mut = self.imp().value.try_borrow_mut()?;
189        borrowed_mut
190            .as_mut()
191            .downcast_mut::<T>()
192            .ok_or(BorrowMutError::InvalidType)?;
193        drop(borrowed_mut);
194        Ok(self.borrow_mut()) // Now this won't panic
195    }
196
197    // rustdoc-stripper-ignore-next
198    /// Immutably borrows the wrapped value.
199    ///
200    /// The borrow lasts until the returned `Ref` exits scope. Multiple
201    /// immutable borrows can be taken out at the same time.
202    ///
203    /// # Panics
204    ///
205    /// Panics if the value is currently mutably borrowed or if it's not of type `T`.
206    ///
207    /// For a non-panicking variant, use
208    /// [`try_borrow`](#method.try_borrow).
209    #[track_caller]
210    pub fn borrow<T: 'static>(&self) -> Ref<'_, T> {
211        Ref::map(self.imp().value.borrow(), |value| {
212            value
213                .as_ref()
214                .downcast_ref::<T>()
215                .expect("can't downcast value to requested type")
216        })
217    }
218
219    // rustdoc-stripper-ignore-next
220    /// Mutably borrows the wrapped value.
221    ///
222    /// The borrow lasts until the returned `RefMut` or all `RefMut`s derived
223    /// from it exit scope. The value cannot be borrowed while this borrow is
224    /// active.
225    ///
226    /// # Panics
227    ///
228    /// Panics if the value is currently borrowed or if it's not of type `T`.
229    ///
230    /// For a non-panicking variant, use
231    /// [`try_borrow_mut`](#method.try_borrow_mut).
232    #[track_caller]
233    pub fn borrow_mut<T: 'static>(&self) -> RefMut<'_, T> {
234        RefMut::map(self.imp().value.borrow_mut(), |value| {
235            value
236                .as_mut()
237                .downcast_mut::<T>()
238                .expect("can't downcast value to requested type")
239        })
240    }
241}