Skip to main content

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}