Skip to content

Commit 0b365e9

Browse files
committed
Support to_alias_override/to_alias_fallback in Require trait (Luau)
1 parent 15fb63b commit 0b365e9

2 files changed

Lines changed: 190 additions & 2 deletions

File tree

src/luau/require.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,24 @@ pub trait Require {
6666
/// configuration file.
6767
fn jump_to_alias(&mut self, path: &str) -> StdResult<(), NavigateError>;
6868

69+
/// Provides an initial alias override opportunity prior to searching for
70+
/// configuration files.
71+
///
72+
/// If `Ok(())` is returned, alias resolution stops here and the internal state
73+
/// must point at the aliased location.
74+
fn to_alias_override(&mut self, _alias: &str) -> StdResult<(), NavigateError> {
75+
Err(NavigateError::NotFound)
76+
}
77+
78+
/// Provides a final opportunity to resolve an alias if it cannot be found in
79+
/// configuration files.
80+
///
81+
/// If `Ok(())` is returned, alias resolution stops here and the internal state
82+
/// must point at the aliased location.
83+
fn to_alias_fallback(&mut self, _alias: &str) -> StdResult<(), NavigateError> {
84+
Err(NavigateError::NotFound)
85+
}
86+
6987
// Navigate to parent directory
7088
fn to_parent(&mut self) -> StdResult<(), NavigateError>;
7189

@@ -192,6 +210,30 @@ pub(super) unsafe extern "C-unwind" fn init_config(config: *mut ffi::luarequire_
192210
})
193211
}
194212

213+
unsafe extern "C-unwind" fn to_alias_override(
214+
state: *mut ffi::lua_State,
215+
ctx: *mut c_void,
216+
alias_unprefixed: *const c_char,
217+
) -> ffi::luarequire_NavigateResult {
218+
let mut this = try_borrow_mut!(state, ctx);
219+
let alias = CStr::from_ptr(alias_unprefixed).to_string_lossy();
220+
callback_error_ext(state, ptr::null_mut(), true, move |_, _| {
221+
this.to_alias_override(&alias).into_nav_result()
222+
})
223+
}
224+
225+
unsafe extern "C-unwind" fn to_alias_fallback(
226+
state: *mut ffi::lua_State,
227+
ctx: *mut c_void,
228+
alias_unprefixed: *const c_char,
229+
) -> ffi::luarequire_NavigateResult {
230+
let mut this = try_borrow_mut!(state, ctx);
231+
let alias = CStr::from_ptr(alias_unprefixed).to_string_lossy();
232+
callback_error_ext(state, ptr::null_mut(), true, move |_, _| {
233+
this.to_alias_fallback(&alias).into_nav_result()
234+
})
235+
}
236+
195237
unsafe extern "C-unwind" fn to_parent(
196238
state: *mut ffi::lua_State,
197239
ctx: *mut c_void,
@@ -298,8 +340,8 @@ pub(super) unsafe extern "C-unwind" fn init_config(config: *mut ffi::luarequire_
298340
(*config).is_require_allowed = is_require_allowed;
299341
(*config).reset = reset;
300342
(*config).jump_to_alias = jump_to_alias;
301-
(*config).to_alias_override = None;
302-
(*config).to_alias_fallback = None;
343+
(*config).to_alias_override = Some(to_alias_override);
344+
(*config).to_alias_fallback = Some(to_alias_fallback);
303345
(*config).to_parent = to_parent;
304346
(*config).to_child = to_child;
305347
(*config).is_module_present = is_module_present;

tests/luau/require.rs

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,152 @@ fn test_require_with_config_luau() {
251251
test_require_with_config_inner("with_config_luau");
252252
}
253253

254+
#[test]
255+
fn test_alias_override() {
256+
let lua = Lua::new();
257+
258+
struct OverrideRequire(FsRequirer);
259+
260+
impl Require for OverrideRequire {
261+
fn is_require_allowed(&self, chunk_name: &str) -> bool {
262+
self.0.is_require_allowed(chunk_name)
263+
}
264+
265+
fn reset(&mut self, chunk_name: &str) -> StdResult<(), NavigateError> {
266+
self.0.reset(chunk_name)
267+
}
268+
269+
fn jump_to_alias(&mut self, path: &str) -> StdResult<(), NavigateError> {
270+
self.0.jump_to_alias(path)
271+
}
272+
273+
fn to_alias_override(&mut self, alias: &str) -> StdResult<(), NavigateError> {
274+
if alias == "testoverride" {
275+
self.0.jump_to_alias("./tests/luau/require/without_config")
276+
} else {
277+
Err(NavigateError::NotFound)
278+
}
279+
}
280+
281+
fn to_parent(&mut self) -> StdResult<(), NavigateError> {
282+
self.0.to_parent()
283+
}
284+
285+
fn to_child(&mut self, name: &str) -> StdResult<(), NavigateError> {
286+
self.0.to_child(name)
287+
}
288+
289+
fn has_module(&self) -> bool {
290+
self.0.has_module()
291+
}
292+
293+
fn cache_key(&self) -> String {
294+
self.0.cache_key()
295+
}
296+
297+
fn has_config(&self) -> bool {
298+
self.0.has_config()
299+
}
300+
301+
fn config(&self) -> IoResult<Vec<u8>> {
302+
self.0.config()
303+
}
304+
305+
fn loader(&self, lua: &Lua) -> Result<mlua::Function> {
306+
self.0.loader(lua)
307+
}
308+
}
309+
310+
let require_fn = lua
311+
.create_require_function(OverrideRequire(FsRequirer::new()))
312+
.unwrap();
313+
lua.globals().set("require", require_fn).unwrap();
314+
315+
// to_alias_override intercepts before config-file search
316+
let res = run_require(&lua, "@testoverride/dependency").unwrap();
317+
assert_eq!("result from dependency", get_str(&res, 1));
318+
319+
// Different sub-path through the same alias
320+
let res = run_require(&lua, "@testoverride/module").unwrap();
321+
assert_eq!("required into module", get_str(&res, 2));
322+
323+
// Aliases not handled by the override still fail normally
324+
let res = run_require(&lua, "@unknown_alias_xyz/anything");
325+
assert!(res.is_err());
326+
assert!((res.unwrap_err().to_string()).contains("@unknown_alias_xyz is not a valid alias"));
327+
}
328+
329+
#[test]
330+
fn test_alias_fallback() {
331+
let lua = Lua::new();
332+
333+
struct FallbackRequire(FsRequirer);
334+
335+
impl Require for FallbackRequire {
336+
fn is_require_allowed(&self, chunk_name: &str) -> bool {
337+
self.0.is_require_allowed(chunk_name)
338+
}
339+
340+
fn reset(&mut self, chunk_name: &str) -> StdResult<(), NavigateError> {
341+
self.0.reset(chunk_name)
342+
}
343+
344+
fn jump_to_alias(&mut self, path: &str) -> StdResult<(), NavigateError> {
345+
self.0.jump_to_alias(path)
346+
}
347+
348+
fn to_alias_fallback(&mut self, alias: &str) -> StdResult<(), NavigateError> {
349+
if alias == "testfallback" {
350+
self.0.jump_to_alias("./tests/luau/require/without_config")
351+
} else {
352+
Err(NavigateError::NotFound)
353+
}
354+
}
355+
356+
fn to_parent(&mut self) -> StdResult<(), NavigateError> {
357+
self.0.to_parent()
358+
}
359+
360+
fn to_child(&mut self, name: &str) -> StdResult<(), NavigateError> {
361+
self.0.to_child(name)
362+
}
363+
364+
fn has_module(&self) -> bool {
365+
self.0.has_module()
366+
}
367+
368+
fn cache_key(&self) -> String {
369+
self.0.cache_key()
370+
}
371+
372+
fn has_config(&self) -> bool {
373+
self.0.has_config()
374+
}
375+
376+
fn config(&self) -> IoResult<Vec<u8>> {
377+
self.0.config()
378+
}
379+
380+
fn loader(&self, lua: &Lua) -> Result<mlua::Function> {
381+
self.0.loader(lua)
382+
}
383+
}
384+
385+
let require_fn = lua
386+
.create_require_function(FallbackRequire(FsRequirer::new()))
387+
.unwrap();
388+
lua.globals().set("require", require_fn).unwrap();
389+
390+
// to_alias_fallback catches after config-file search misses
391+
let res = run_require(&lua, "@testfallback/dependency").unwrap();
392+
assert_eq!("result from dependency", get_str(&res, 1));
393+
394+
// Aliases not handled by the fallback still fail
395+
let res = run_require(&lua, "@unknown_alias_xyz/anything");
396+
assert!(res.is_err());
397+
assert!((res.unwrap_err().to_string()).contains("@unknown_alias_xyz is not a valid alias"));
398+
}
399+
254400
#[cfg(all(feature = "async", not(windows)))]
255401
#[tokio::test]
256402
async fn test_async_require() -> Result<()> {

0 commit comments

Comments
 (0)