1use std::cell::RefCell;
2
3thread_local! {
4 static LUA: RefCell<Option<mlua::WeakLua>> = const { RefCell::new(None) };
5}
6
7struct LuaGuard<'r> {
8 prev: Option<mlua::WeakLua>,
9 store: &'r RefCell<Option<mlua::WeakLua>>,
10}
11
12impl<'r> LuaGuard<'r> {
13 fn new(lua: &mlua::Lua, store: &'r RefCell<Option<mlua::WeakLua>>) -> Self {
14 let weak = lua.weak();
15 let prev = store.replace(Some(weak));
16 Self { prev, store }
17 }
18
19 fn bind<R, F>(lua: &mlua::Lua, store: &'r RefCell<Option<mlua::WeakLua>>, f: F) -> R
20 where
21 F: FnOnce() -> R,
22 {
23 let _guard = Self::new(lua, store);
24 f()
25 }
26
27 fn with<R, F>(store: &'r RefCell<Option<mlua::WeakLua>>, f: F) -> Result<R, mlua::Error>
28 where
29 F: FnOnce(&mlua::Lua) -> Result<R, mlua::Error>,
30 {
31 let lua = store
34 .try_borrow()
35 .map_err(mlua::Error::runtime)?
36 .as_ref()
37 .ok_or_else(|| {
38 mlua::Error::runtime("`mlua::Lua` instance accessed outside of a render context")
39 })?
40 .try_upgrade()
41 .ok_or_else(|| mlua::Error::runtime("`mlua::Lua` instance is not available"))?;
42
43 f(&lua)
44 }
45}
46
47impl Drop for LuaGuard<'_> {
48 fn drop(&mut self) {
49 self.store.replace(self.prev.take());
50 }
51}
52
53pub fn bind_lua<R, F>(lua: &mlua::Lua, f: F) -> R
54where
55 F: FnOnce() -> R,
56{
57 LUA.with(|slot| LuaGuard::bind(lua, slot, f))
58}
59
60pub fn with_lua<R, F>(f: F) -> Result<R, mlua::Error>
61where
62 F: FnOnce(&mlua::Lua) -> Result<R, mlua::Error>,
63{
64 LUA.with(|store| LuaGuard::with(store, f))
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70
71 #[test]
72 fn with_outside_context() {
73 let err = with_lua(|_| Ok(()));
74
75 assert!(err.is_err());
76 assert!(
77 err.unwrap_err()
78 .to_string()
79 .contains("accessed outside of a render context")
80 );
81 }
82
83 #[test]
84 fn test_recursive_bind() {
85 let lua = mlua::Lua::new();
86
87 let res = bind_lua(&lua, || {
88 with_lua(|lua| bind_lua(lua, || with_lua(|lua| bind_lua(lua, || Ok(())))))
89 });
90
91 assert!(res.is_ok())
92 }
93
94 #[test]
95 fn test_drop_and_restore() {
96 let lua1 = &mlua::Lua::new();
97 let lua2 = &mlua::Lua::new();
98
99 let reg1 = lua1.create_registry_value(1).unwrap();
100 let reg2 = lua2.create_registry_value(2).unwrap();
101
102 bind_lua(lua1, || {
103 let res1 = with_lua(|lua1| Ok(lua1.owns_registry_value(®1))).unwrap();
104 assert!(res1);
105
106 let res2 = bind_lua(lua2, || {
107 with_lua(|lua2| Ok(lua2.owns_registry_value(®2)))
108 })
109 .unwrap();
110 assert!(res2);
111
112 let res1 = with_lua(|lua1| Ok(lua1.owns_registry_value(®1))).unwrap();
113 assert!(res1);
114 })
115 }
116
117 #[test]
118 fn test_panic_and_restore() {
119 let lua1 = &mlua::Lua::new();
120 let lua2 = &mlua::Lua::new();
121
122 let reg1 = lua1.create_registry_value(1).unwrap();
123
124 bind_lua(lua1, || {
125 let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
126 bind_lua(lua2, || {
127 with_lua(|_| -> Result<(), mlua::Error> { panic!("pow") }).unwrap();
128 });
129 }));
130
131 let res1 = with_lua(|lua1| Ok(lua1.owns_registry_value(®1))).unwrap();
132 assert!(res1);
133 })
134 }
135}