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