Skip to main content

luaur_require/functions/
resolve_require.rs

1use 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}