mlua/types/
app_data.rs

1use std::any::{Any, TypeId};
2use std::cell::{BorrowError, BorrowMutError, Cell, Ref, RefCell, RefMut, UnsafeCell};
3use std::fmt;
4use std::ops::{Deref, DerefMut};
5use std::result::Result as StdResult;
6
7use rustc_hash::FxHashMap;
8
9use super::MaybeSend;
10use crate::state::LuaGuard;
11
12#[cfg(not(feature = "send"))]
13type Container = UnsafeCell<FxHashMap<TypeId, RefCell<Box<dyn Any>>>>;
14
15#[cfg(feature = "send")]
16type Container = UnsafeCell<FxHashMap<TypeId, RefCell<Box<dyn Any + Send>>>>;
17
18/// A container for arbitrary data associated with the Lua state.
19#[derive(Debug, Default)]
20pub struct AppData {
21    container: Container,
22    borrow: Cell<usize>,
23}
24
25impl AppData {
26    #[track_caller]
27    pub(crate) fn insert<T: MaybeSend + 'static>(&self, data: T) -> Option<T> {
28        match self.try_insert(data) {
29            Ok(data) => data,
30            Err(_) => panic!("cannot mutably borrow app data container"),
31        }
32    }
33
34    pub(crate) fn try_insert<T: MaybeSend + 'static>(&self, data: T) -> StdResult<Option<T>, T> {
35        if self.borrow.get() != 0 {
36            return Err(data);
37        }
38        // SAFETY: we checked that there are no other references to the container
39        Ok(unsafe { &mut *self.container.get() }
40            .insert(TypeId::of::<T>(), RefCell::new(Box::new(data)))
41            .and_then(|data| data.into_inner().downcast::<T>().ok().map(|data| *data)))
42    }
43
44    #[inline]
45    #[track_caller]
46    pub(crate) fn borrow<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRef<'_, T>> {
47        match self.try_borrow(guard) {
48            Ok(data) => data,
49            Err(err) => panic!("already mutably borrowed: {err:?}"),
50        }
51    }
52
53    pub(crate) fn try_borrow<T: 'static>(
54        &self,
55        guard: Option<LuaGuard>,
56    ) -> Result<Option<AppDataRef<'_, T>>, BorrowError> {
57        let data = unsafe { &*self.container.get() }
58            .get(&TypeId::of::<T>())
59            .map(|c| c.try_borrow())
60            .transpose()?
61            .and_then(|data| Ref::filter_map(data, |data| data.downcast_ref()).ok());
62        match data {
63            Some(data) => {
64                self.borrow.set(self.borrow.get() + 1);
65                Ok(Some(AppDataRef {
66                    data,
67                    borrow: &self.borrow,
68                    _guard: guard,
69                }))
70            }
71            None => Ok(None),
72        }
73    }
74
75    #[inline]
76    #[track_caller]
77    pub(crate) fn borrow_mut<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRefMut<'_, T>> {
78        match self.try_borrow_mut(guard) {
79            Ok(data) => data,
80            Err(err) => panic!("already borrowed: {err:?}"),
81        }
82    }
83
84    pub(crate) fn try_borrow_mut<T: 'static>(
85        &self,
86        guard: Option<LuaGuard>,
87    ) -> Result<Option<AppDataRefMut<'_, T>>, BorrowMutError> {
88        let data = unsafe { &*self.container.get() }
89            .get(&TypeId::of::<T>())
90            .map(|c| c.try_borrow_mut())
91            .transpose()?
92            .and_then(|data| RefMut::filter_map(data, |data| data.downcast_mut()).ok());
93        match data {
94            Some(data) => {
95                self.borrow.set(self.borrow.get() + 1);
96                Ok(Some(AppDataRefMut {
97                    data,
98                    borrow: &self.borrow,
99                    _guard: guard,
100                }))
101            }
102            None => Ok(None),
103        }
104    }
105
106    #[track_caller]
107    pub(crate) fn remove<T: 'static>(&self) -> Option<T> {
108        if self.borrow.get() != 0 {
109            panic!("cannot mutably borrow app data container");
110        }
111        // SAFETY: we checked that there are no other references to the container
112        unsafe { &mut *self.container.get() }
113            .remove(&TypeId::of::<T>())?
114            .into_inner()
115            .downcast::<T>()
116            .ok()
117            .map(|data| *data)
118    }
119}
120
121/// A wrapper type for an immutably borrowed value from an app data container.
122///
123/// This type is similar to [`Ref`].
124pub struct AppDataRef<'a, T: ?Sized + 'a> {
125    data: Ref<'a, T>,
126    borrow: &'a Cell<usize>,
127    _guard: Option<LuaGuard>,
128}
129
130impl<T: ?Sized> Drop for AppDataRef<'_, T> {
131    fn drop(&mut self) {
132        self.borrow.set(self.borrow.get() - 1);
133    }
134}
135
136impl<T: ?Sized> Deref for AppDataRef<'_, T> {
137    type Target = T;
138
139    #[inline]
140    fn deref(&self) -> &Self::Target {
141        &self.data
142    }
143}
144
145impl<T: ?Sized + fmt::Display> fmt::Display for AppDataRef<'_, T> {
146    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147        (**self).fmt(f)
148    }
149}
150
151impl<T: ?Sized + fmt::Debug> fmt::Debug for AppDataRef<'_, T> {
152    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153        (**self).fmt(f)
154    }
155}
156
157/// A wrapper type for a mutably borrowed value from an app data container.
158///
159/// This type is similar to [`RefMut`].
160pub struct AppDataRefMut<'a, T: ?Sized + 'a> {
161    data: RefMut<'a, T>,
162    borrow: &'a Cell<usize>,
163    _guard: Option<LuaGuard>,
164}
165
166impl<T: ?Sized> Drop for AppDataRefMut<'_, T> {
167    fn drop(&mut self) {
168        self.borrow.set(self.borrow.get() - 1);
169    }
170}
171
172impl<T: ?Sized> Deref for AppDataRefMut<'_, T> {
173    type Target = T;
174
175    #[inline]
176    fn deref(&self) -> &Self::Target {
177        &self.data
178    }
179}
180
181impl<T: ?Sized> DerefMut for AppDataRefMut<'_, T> {
182    #[inline]
183    fn deref_mut(&mut self) -> &mut Self::Target {
184        &mut self.data
185    }
186}
187
188impl<T: ?Sized + fmt::Display> fmt::Display for AppDataRefMut<'_, T> {
189    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190        (**self).fmt(f)
191    }
192}
193
194impl<T: ?Sized + fmt::Debug> fmt::Debug for AppDataRefMut<'_, T> {
195    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196        (**self).fmt(f)
197    }
198}
199
200#[cfg(test)]
201mod assertions {
202    use super::*;
203
204    #[cfg(not(feature = "send"))]
205    static_assertions::assert_not_impl_any!(AppData: Send);
206    #[cfg(feature = "send")]
207    static_assertions::assert_impl_all!(AppData: Send);
208
209    // Must be !Send
210    static_assertions::assert_not_impl_any!(AppDataRef<()>: Send);
211    static_assertions::assert_not_impl_any!(AppDataRefMut<()>: Send);
212}