mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Some minor changes in Luau TextRequirer (comments, naming, etc)
This commit is contained in:
+48
-27
@@ -14,7 +14,7 @@ use crate::state::{callback_error_ext, Lua};
|
||||
use crate::table::Table;
|
||||
use crate::types::MaybeSend;
|
||||
|
||||
/// An error that can occur during navigation in the Luau `require` system.
|
||||
/// An error that can occur during navigation in the Luau `require-by-string` system.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -50,7 +50,7 @@ impl From<Error> for NavigateError {
|
||||
#[cfg(feature = "luau")]
|
||||
type WriteResult = ffi::luarequire_WriteResult;
|
||||
|
||||
/// A trait for handling modules loading and navigation in the Luau `require` system.
|
||||
/// A trait for handling modules loading and navigation in the Luau `require-by-string` system.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub trait Require: MaybeSend {
|
||||
@@ -103,16 +103,26 @@ impl fmt::Debug for dyn Require {
|
||||
}
|
||||
}
|
||||
|
||||
/// The standard implementation of Luau `require` navigation.
|
||||
#[doc(hidden)]
|
||||
/// The standard implementation of Luau `require-by-string` navigation.
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TextRequirer {
|
||||
/// An absolute path to the current Luau module (not mapped to a physical file)
|
||||
abs_path: PathBuf,
|
||||
/// A relative path to the current Luau module (not mapped to a physical file)
|
||||
rel_path: PathBuf,
|
||||
module_path: PathBuf,
|
||||
/// A physical path to the current Luau module, which is a file or a directory with an
|
||||
/// `init.lua(u)` file
|
||||
resolved_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl TextRequirer {
|
||||
/// The prefix used for chunk names in the require system.
|
||||
/// Only chunk names starting with this prefix are allowed to be used in `require`.
|
||||
const CHUNK_PREFIX: &str = "@";
|
||||
|
||||
/// The file extensions that are considered valid for Luau modules.
|
||||
const FILE_EXTENSIONS: &[&str] = &["luau", "lua"];
|
||||
|
||||
/// Creates a new `TextRequirer` instance.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
@@ -156,14 +166,17 @@ impl TextRequirer {
|
||||
components.into_iter().collect()
|
||||
}
|
||||
|
||||
fn find_module(path: &Path) -> StdResult<PathBuf, NavigateError> {
|
||||
/// Resolve a Luau module path to a physical file or directory.
|
||||
///
|
||||
/// Empty directories without init files are considered valid as "intermediate" directories.
|
||||
fn resolve_module(path: &Path) -> StdResult<Option<PathBuf>, NavigateError> {
|
||||
let mut found_path = None;
|
||||
|
||||
if path.components().next_back() != Some(Component::Normal("init".as_ref())) {
|
||||
let current_ext = (path.extension().and_then(|s| s.to_str()))
|
||||
.map(|s| format!("{s}."))
|
||||
.unwrap_or_default();
|
||||
for ext in ["luau", "lua"] {
|
||||
for ext in Self::FILE_EXTENSIONS {
|
||||
let candidate = path.with_extension(format!("{current_ext}{ext}"));
|
||||
if candidate.is_file() && found_path.replace(candidate).is_some() {
|
||||
return Err(NavigateError::Ambiguous);
|
||||
@@ -171,7 +184,7 @@ impl TextRequirer {
|
||||
}
|
||||
}
|
||||
if path.is_dir() {
|
||||
for component in ["init.luau", "init.lua"] {
|
||||
for component in Self::FILE_EXTENSIONS.iter().map(|ext| format!("init.{ext}")) {
|
||||
let candidate = path.join(component);
|
||||
if candidate.is_file() && found_path.replace(candidate).is_some() {
|
||||
return Err(NavigateError::Ambiguous);
|
||||
@@ -179,21 +192,22 @@ impl TextRequirer {
|
||||
}
|
||||
|
||||
if found_path.is_none() {
|
||||
found_path = Some(PathBuf::new());
|
||||
// Directories without init files are considered valid "intermediate" path
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
|
||||
found_path.ok_or(NavigateError::NotFound)
|
||||
Ok(Some(found_path.ok_or(NavigateError::NotFound)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl Require for TextRequirer {
|
||||
fn is_require_allowed(&self, chunk_name: &str) -> bool {
|
||||
chunk_name.starts_with('@')
|
||||
chunk_name.starts_with(Self::CHUNK_PREFIX)
|
||||
}
|
||||
|
||||
fn reset(&mut self, chunk_name: &str) -> StdResult<(), NavigateError> {
|
||||
if !chunk_name.starts_with('@') {
|
||||
if !chunk_name.starts_with(Self::CHUNK_PREFIX) {
|
||||
return Err(NavigateError::NotFound);
|
||||
}
|
||||
let chunk_name = Self::normalize_chunk_name(&chunk_name[1..]);
|
||||
@@ -205,24 +219,24 @@ impl Require for TextRequirer {
|
||||
let cwd = env::current_dir().map_err(|_| NavigateError::NotFound)?;
|
||||
self.abs_path = Self::normalize_path(&cwd.join(chunk_filename));
|
||||
self.rel_path = ([Component::CurDir, Component::Normal(chunk_filename)].into_iter()).collect();
|
||||
self.module_path = PathBuf::new();
|
||||
self.resolved_path = None;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if chunk_path.is_absolute() {
|
||||
let module_path = Self::find_module(&chunk_path)?;
|
||||
let resolved_path = Self::resolve_module(&chunk_path)?;
|
||||
self.abs_path = chunk_path.clone();
|
||||
self.rel_path = chunk_path;
|
||||
self.module_path = module_path;
|
||||
self.resolved_path = resolved_path;
|
||||
} else {
|
||||
// Relative path
|
||||
let cwd = env::current_dir().map_err(|_| NavigateError::NotFound)?;
|
||||
let abs_path = Self::normalize_path(&cwd.join(&chunk_path));
|
||||
let module_path = Self::find_module(&abs_path)?;
|
||||
let resolved_path = Self::resolve_module(&abs_path)?;
|
||||
self.abs_path = abs_path;
|
||||
self.rel_path = chunk_path;
|
||||
self.module_path = module_path;
|
||||
self.resolved_path = resolved_path;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -230,11 +244,11 @@ impl Require for TextRequirer {
|
||||
|
||||
fn jump_to_alias(&mut self, path: &str) -> StdResult<(), NavigateError> {
|
||||
let path = Self::normalize_path(path.as_ref());
|
||||
let module_path = Self::find_module(&path)?;
|
||||
let resolved_path = Self::resolve_module(&path)?;
|
||||
|
||||
self.abs_path = path.clone();
|
||||
self.rel_path = path;
|
||||
self.module_path = module_path;
|
||||
self.resolved_path = resolved_path;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -242,15 +256,18 @@ impl Require for TextRequirer {
|
||||
fn to_parent(&mut self) -> StdResult<(), NavigateError> {
|
||||
let mut abs_path = self.abs_path.clone();
|
||||
if !abs_path.pop() {
|
||||
// It's important to return `NotFound` if we reached the root, as it's a "recoverable" error if we
|
||||
// cannot go beyond the root directory.
|
||||
// Luau "require-by-string` has a special logic to search for config file to resolve aliases.
|
||||
return Err(NavigateError::NotFound);
|
||||
}
|
||||
let mut rel_parent = self.rel_path.clone();
|
||||
rel_parent.pop();
|
||||
let module_path = Self::find_module(&abs_path)?;
|
||||
let resolved_path = Self::resolve_module(&abs_path)?;
|
||||
|
||||
self.abs_path = abs_path;
|
||||
self.rel_path = Self::normalize_path(&rel_parent);
|
||||
self.module_path = module_path;
|
||||
self.resolved_path = resolved_path;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -258,21 +275,23 @@ impl Require for TextRequirer {
|
||||
fn to_child(&mut self, name: &str) -> StdResult<(), NavigateError> {
|
||||
let abs_path = self.abs_path.join(name);
|
||||
let rel_path = self.rel_path.join(name);
|
||||
let module_path = Self::find_module(&abs_path)?;
|
||||
let resolved_path = Self::resolve_module(&abs_path)?;
|
||||
|
||||
self.abs_path = abs_path;
|
||||
self.rel_path = rel_path;
|
||||
self.module_path = module_path;
|
||||
self.resolved_path = resolved_path;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn has_module(&self) -> bool {
|
||||
self.module_path.is_file()
|
||||
(self.resolved_path.as_deref())
|
||||
.map(Path::is_file)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn cache_key(&self) -> String {
|
||||
self.module_path.display().to_string()
|
||||
self.resolved_path.as_deref().unwrap().display().to_string()
|
||||
}
|
||||
|
||||
fn has_config(&self) -> bool {
|
||||
@@ -285,7 +304,9 @@ impl Require for TextRequirer {
|
||||
|
||||
fn loader(&self, lua: &Lua) -> Result<Function> {
|
||||
let name = format!("@{}", self.rel_path.display());
|
||||
lua.load(&*self.module_path).set_name(name).into_function()
|
||||
lua.load(self.resolved_path.as_deref().unwrap())
|
||||
.set_name(name)
|
||||
.into_function()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,7 +517,7 @@ unsafe fn write_to_buffer(
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
pub fn create_require_function<R: Require + 'static>(lua: &Lua, require: R) -> Result<Function> {
|
||||
pub(super) fn create_require_function<R: Require + 'static>(lua: &Lua, require: R) -> Result<Function> {
|
||||
unsafe extern "C-unwind" fn find_current_file(state: *mut ffi::lua_State) -> c_int {
|
||||
let mut ar: ffi::lua_Debug = mem::zeroed();
|
||||
for level in 2.. {
|
||||
|
||||
+2
-1
@@ -25,7 +25,8 @@ pub use crate::HookTriggers as LuaHookTriggers;
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{
|
||||
CompileConstant as LuaCompileConstant, CoverageInfo as LuaCoverageInfo,
|
||||
NavigateError as LuaNavigateError, Require as LuaRequire, Vector as LuaVector,
|
||||
NavigateError as LuaNavigateError, Require as LuaRequire, TextRequirer as LuaTextRequirer,
|
||||
Vector as LuaVector,
|
||||
};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
|
||||
Reference in New Issue
Block a user