Skip to main content

luaur_require/records/
navigation_context.rs

1use crate::enums::navigate_result::NavigateResult;
2use crate::functions::convert_navigate_result::convert_navigate_result;
3use crate::records::runtime_luau_config_timer::RuntimeLuauConfigTimer;
4use crate::records::runtime_navigation_context::RuntimeNavigationContext;
5use alloc::string::String;
6use alloc::{ffi::CString, rc::Rc};
7use core::ffi::{c_int, c_void};
8use core::option::Option;
9use luaur_vm::functions::lua_getthreaddata::lua_getthreaddata;
10use luaur_vm::functions::lua_l_error_l::lua_l_error_l;
11use luaur_vm::functions::lua_setthreaddata::lua_setthreaddata;
12use luaur_vm::type_aliases::lua_state::lua_State;
13
14pub use crate::enums::config_behavior::ConfigBehavior;
15pub use crate::enums::config_status::ConfigStatus;
16
17#[allow(non_camel_case_types)]
18pub struct NavigationContext {
19    pub(crate) luau_config_init: Option<alloc::boxed::Box<dyn Fn(*mut core::ffi::c_void)>>,
20    pub(crate) luau_config_interrupt:
21        Option<unsafe extern "C-unwind" fn(l: *mut core::ffi::c_void, gc: core::ffi::c_int)>,
22}
23
24impl NavigationContext {
25    pub const NavigateResult: () = ();
26    pub const ConfigBehavior: () = ();
27    pub const ConfigStatus: () = ();
28}
29
30pub trait NavigationContextTrait {
31    fn reset_to_requirer(&mut self) -> NavigateResult;
32    fn jump_to_alias(&mut self, path: &str) -> NavigateResult;
33
34    fn to_alias_override(&mut self, _alias_unprefixed: &str) -> NavigateResult {
35        NavigateResult::NotFound
36    }
37
38    fn to_alias_fallback(&mut self, _alias_unprefixed: &str) -> NavigateResult {
39        NavigateResult::NotFound
40    }
41
42    fn to_parent(&mut self) -> NavigateResult;
43    fn to_child(&mut self, component: &str) -> NavigateResult;
44
45    fn get_config_status(&self) -> ConfigStatus {
46        ConfigStatus::Absent
47    }
48
49    fn get_config_behavior(&self) -> ConfigBehavior {
50        ConfigBehavior::GetAlias
51    }
52
53    fn get_alias(&self, _alias: &str) -> Option<String> {
54        None
55    }
56
57    fn get_config(&self) -> Option<String> {
58        None
59    }
60
61    fn luau_config_init(&self) -> Option<Rc<dyn Fn(*mut lua_State)>> {
62        None
63    }
64
65    fn luau_config_interrupt(
66        &self,
67    ) -> Option<unsafe extern "C-unwind" fn(l: *mut lua_State, gc: c_int)> {
68        None
69    }
70}
71
72impl NavigationContextTrait for NavigationContext {
73    fn reset_to_requirer(&mut self) -> NavigateResult {
74        NavigateResult::NotFound
75    }
76
77    fn jump_to_alias(&mut self, _path: &str) -> NavigateResult {
78        NavigateResult::NotFound
79    }
80
81    fn to_parent(&mut self) -> NavigateResult {
82        NavigateResult::NotFound
83    }
84
85    fn to_child(&mut self, _component: &str) -> NavigateResult {
86        NavigateResult::NotFound
87    }
88}
89
90impl NavigationContextTrait for RuntimeNavigationContext {
91    fn reset_to_requirer(&mut self) -> NavigateResult {
92        unsafe {
93            if self.config.is_null() {
94                return NavigateResult::NotFound;
95            }
96
97            let Some(reset) = (*self.config).reset else {
98                return NavigateResult::NotFound;
99            };
100
101            let Ok(requirer_chunkname) = CString::new(self.requirer_chunkname.as_str()) else {
102                return NavigateResult::NotFound;
103            };
104
105            convert_navigate_result(reset(self.l, self.ctx, requirer_chunkname.as_ptr()) as i32)
106        }
107    }
108
109    fn jump_to_alias(&mut self, path: &str) -> NavigateResult {
110        unsafe {
111            if self.config.is_null() {
112                return NavigateResult::NotFound;
113            }
114
115            let Some(jump_to_alias) = (*self.config).jump_to_alias else {
116                return NavigateResult::NotFound;
117            };
118
119            let Ok(path) = CString::new(path) else {
120                return NavigateResult::NotFound;
121            };
122
123            convert_navigate_result(jump_to_alias(self.l, self.ctx, path.as_ptr()) as i32)
124        }
125    }
126
127    fn to_alias_override(&mut self, alias_unprefixed: &str) -> NavigateResult {
128        unsafe {
129            if self.config.is_null() {
130                return NavigateResult::NotFound;
131            }
132
133            let Some(to_alias_override) = (*self.config).to_alias_override else {
134                return NavigateResult::NotFound;
135            };
136
137            let Ok(alias_unprefixed) = CString::new(alias_unprefixed) else {
138                return NavigateResult::NotFound;
139            };
140
141            convert_navigate_result(
142                to_alias_override(self.l, self.ctx, alias_unprefixed.as_ptr()) as i32,
143            )
144        }
145    }
146
147    fn to_alias_fallback(&mut self, alias_unprefixed: &str) -> NavigateResult {
148        unsafe {
149            if self.config.is_null() {
150                return NavigateResult::NotFound;
151            }
152
153            let Some(to_alias_fallback) = (*self.config).to_alias_fallback else {
154                return NavigateResult::NotFound;
155            };
156
157            let Ok(alias_unprefixed) = CString::new(alias_unprefixed) else {
158                return NavigateResult::NotFound;
159            };
160
161            convert_navigate_result(
162                to_alias_fallback(self.l, self.ctx, alias_unprefixed.as_ptr()) as i32,
163            )
164        }
165    }
166
167    fn to_parent(&mut self) -> NavigateResult {
168        RuntimeNavigationContext::to_parent(self)
169    }
170
171    fn to_child(&mut self, component: &str) -> NavigateResult {
172        unsafe {
173            if self.config.is_null() {
174                return NavigateResult::NotFound;
175            }
176
177            let Some(to_child) = (*self.config).to_child else {
178                return NavigateResult::NotFound;
179            };
180
181            let Ok(component) = CString::new(component) else {
182                return NavigateResult::NotFound;
183            };
184
185            convert_navigate_result(to_child(self.l, self.ctx, component.as_ptr()) as i32)
186        }
187    }
188
189    fn get_config_status(&self) -> ConfigStatus {
190        RuntimeNavigationContext::get_config_status(self)
191    }
192
193    fn get_config_behavior(&self) -> ConfigBehavior {
194        RuntimeNavigationContext::get_config_behavior(self)
195    }
196
197    fn get_alias(&self, alias: &str) -> Option<String> {
198        RuntimeNavigationContext::get_alias(self, alias)
199    }
200
201    fn get_config(&self) -> Option<String> {
202        RuntimeNavigationContext::get_config(self)
203    }
204
205    fn luau_config_init(&self) -> Option<Rc<dyn Fn(*mut lua_State)>> {
206        let config = self.config;
207        let ctx = self.ctx;
208        let timer = core::ptr::addr_of!(self.timer) as *mut RuntimeLuauConfigTimer as usize;
209
210        Some(Rc::new(move |l: *mut lua_State| unsafe {
211            let timeout = if !config.is_null() {
212                if let Some(get_timeout) = (*config).get_luau_config_timeout {
213                    get_timeout(l as *mut c_void, ctx)
214                } else {
215                    2000
216                }
217            } else {
218                2000
219            };
220
221            let timer = timer as *mut RuntimeLuauConfigTimer;
222            (*timer).start(timeout);
223            lua_setthreaddata(l, timer as *mut c_void);
224        }))
225    }
226
227    fn luau_config_interrupt(
228        &self,
229    ) -> Option<unsafe extern "C-unwind" fn(l: *mut lua_State, gc: c_int)> {
230        Some(runtime_luau_config_interrupt)
231    }
232}
233
234unsafe extern "C-unwind" fn runtime_luau_config_interrupt(l: *mut lua_State, _gc: c_int) {
235    let timer = lua_getthreaddata(l) as *const RuntimeLuauConfigTimer;
236    if !timer.is_null() && (*timer).is_finished() {
237        lua_l_error_l(
238            l,
239            c"configuration execution timed out".as_ptr(),
240            format_args!("configuration execution timed out"),
241        );
242    }
243}