luaur_require/functions/
resolve_require.rs1use crate::enums::status_require_impl::Status as RequireStatus;
2use crate::enums::status_require_navigator::Status as NavigatorStatus;
3use crate::records::luarequire_configuration::luarequire_Configuration;
4use crate::records::navigator::Navigator;
5use crate::records::resolved_require::ResolvedRequire;
6use crate::records::runtime_error_handler::RuntimeErrorHandler;
7use crate::records::runtime_navigation_context::RuntimeNavigationContext;
8use alloc::ffi::CString;
9use alloc::string::String;
10use core::ffi::{c_char, c_int, c_void};
11use luaur_vm::enums::lua_type::lua_Type;
12use luaur_vm::functions::lua_getfield::lua_getfield;
13use luaur_vm::functions::lua_l_findtable::luaL_findtable;
14use luaur_vm::functions::lua_remove::lua_remove;
15use luaur_vm::functions::lua_type::lua_type;
16use luaur_vm::macros::lua_pop::lua_pop;
17use luaur_vm::macros::lua_registryindex::LUA_REGISTRYINDEX;
18use luaur_vm::records::lua_state::lua_State;
19
20const REQUIRED_CACHE_TABLE_KEY: *const c_char = c"_MODULES".as_ptr();
21
22pub fn resolve_require(
23 lrc: *mut luarequire_Configuration,
24 l: *mut lua_State,
25 ctx: *mut c_void,
26 requirer_chunkname: *const c_char,
27 path: String,
28) -> ResolvedRequire {
29 unsafe {
30 let Some(is_require_allowed) = lrc.as_ref().and_then(|config| config.is_require_allowed)
31 else {
32 return ResolvedRequire::from_error_message("require is not supported in this context");
33 };
34
35 if !is_require_allowed(l as *mut c_void, ctx, requirer_chunkname) {
36 return ResolvedRequire::from_error_message("require is not supported in this context");
37 }
38 }
39
40 let requirer_chunkname = unsafe {
41 if requirer_chunkname.is_null() {
42 String::new()
43 } else {
44 core::ffi::CStr::from_ptr(requirer_chunkname)
45 .to_string_lossy()
46 .into_owned()
47 }
48 };
49
50 let mut navigation_context =
51 RuntimeNavigationContext::new(lrc, l as *mut c_void, ctx, requirer_chunkname);
52 let mut error_handler = RuntimeErrorHandler::new(path.clone());
53 let mut navigator = Navigator::new(&mut navigation_context, &mut error_handler);
54
55 if navigator.navigate(path) == NavigatorStatus::ErrorReported {
56 return ResolvedRequire::resolved_require_from_error_handler(&error_handler);
57 }
58
59 if !navigation_context.is_module_present() {
60 return ResolvedRequire::from_error_message("no module present at resolved path");
61 }
62
63 let Some(cache_key) = navigation_context.get_cache_key() else {
64 return ResolvedRequire::from_error_message("could not get cache key for module");
65 };
66
67 unsafe {
68 luaL_findtable(l, LUA_REGISTRYINDEX, REQUIRED_CACHE_TABLE_KEY, 1);
69 let cache_key_c = CString::new(cache_key.as_str()).unwrap();
70 lua_getfield(l, -1, cache_key_c.as_ptr());
71 let cached = lua_type(l, -1) != lua_Type::LUA_TNIL as c_int;
72 lua_pop(l, 2);
73
74 if cached {
75 lua_getfield(l, LUA_REGISTRYINDEX, REQUIRED_CACHE_TABLE_KEY);
76 lua_getfield(l, -1, cache_key_c.as_ptr());
77 lua_remove(l, -2);
78
79 return ResolvedRequire {
80 status: RequireStatus::Cached,
81 chunkname: String::new(),
82 loadname: String::new(),
83 cacheKey: String::new(),
84 error: String::new(),
85 };
86 }
87 }
88
89 let Some(chunkname) = navigation_context.get_chunkname() else {
90 return ResolvedRequire::from_error_message("could not get chunkname for module");
91 };
92
93 let Some(loadname) = navigation_context.get_loadname() else {
94 return ResolvedRequire::from_error_message("could not get loadname for module");
95 };
96
97 ResolvedRequire {
98 status: RequireStatus::ModuleRead,
99 chunkname,
100 loadname,
101 cacheKey: cache_key,
102 error: String::new(),
103 }
104}