1use std::any::{Any, TypeId};
18use std::cell::{Cell, RefCell};
19use std::collections::HashMap;
20use std::ops::{Deref, DerefMut};
21use std::rc::Rc;
22
23use crate::error::{Error, Result};
24use crate::state::Lua;
25use crate::sys::lua_State;
26
27type Entry = Rc<RefCell<Box<dyn Any>>>;
30
31#[derive(Default)]
34struct Store {
35 entries: HashMap<TypeId, Entry>,
36 borrow: Rc<Cell<usize>>,
37}
38
39thread_local! {
40 static APP_DATA: RefCell<HashMap<*mut core::ffi::c_void, Store>> =
42 RefCell::new(HashMap::new());
43}
44
45unsafe fn vm_key(state: *mut lua_State) -> *mut core::ffi::c_void {
46 unsafe { (*state).global as *mut core::ffi::c_void }
47}
48
49pub struct AppDataRef<T: 'static> {
52 _owner: Entry,
56 guard: std::cell::Ref<'static, Box<dyn Any>>,
57 borrow: Rc<Cell<usize>>,
58 _marker: std::marker::PhantomData<T>,
59}
60
61impl<T: 'static> Drop for AppDataRef<T> {
62 fn drop(&mut self) {
63 self.borrow.set(self.borrow.get().saturating_sub(1));
64 }
65}
66
67impl<T: 'static> Deref for AppDataRef<T> {
68 type Target = T;
69 fn deref(&self) -> &T {
70 self.guard
71 .downcast_ref::<T>()
72 .expect("app data type mismatch")
73 }
74}
75
76impl<T: std::fmt::Debug + 'static> std::fmt::Debug for AppDataRef<T> {
77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78 (**self).fmt(f)
79 }
80}
81
82impl<T: std::fmt::Display + 'static> std::fmt::Display for AppDataRef<T> {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 (**self).fmt(f)
85 }
86}
87
88impl<T: PartialEq + 'static> PartialEq<T> for AppDataRef<T> {
89 fn eq(&self, other: &T) -> bool {
90 (**self) == *other
91 }
92}
93
94pub struct AppDataRefMut<T: 'static> {
97 _owner: Entry,
98 guard: std::cell::RefMut<'static, Box<dyn Any>>,
99 borrow: Rc<Cell<usize>>,
100 _marker: std::marker::PhantomData<T>,
101}
102
103impl<T: 'static> Drop for AppDataRefMut<T> {
104 fn drop(&mut self) {
105 self.borrow.set(self.borrow.get().saturating_sub(1));
106 }
107}
108
109impl<T: 'static> Deref for AppDataRefMut<T> {
110 type Target = T;
111 fn deref(&self) -> &T {
112 self.guard
113 .downcast_ref::<T>()
114 .expect("app data type mismatch")
115 }
116}
117
118impl<T: 'static> DerefMut for AppDataRefMut<T> {
119 fn deref_mut(&mut self) -> &mut T {
120 self.guard
121 .downcast_mut::<T>()
122 .expect("app data type mismatch")
123 }
124}
125
126impl<T: std::fmt::Debug + 'static> std::fmt::Debug for AppDataRefMut<T> {
127 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128 (**self).fmt(f)
129 }
130}
131
132impl<T: PartialEq + 'static> PartialEq<T> for AppDataRefMut<T> {
133 fn eq(&self, other: &T) -> bool {
134 (**self) == *other
135 }
136}
137
138impl Lua {
139 pub fn set_app_data<T: 'static>(&self, data: T) {
145 self.try_set_app_data(data)
146 .expect("cannot mutably borrow app data container");
147 }
148
149 pub fn try_set_app_data<T: 'static>(&self, data: T) -> Result<Option<T>> {
153 let key = unsafe { vm_key(self.state()) };
154 APP_DATA.with(|m| {
155 let mut outer = m.borrow_mut();
156 let store = outer.entry(key).or_default();
157 if store.borrow.get() != 0 {
159 return Err(Error::runtime("cannot mutably borrow app data container"));
160 }
161 let old = store
162 .entries
163 .insert(TypeId::of::<T>(), Rc::new(RefCell::new(Box::new(data))));
164 Ok(old.and_then(|e| {
165 Rc::try_unwrap(e)
166 .ok()
167 .and_then(|cell| cell.into_inner().downcast::<T>().ok().map(|b| *b))
168 }))
169 })
170 }
171
172 pub fn app_data_ref<T: 'static>(&self) -> Option<AppDataRef<T>> {
178 match self.try_app_data_ref::<T>() {
179 Ok(opt) => opt,
180 Err(_) => panic!("already mutably borrowed"),
181 }
182 }
183
184 pub fn try_app_data_ref<T: 'static>(&self) -> Result<Option<AppDataRef<T>>> {
188 let key = unsafe { vm_key(self.state()) };
189 let (entry, borrow) = match APP_DATA.with(|m| {
190 let outer = m.borrow();
191 outer.get(&key).and_then(|store| {
192 store
193 .entries
194 .get(&TypeId::of::<T>())
195 .map(|e| (e.clone(), store.borrow.clone()))
196 })
197 }) {
198 Some(pair) => pair,
199 None => return Ok(None),
200 };
201 let guard = entry
202 .try_borrow()
203 .map_err(|_| Error::runtime("app data is currently mutably borrowed"))?;
204 let guard: std::cell::Ref<'static, Box<dyn Any>> = unsafe { std::mem::transmute(guard) };
207 borrow.set(borrow.get() + 1);
208 Ok(Some(AppDataRef {
209 _owner: entry,
210 guard,
211 borrow,
212 _marker: std::marker::PhantomData,
213 }))
214 }
215
216 pub fn app_data_mut<T: 'static>(&self) -> Option<AppDataRefMut<T>> {
222 match self.try_app_data_mut::<T>() {
223 Ok(opt) => opt,
224 Err(_) => panic!("already borrowed"),
225 }
226 }
227
228 pub fn try_app_data_mut<T: 'static>(&self) -> Result<Option<AppDataRefMut<T>>> {
232 let key = unsafe { vm_key(self.state()) };
233 let (entry, borrow) = match APP_DATA.with(|m| {
234 let outer = m.borrow();
235 outer.get(&key).and_then(|store| {
236 store
237 .entries
238 .get(&TypeId::of::<T>())
239 .map(|e| (e.clone(), store.borrow.clone()))
240 })
241 }) {
242 Some(pair) => pair,
243 None => return Ok(None),
244 };
245 let guard = entry
246 .try_borrow_mut()
247 .map_err(|_| Error::runtime("app data is currently borrowed"))?;
248 let guard: std::cell::RefMut<'static, Box<dyn Any>> = unsafe { std::mem::transmute(guard) };
249 borrow.set(borrow.get() + 1);
250 Ok(Some(AppDataRefMut {
251 _owner: entry,
252 guard,
253 borrow,
254 _marker: std::marker::PhantomData,
255 }))
256 }
257
258 pub fn remove_app_data<T: 'static>(&self) -> Option<T> {
264 let key = unsafe { vm_key(self.state()) };
265 APP_DATA.with(|m| {
266 let mut outer = m.borrow_mut();
267 let store = outer.get_mut(&key)?;
268 if store.borrow.get() != 0 {
269 panic!("cannot mutably borrow app data container");
270 }
271 let entry = store.entries.remove(&TypeId::of::<T>())?;
272 Rc::try_unwrap(entry)
273 .ok()
274 .and_then(|cell| cell.into_inner().downcast::<T>().ok().map(|b| *b))
275 })
276 }
277}
278
279pub(crate) fn clear_app_data(state: *mut lua_State) {
281 let key = unsafe { vm_key(state) };
282 APP_DATA.with(|m| {
283 m.borrow_mut().remove(&key);
284 });
285}