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::{Object, subclass::prelude::*};
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 let borrowed = self.imp().value.try_borrow()?;
163 Ref::filter_map(borrowed, |value| value.downcast_ref::<T>())
164 .map_err(|_| BorrowError::InvalidType)
165 }
166
167 // rustdoc-stripper-ignore-next
168 /// Mutably borrows the wrapped value, returning an error if the value is currently borrowed.
169 /// or if it's not of type `T`.
170 ///
171 /// The borrow lasts until the returned `RefMut` or all `RefMut`s derived
172 /// from it exit scope. The value cannot be borrowed while this borrow is
173 /// active.
174 ///
175 /// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut).
176 pub fn try_borrow_mut<T: 'static>(&mut self) -> Result<RefMut<'_, T>, BorrowMutError> {
177 let borrowed_mut = self.imp().value.try_borrow_mut()?;
178 RefMut::filter_map(borrowed_mut, |value| value.downcast_mut::<T>())
179 .map_err(|_| BorrowMutError::InvalidType)
180 }
181
182 // rustdoc-stripper-ignore-next
183 /// Immutably borrows the wrapped value.
184 ///
185 /// The borrow lasts until the returned `Ref` exits scope. Multiple
186 /// immutable borrows can be taken out at the same time.
187 ///
188 /// # Panics
189 ///
190 /// Panics if the value is currently mutably borrowed or if it's not of type `T`.
191 ///
192 /// For a non-panicking variant, use
193 /// [`try_borrow`](#method.try_borrow).
194 #[track_caller]
195 pub fn borrow<T: 'static>(&self) -> Ref<'_, T> {
196 Ref::map(self.imp().value.borrow(), |value| {
197 value
198 .as_ref()
199 .downcast_ref::<T>()
200 .expect("can't downcast value to requested type")
201 })
202 }
203
204 // rustdoc-stripper-ignore-next
205 /// Mutably borrows the wrapped value.
206 ///
207 /// The borrow lasts until the returned `RefMut` or all `RefMut`s derived
208 /// from it exit scope. The value cannot be borrowed while this borrow is
209 /// active.
210 ///
211 /// # Panics
212 ///
213 /// Panics if the value is currently borrowed or if it's not of type `T`.
214 ///
215 /// For a non-panicking variant, use
216 /// [`try_borrow_mut`](#method.try_borrow_mut).
217 #[track_caller]
218 pub fn borrow_mut<T: 'static>(&self) -> RefMut<'_, T> {
219 RefMut::map(self.imp().value.borrow_mut(), |value| {
220 value
221 .as_mut()
222 .downcast_mut::<T>()
223 .expect("can't downcast value to requested type")
224 })
225 }
226}