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}