From ba2d7203f83e593429914414a4e6a00d1c5fd8cc Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 23 Aug 2024 00:26:39 +0330 Subject: [PATCH 01/35] luaurc rewrite --- crates/lune-std/src/globals/require/alias.rs | 73 ----- .../lune-std/src/globals/require/context.rs | 289 ------------------ .../lune-std/src/globals/require/library.rs | 14 - crates/lune-std/src/globals/require/mod.rs | 90 +----- crates/lune-std/src/globals/require/path.rs | 129 -------- crates/lune-std/src/lib.rs | 1 + crates/lune-std/src/luaurc.rs | 210 ++++--------- crates/lune-std/src/path.rs | 29 ++ 8 files changed, 96 insertions(+), 739 deletions(-) delete mode 100644 crates/lune-std/src/globals/require/alias.rs delete mode 100644 crates/lune-std/src/globals/require/context.rs delete mode 100644 crates/lune-std/src/globals/require/library.rs delete mode 100644 crates/lune-std/src/globals/require/path.rs create mode 100644 crates/lune-std/src/path.rs diff --git a/crates/lune-std/src/globals/require/alias.rs b/crates/lune-std/src/globals/require/alias.rs deleted file mode 100644 index 924056b7..00000000 --- a/crates/lune-std/src/globals/require/alias.rs +++ /dev/null @@ -1,73 +0,0 @@ -use mlua::prelude::*; - -use lune_utils::path::{clean_path_and_make_absolute, diff_path, get_current_dir}; - -use crate::luaurc::LuauRc; - -use super::context::*; - -pub(super) async fn require<'lua, 'ctx>( - lua: &'lua Lua, - ctx: &'ctx RequireContext, - source: &str, - alias: &str, - path: &str, -) -> LuaResult> -where - 'lua: 'ctx, -{ - let alias = alias.to_ascii_lowercase(); - - let parent = clean_path_and_make_absolute(source) - .parent() - .expect("how did a root path end up here..") - .to_path_buf(); - - // Try to gather the first luaurc and / or error we - // encounter to display better error messages to users - let mut first_luaurc = None; - let mut first_error = None; - let predicate = |rc: &LuauRc| { - if first_luaurc.is_none() { - first_luaurc.replace(rc.clone()); - } - if let Err(e) = rc.validate() { - if first_error.is_none() { - first_error.replace(e); - } - false - } else { - rc.find_alias(&alias).is_some() - } - }; - - // Try to find a luaurc that contains the alias we're searching for - let luaurc = LuauRc::read_recursive(parent, predicate) - .await - .ok_or_else(|| { - if let Some(error) = first_error { - LuaError::runtime(format!("error while parsing .luaurc file: {error}")) - } else if let Some(luaurc) = first_luaurc { - LuaError::runtime(format!( - "failed to find alias '{alias}' - known aliases:\n{}", - luaurc - .aliases() - .iter() - .map(|(name, path)| format!(" {name} > {path}")) - .collect::>() - .join("\n") - )) - } else { - LuaError::runtime(format!("failed to find alias '{alias}' (no .luaurc)")) - } - })?; - - // We now have our aliased path, our path require function just needs it - // in a slightly different format with both absolute + relative to cwd - let abs_path = luaurc.find_alias(&alias).unwrap().join(path); - let rel_path = diff_path(&abs_path, get_current_dir()).ok_or_else(|| { - LuaError::runtime(format!("failed to find relative path for alias '{alias}'")) - })?; - - super::path::require_abs_rel(lua, ctx, abs_path, rel_path).await -} diff --git a/crates/lune-std/src/globals/require/context.rs b/crates/lune-std/src/globals/require/context.rs deleted file mode 100644 index 0355d270..00000000 --- a/crates/lune-std/src/globals/require/context.rs +++ /dev/null @@ -1,289 +0,0 @@ -use std::{ - collections::HashMap, - path::{Path, PathBuf}, - sync::Arc, -}; - -use mlua::prelude::*; -use mlua_luau_scheduler::LuaSchedulerExt; - -use tokio::{ - fs::read, - sync::{ - broadcast::{self, Sender}, - Mutex as AsyncMutex, - }, -}; - -use lune_utils::path::{clean_path, clean_path_and_make_absolute}; - -use crate::library::LuneStandardLibrary; - -/** - Context containing cached results for all `require` operations. - - The cache uses absolute paths, so any given relative - path will first be transformed into an absolute path. -*/ -#[derive(Debug, Clone)] -pub(super) struct RequireContext { - libraries: Arc>>>, - results: Arc>>>, - pending: Arc>>>, -} - -impl RequireContext { - /** - Creates a new require context for the given [`Lua`] struct. - - Note that this require context is global and only one require - context should be created per [`Lua`] struct, creating more - than one context may lead to undefined require-behavior. - */ - pub fn new() -> Self { - Self { - libraries: Arc::new(AsyncMutex::new(HashMap::new())), - results: Arc::new(AsyncMutex::new(HashMap::new())), - pending: Arc::new(AsyncMutex::new(HashMap::new())), - } - } - - /** - Resolves the given `source` and `path` into require paths - to use, based on the current require context settings. - - This will resolve path segments such as `./`, `../`, ..., and - if the resolved path is not an absolute path, will create an - absolute path by prepending the current working directory. - */ - pub fn resolve_paths( - source: impl AsRef, - path: impl AsRef, - ) -> LuaResult<(PathBuf, PathBuf)> { - let path = PathBuf::from(source.as_ref()) - .parent() - .ok_or_else(|| LuaError::runtime("Failed to get parent path of source"))? - .join(path.as_ref()); - - let abs_path = clean_path_and_make_absolute(&path); - let rel_path = clean_path(path); - - Ok((abs_path, rel_path)) - } - - /** - Checks if the given path has a cached require result. - */ - pub fn is_cached(&self, abs_path: impl AsRef) -> LuaResult { - let is_cached = self - .results - .try_lock() - .expect("RequireContext may not be used from multiple threads") - .contains_key(abs_path.as_ref()); - Ok(is_cached) - } - - /** - Checks if the given path is currently being used in `require`. - */ - pub fn is_pending(&self, abs_path: impl AsRef) -> LuaResult { - let is_pending = self - .pending - .try_lock() - .expect("RequireContext may not be used from multiple threads") - .contains_key(abs_path.as_ref()); - Ok(is_pending) - } - - /** - Gets the resulting value from the require cache. - - Will panic if the path has not been cached, use [`is_cached`] first. - */ - pub fn get_from_cache<'lua>( - &self, - lua: &'lua Lua, - abs_path: impl AsRef, - ) -> LuaResult> { - let results = self - .results - .try_lock() - .expect("RequireContext may not be used from multiple threads"); - - let cached = results - .get(abs_path.as_ref()) - .expect("Path does not exist in results cache"); - match cached { - Err(e) => Err(e.clone()), - Ok(k) => { - let multi_vec = lua - .registry_value::>(k) - .expect("Missing require result in lua registry"); - Ok(LuaMultiValue::from_vec(multi_vec)) - } - } - } - - /** - Waits for the resulting value from the require cache. - - Will panic if the path has not been cached, use [`is_cached`] first. - */ - pub async fn wait_for_cache<'lua>( - &self, - lua: &'lua Lua, - abs_path: impl AsRef, - ) -> LuaResult> { - let mut thread_recv = { - let pending = self - .pending - .try_lock() - .expect("RequireContext may not be used from multiple threads"); - let thread_id = pending - .get(abs_path.as_ref()) - .expect("Path is not currently pending require"); - thread_id.subscribe() - }; - - thread_recv.recv().await.into_lua_err()?; - - self.get_from_cache(lua, abs_path.as_ref()) - } - - async fn load<'lua>( - &self, - lua: &'lua Lua, - abs_path: impl AsRef, - rel_path: impl AsRef, - ) -> LuaResult { - let abs_path = abs_path.as_ref(); - let rel_path = rel_path.as_ref(); - - // Read the file at the given path, try to parse and - // load it into a new lua thread that we can schedule - let file_contents = read(&abs_path).await?; - let file_thread = lua - .load(file_contents) - .set_name(rel_path.to_string_lossy().to_string()); - - // Schedule the thread to run, wait for it to finish running - let thread_id = lua.push_thread_back(file_thread, ())?; - lua.track_thread(thread_id); - lua.wait_for_thread(thread_id).await; - let thread_res = lua.get_thread_result(thread_id).unwrap(); - - // Return the result of the thread, storing any lua value(s) in the registry - match thread_res { - Err(e) => Err(e), - Ok(v) => { - let multi_vec = v.into_vec(); - let multi_key = lua - .create_registry_value(multi_vec) - .expect("Failed to store require result in registry - out of memory"); - Ok(multi_key) - } - } - } - - /** - Loads (requires) the file at the given path. - */ - pub async fn load_with_caching<'lua>( - &self, - lua: &'lua Lua, - abs_path: impl AsRef, - rel_path: impl AsRef, - ) -> LuaResult> { - let abs_path = abs_path.as_ref(); - let rel_path = rel_path.as_ref(); - - // Set this abs path as currently pending - let (broadcast_tx, _) = broadcast::channel(1); - self.pending - .try_lock() - .expect("RequireContext may not be used from multiple threads") - .insert(abs_path.to_path_buf(), broadcast_tx); - - // Try to load at this abs path - let load_res = self.load(lua, abs_path, rel_path).await; - let load_val = match &load_res { - Err(e) => Err(e.clone()), - Ok(k) => { - let multi_vec = lua - .registry_value::>(k) - .expect("Failed to fetch require result from registry"); - Ok(LuaMultiValue::from_vec(multi_vec)) - } - }; - - // NOTE: We use the async lock and not try_lock here because - // some other thread may be wanting to insert into the require - // cache at the same time, and that's not an actual error case - self.results - .lock() - .await - .insert(abs_path.to_path_buf(), load_res); - - // Remove the pending thread id from the require context, - // broadcast a message to let any listeners know that this - // path has now finished the require process and is cached - let broadcast_tx = self - .pending - .try_lock() - .expect("RequireContext may not be used from multiple threads") - .remove(abs_path) - .expect("Pending require broadcaster was unexpectedly removed"); - broadcast_tx.send(()).ok(); - - load_val - } - - /** - Loads (requires) the library with the given name. - */ - pub fn load_library<'lua>( - &self, - lua: &'lua Lua, - name: impl AsRef, - ) -> LuaResult> { - let library: LuneStandardLibrary = match name.as_ref().parse() { - Err(e) => return Err(LuaError::runtime(e)), - Ok(b) => b, - }; - - let mut cache = self - .libraries - .try_lock() - .expect("RequireContext may not be used from multiple threads"); - - if let Some(res) = cache.get(&library) { - return match res { - Err(e) => return Err(e.clone()), - Ok(key) => { - let multi_vec = lua - .registry_value::>(key) - .expect("Missing library result in lua registry"); - Ok(LuaMultiValue::from_vec(multi_vec)) - } - }; - }; - - let result = library.module(lua); - - cache.insert( - library, - match result.clone() { - Err(e) => Err(e), - Ok(multi) => { - let multi_vec = multi.into_vec(); - let multi_key = lua - .create_registry_value(multi_vec) - .expect("Failed to store require result in registry - out of memory"); - Ok(multi_key) - } - }, - ); - - result - } -} diff --git a/crates/lune-std/src/globals/require/library.rs b/crates/lune-std/src/globals/require/library.rs deleted file mode 100644 index b47ea928..00000000 --- a/crates/lune-std/src/globals/require/library.rs +++ /dev/null @@ -1,14 +0,0 @@ -use mlua::prelude::*; - -use super::context::*; - -pub(super) fn require<'lua, 'ctx>( - lua: &'lua Lua, - ctx: &'ctx RequireContext, - name: &str, -) -> LuaResult> -where - 'lua: 'ctx, -{ - ctx.load_library(lua, name) -} diff --git a/crates/lune-std/src/globals/require/mod.rs b/crates/lune-std/src/globals/require/mod.rs index 3876e363..eb0f36c4 100644 --- a/crates/lune-std/src/globals/require/mod.rs +++ b/crates/lune-std/src/globals/require/mod.rs @@ -1,93 +1,5 @@ use mlua::prelude::*; -use lune_utils::TableBuilder; - -mod context; -use context::RequireContext; - -mod alias; -mod library; -mod path; - -const REQUIRE_IMPL: &str = r" -return require(source(), ...) -"; - pub fn create(lua: &Lua) -> LuaResult { - lua.set_app_data(RequireContext::new()); - - /* - Require implementation needs a few workarounds: - - - Async functions run outside of the lua resumption cycle, - so the current lua thread, as well as its stack/debug info - is not available, meaning we have to use a normal function - - - Using the async require function directly in another lua function - would mean yielding across the metamethod/c-call boundary, meaning - we have to first load our two functions into a normal lua chunk - and then load that new chunk into our final require function - - Also note that we inspect the stack at level 2: - - 1. The current c / rust function - 2. The wrapper lua chunk defined above - 3. The lua chunk we are require-ing from - */ - - let require_fn = lua.create_async_function(require)?; - let get_source_fn = lua.create_function(move |lua, (): ()| match lua.inspect_stack(2) { - None => Err(LuaError::runtime( - "Failed to get stack info for require source", - )), - Some(info) => match info.source().source { - None => Err(LuaError::runtime( - "Stack info is missing source for require", - )), - Some(source) => lua.create_string(source.as_bytes()), - }, - })?; - - let require_env = TableBuilder::new(lua)? - .with_value("source", get_source_fn)? - .with_value("require", require_fn)? - .build_readonly()?; - - lua.load(REQUIRE_IMPL) - .set_name("require") - .set_environment(require_env) - .into_function()? - .into_lua(lua) -} - -async fn require<'lua>( - lua: &'lua Lua, - (source, path): (LuaString<'lua>, LuaString<'lua>), -) -> LuaResult> { - let source = source - .to_str() - .into_lua_err() - .context("Failed to parse require source as string")? - .to_string(); - - let path = path - .to_str() - .into_lua_err() - .context("Failed to parse require path as string")? - .to_string(); - - let context = lua - .app_data_ref() - .expect("Failed to get RequireContext from app data"); - - if let Some(builtin_name) = path.strip_prefix("@lune/").map(str::to_ascii_lowercase) { - library::require(lua, &context, &builtin_name) - } else if let Some(aliased_path) = path.strip_prefix('@') { - let (alias, path) = aliased_path.split_once('/').ok_or(LuaError::runtime( - "Require with custom alias must contain '/' delimiter", - ))?; - alias::require(lua, &context, &source, alias, path).await - } else { - path::require(lua, &context, &source, &path).await - } + todo!() } diff --git a/crates/lune-std/src/globals/require/path.rs b/crates/lune-std/src/globals/require/path.rs deleted file mode 100644 index 1fabebf4..00000000 --- a/crates/lune-std/src/globals/require/path.rs +++ /dev/null @@ -1,129 +0,0 @@ -use std::path::{Path, PathBuf}; - -use mlua::prelude::*; -use mlua::Error::ExternalError; - -use super::context::*; - -pub(super) async fn require<'lua, 'ctx>( - lua: &'lua Lua, - ctx: &'ctx RequireContext, - source: &str, - path: &str, -) -> LuaResult> -where - 'lua: 'ctx, -{ - let (abs_path, rel_path) = RequireContext::resolve_paths(source, path)?; - require_abs_rel(lua, ctx, abs_path, rel_path).await -} - -pub(super) async fn require_abs_rel<'lua, 'ctx>( - lua: &'lua Lua, - ctx: &'ctx RequireContext, - abs_path: PathBuf, // Absolute to filesystem - rel_path: PathBuf, // Relative to CWD (for displaying) -) -> LuaResult> -where - 'lua: 'ctx, -{ - // 1. Try to require the exact path - match require_inner(lua, ctx, &abs_path, &rel_path).await { - Ok(res) => return Ok(res), - Err(err) => { - if !is_file_not_found_error(&err) { - return Err(err); - } - } - } - - // 2. Try to require the path with an added "luau" extension - // 3. Try to require the path with an added "lua" extension - for extension in ["luau", "lua"] { - match require_inner( - lua, - ctx, - &append_extension(&abs_path, extension), - &append_extension(&rel_path, extension), - ) - .await - { - Ok(res) => return Ok(res), - Err(err) => { - if !is_file_not_found_error(&err) { - return Err(err); - } - } - } - } - - // We didn't find any direct file paths, look - // for directories with "init" files in them... - let abs_init = abs_path.join("init"); - let rel_init = rel_path.join("init"); - - // 4. Try to require the init path with an added "luau" extension - // 5. Try to require the init path with an added "lua" extension - for extension in ["luau", "lua"] { - match require_inner( - lua, - ctx, - &append_extension(&abs_init, extension), - &append_extension(&rel_init, extension), - ) - .await - { - Ok(res) => return Ok(res), - Err(err) => { - if !is_file_not_found_error(&err) { - return Err(err); - } - } - } - } - - // Nothing left to try, throw an error - Err(LuaError::runtime(format!( - "No file exists at the path '{}'", - rel_path.display() - ))) -} - -async fn require_inner<'lua, 'ctx>( - lua: &'lua Lua, - ctx: &'ctx RequireContext, - abs_path: impl AsRef, - rel_path: impl AsRef, -) -> LuaResult> -where - 'lua: 'ctx, -{ - let abs_path = abs_path.as_ref(); - let rel_path = rel_path.as_ref(); - - if ctx.is_cached(abs_path)? { - ctx.get_from_cache(lua, abs_path) - } else if ctx.is_pending(abs_path)? { - ctx.wait_for_cache(lua, &abs_path).await - } else { - ctx.load_with_caching(lua, &abs_path, &rel_path).await - } -} - -fn append_extension(path: impl Into, ext: &'static str) -> PathBuf { - let mut new = path.into(); - match new.extension() { - // FUTURE: There's probably a better way to do this than converting to a lossy string - Some(e) => new.set_extension(format!("{}.{ext}", e.to_string_lossy())), - None => new.set_extension(ext), - }; - new -} - -fn is_file_not_found_error(err: &LuaError) -> bool { - if let ExternalError(err) = err { - err.as_ref().downcast_ref::().is_some() - } else { - false - } -} diff --git a/crates/lune-std/src/lib.rs b/crates/lune-std/src/lib.rs index a29bef03..8de5d4aa 100644 --- a/crates/lune-std/src/lib.rs +++ b/crates/lune-std/src/lib.rs @@ -6,6 +6,7 @@ mod global; mod globals; mod library; mod luaurc; +mod path; pub use self::global::LuneStandardGlobal; pub use self::globals::version::set_global_version; diff --git a/crates/lune-std/src/luaurc.rs b/crates/lune-std/src/luaurc.rs index 0eada593..ee2a1388 100644 --- a/crates/lune-std/src/luaurc.rs +++ b/crates/lune-std/src/luaurc.rs @@ -1,164 +1,84 @@ +use crate::path::get_parent_path; +use mlua::ExternalResult; +use serde::Deserialize; use std::{ collections::HashMap, - path::{Path, PathBuf, MAIN_SEPARATOR}, - sync::Arc, + env::current_dir, + path::{Path, PathBuf}, }; +use tokio::fs; -use serde::{Deserialize, Serialize}; -use serde_json::Value as JsonValue; -use tokio::fs::read; - -use lune_utils::path::{clean_path, clean_path_and_make_absolute}; - -const LUAURC_FILE: &str = ".luaurc"; - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "lowercase")] -enum LuauLanguageMode { - NoCheck, - NonStrict, - Strict, -} - -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -struct LuauRcConfig { - #[serde(skip_serializing_if = "Option::is_none")] - language_mode: Option, - #[serde(skip_serializing_if = "Option::is_none")] - lint: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - lint_errors: Option, - #[serde(skip_serializing_if = "Option::is_none")] - type_errors: Option, - #[serde(skip_serializing_if = "Option::is_none")] - globals: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - paths: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - aliases: Option>, +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct RequireAlias<'a> { + pub alias: &'a str, + pub path: &'a str, } -/** - A deserialized `.luaurc` file. - - Contains utility methods for validating and searching for aliases. -*/ -#[derive(Debug, Clone)] -pub struct LuauRc { - dir: Arc, - config: LuauRcConfig, +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct Luaurc { + aliases: Option>, } -impl LuauRc { - /** - Reads a `.luaurc` file from the given directory. +/// Parses path into `RequireAlias` struct +/// +/// ### Examples +/// +/// `@lune/task` becomes `Some({ alias: "lune", path: "task" })` +/// +/// `../path/script` becomes `None` +pub fn path_to_alias(path: &Path) -> Result, mlua::Error> { + if let Some(aliased_path) = path + .to_str() + .ok_or(mlua::Error::runtime("Couldn't turn path into string"))? + .strip_prefix('@') + { + let (alias, path) = aliased_path.split_once('/').ok_or(mlua::Error::runtime( + "Require with alias doesn't contain '/'", + ))?; - If the file does not exist, or if it is invalid, this function returns `None`. - */ - pub async fn read(dir: impl AsRef) -> Option { - let dir = clean_path_and_make_absolute(dir); - let path = dir.join(LUAURC_FILE); - let bytes = read(&path).await.ok()?; - let config = serde_json::from_slice(&bytes).ok()?; - Some(Self { - dir: dir.into(), - config, - }) + Ok(Some(RequireAlias { alias, path })) + } else { + Ok(None) } +} - /** - Reads a `.luaurc` file from the given directory, and then recursively searches - for a `.luaurc` file in the parent directories if a predicate is not satisfied. - - If no `.luaurc` file exists, or if they are invalid, this function returns `None`. - */ - pub async fn read_recursive( - dir: impl AsRef, - mut predicate: impl FnMut(&Self) -> bool, - ) -> Option { - let mut current = clean_path_and_make_absolute(dir); - loop { - if let Some(rc) = Self::read(¤t).await { - if predicate(&rc) { - return Some(rc); - } - } - if let Some(parent) = current.parent() { - current = parent.to_path_buf(); - } else { - return None; - } - } +async fn parse_luaurc(_: &mlua::Lua, path: &PathBuf) -> Result, mlua::Error> { + if fs::try_exists(path).await? { + let content = fs::read(path).await?; + serde_json::from_slice(&content).map(Some).into_lua_err() + } else { + Ok(None) } +} - /** - Validates that the `.luaurc` file is correct. - - This primarily validates aliases since they are not - validated during creation of the [`LuauRc`] struct. - - # Errors - - If an alias key is invalid. - */ - pub fn validate(&self) -> Result<(), String> { - if let Some(aliases) = &self.config.aliases { - for alias in aliases.keys() { - if !is_valid_alias_key(alias) { - return Err(format!("invalid alias key: {alias}")); +/// Searches for .luaurc recursively +/// until an alias for the provided `RequireAlias` is found +pub async fn resolve_require_alias<'lua>( + lua: &'lua mlua::Lua, + alias: &'lua RequireAlias<'lua>, +) -> Result { + let cwd = current_dir()?; + let parent = cwd.join(get_parent_path(lua)?); + let ancestors = parent.ancestors(); + + for path in ancestors { + if path.starts_with(&cwd) { + if let Some(luaurc) = parse_luaurc(lua, &parent.join(".luaurc")).await? { + if let Some(aliases) = luaurc.aliases { + if let Some(alias_path) = aliases.get(alias.alias) { + let resolved = path.join(alias_path.join(alias.path)); + + return Ok(resolved); + } } } + } else { + break; } - Ok(()) - } - - /** - Gets a copy of all aliases in the `.luaurc` file. - - Will return an empty map if there are no aliases. - */ - #[must_use] - pub fn aliases(&self) -> HashMap { - self.config.aliases.clone().unwrap_or_default() - } - - /** - Finds an alias in the `.luaurc` file by name. - - If the alias does not exist, this function returns `None`. - */ - #[must_use] - pub fn find_alias(&self, name: &str) -> Option { - self.config.aliases.as_ref().and_then(|aliases| { - aliases.iter().find_map(|(alias, path)| { - if alias - .trim_end_matches(MAIN_SEPARATOR) - .eq_ignore_ascii_case(name) - && is_valid_alias_key(alias) - { - Some(clean_path(self.dir.join(path))) - } else { - None - } - }) - }) - } -} - -fn is_valid_alias_key(alias: impl AsRef) -> bool { - let alias = alias.as_ref(); - if alias.is_empty() - || alias.starts_with('.') - || alias.starts_with("..") - || alias.chars().any(|c| c == MAIN_SEPARATOR) - { - false // Paths are not valid alias keys - } else { - alias.chars().all(is_valid_alias_char) } -} -fn is_valid_alias_char(c: char) -> bool { - c.is_ascii_alphanumeric() || c == '-' || c == '_' || c == '.' + Err(mlua::Error::runtime(format!( + "Coudln't find the alias '{}' in any .luaurc file", + alias.alias + ))) } diff --git a/crates/lune-std/src/path.rs b/crates/lune-std/src/path.rs new file mode 100644 index 00000000..e35f9f56 --- /dev/null +++ b/crates/lune-std/src/path.rs @@ -0,0 +1,29 @@ +use std::path::PathBuf; + +pub fn get_script_path(lua: &mlua::Lua) -> Result { + let Some(debug) = lua.inspect_stack(2) else { + return Err(mlua::Error::runtime("Failed to inspect stack")); + }; + + match debug + .source() + .source + .map(|raw_source| PathBuf::from(raw_source.to_string())) + { + Some(script) => Ok(script), + None => Err(mlua::Error::runtime( + "Failed to get path of the script that called require", + )), + } +} + +pub fn get_parent_path(lua: &mlua::Lua) -> Result { + let script = get_script_path(lua)?; + + match script.parent() { + Some(parent) => Ok(parent.to_path_buf()), + None => Err(mlua::Error::runtime( + "Failed to get parent of the script that called require", + )), + } +} From 3f709f1e96fd55ff34d6f8e5d1836faf72de1492 Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 23 Aug 2024 00:46:59 +0330 Subject: [PATCH 02/35] resolve require path --- crates/lune-std/src/globals/require/mod.rs | 35 +++++++++++++++++++++- crates/lune-std/src/luaurc.rs | 2 +- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/crates/lune-std/src/globals/require/mod.rs b/crates/lune-std/src/globals/require/mod.rs index eb0f36c4..a04382ad 100644 --- a/crates/lune-std/src/globals/require/mod.rs +++ b/crates/lune-std/src/globals/require/mod.rs @@ -1,5 +1,38 @@ +use std::path::PathBuf; + use mlua::prelude::*; +use crate::{luaurc::path_to_alias, path::get_parent_path}; + +pub async fn lua_require(lua: &Lua, path: String) -> LuaResult { + let require_path_rel = PathBuf::from(path); + let require_alias = path_to_alias(&require_path_rel)?; + + if let Some(require_alias) = require_alias { + if require_alias.alias == "lune" { + Err(LuaError::runtime(format!( + "Tried requiring a lune library '{}'\nbut aliases are not implemented yet.", + require_alias.path, + ))) + } else { + Err(LuaError::runtime(format!( + "Tried requiring a custom alias '{}'\nbut aliases are not implemented yet.", + require_alias.alias, + ))) + } + } else { + let parent_path = get_parent_path(lua)?; + let require_path_abs = parent_path.join(require_path_rel); + + Err(LuaError::runtime(format!( + "Tried requiring '{}'\nbut requires are not implemented yet.", + require_path_abs.to_string_lossy(), + ))) + } +} + pub fn create(lua: &Lua) -> LuaResult { - todo!() + let f = lua.create_async_function(lua_require)?; + + f.into_lua(lua) } diff --git a/crates/lune-std/src/luaurc.rs b/crates/lune-std/src/luaurc.rs index ee2a1388..c4fb6551 100644 --- a/crates/lune-std/src/luaurc.rs +++ b/crates/lune-std/src/luaurc.rs @@ -26,7 +26,7 @@ pub struct Luaurc { /// `@lune/task` becomes `Some({ alias: "lune", path: "task" })` /// /// `../path/script` becomes `None` -pub fn path_to_alias(path: &Path) -> Result, mlua::Error> { +pub fn path_to_alias(path: &Path) -> Result>, mlua::Error> { if let Some(aliased_path) = path .to_str() .ok_or(mlua::Error::runtime("Couldn't turn path into string"))? From 2b7bb8903798b53fe74f66006129d5cbd45b9cdd Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 23 Aug 2024 00:50:05 +0330 Subject: [PATCH 03/35] StandardLibrary trait --- crates/lune-std/src/library.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/crates/lune-std/src/library.rs b/crates/lune-std/src/library.rs index 9a301f57..5c856b49 100644 --- a/crates/lune-std/src/library.rs +++ b/crates/lune-std/src/library.rs @@ -2,6 +2,17 @@ use std::str::FromStr; use mlua::prelude::*; +pub trait StandardLibrary +where + Self: Sized + 'static + FromStr, +{ + const ALL: &'static [Self]; + + fn name(&self) -> &'static str; + + fn module<'lua>(&self, lua: &'lua Lua) -> LuaResult>; +} + /** A standard library provided by Lune. */ @@ -20,12 +31,12 @@ pub enum LuneStandardLibrary { #[cfg(feature = "roblox")] Roblox, } -impl LuneStandardLibrary { +impl StandardLibrary for LuneStandardLibrary { /** All available standard libraries. */ #[rustfmt::skip] - pub const ALL: &'static [Self] = &[ + const ALL: &'static [Self] = &[ #[cfg(feature = "datetime")] Self::DateTime, #[cfg(feature = "fs")] Self::Fs, #[cfg(feature = "luau")] Self::Luau, @@ -44,7 +55,7 @@ impl LuneStandardLibrary { #[must_use] #[rustfmt::skip] #[allow(unreachable_patterns)] - pub fn name(&self) -> &'static str { + fn name(&self) -> &'static str { match self { #[cfg(feature = "datetime")] Self::DateTime => "datetime", #[cfg(feature = "fs")] Self::Fs => "fs", @@ -70,7 +81,7 @@ impl LuneStandardLibrary { */ #[rustfmt::skip] #[allow(unreachable_patterns)] - pub fn module<'lua>(&self, lua: &'lua Lua) -> LuaResult> { + fn module<'lua>(&self, lua: &'lua Lua) -> LuaResult> { let res: LuaResult = match self { #[cfg(feature = "datetime")] Self::DateTime => lune_std_datetime::module(lua), #[cfg(feature = "fs")] Self::Fs => lune_std_fs::module(lua), From 12b87b30650bc75e505739cc40d36078b60ace45 Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 23 Aug 2024 01:11:41 +0330 Subject: [PATCH 04/35] make standardlibrary object safe --- crates/lune-std/src/globals/require/mod.rs | 6 ++-- .../lune-std/src/globals/require/storage.rs | 6 ++++ crates/lune-std/src/library.rs | 31 ++----------------- 3 files changed, 11 insertions(+), 32 deletions(-) create mode 100644 crates/lune-std/src/globals/require/storage.rs diff --git a/crates/lune-std/src/globals/require/mod.rs b/crates/lune-std/src/globals/require/mod.rs index a04382ad..c4eee70f 100644 --- a/crates/lune-std/src/globals/require/mod.rs +++ b/crates/lune-std/src/globals/require/mod.rs @@ -1,8 +1,8 @@ -use std::path::PathBuf; - +use crate::{luaurc::path_to_alias, path::get_parent_path}; use mlua::prelude::*; +use std::path::PathBuf; -use crate::{luaurc::path_to_alias, path::get_parent_path}; +pub mod storage; pub async fn lua_require(lua: &Lua, path: String) -> LuaResult { let require_path_rel = PathBuf::from(path); diff --git a/crates/lune-std/src/globals/require/storage.rs b/crates/lune-std/src/globals/require/storage.rs new file mode 100644 index 00000000..8c1eda95 --- /dev/null +++ b/crates/lune-std/src/globals/require/storage.rs @@ -0,0 +1,6 @@ +use crate::library::StandardLibrary; +use std::collections::HashMap; + +pub struct RequireStorage<'a> { + stds: HashMap<&'a str, Box>, +} diff --git a/crates/lune-std/src/library.rs b/crates/lune-std/src/library.rs index 5c856b49..96961c0b 100644 --- a/crates/lune-std/src/library.rs +++ b/crates/lune-std/src/library.rs @@ -2,12 +2,7 @@ use std::str::FromStr; use mlua::prelude::*; -pub trait StandardLibrary -where - Self: Sized + 'static + FromStr, -{ - const ALL: &'static [Self]; - +pub trait StandardLibrary { fn name(&self) -> &'static str; fn module<'lua>(&self, lua: &'lua Lua) -> LuaResult>; @@ -32,23 +27,6 @@ pub enum LuneStandardLibrary { } impl StandardLibrary for LuneStandardLibrary { - /** - All available standard libraries. - */ - #[rustfmt::skip] - const ALL: &'static [Self] = &[ - #[cfg(feature = "datetime")] Self::DateTime, - #[cfg(feature = "fs")] Self::Fs, - #[cfg(feature = "luau")] Self::Luau, - #[cfg(feature = "net")] Self::Net, - #[cfg(feature = "task")] Self::Task, - #[cfg(feature = "process")] Self::Process, - #[cfg(feature = "regex")] Self::Regex, - #[cfg(feature = "serde")] Self::Serde, - #[cfg(feature = "stdio")] Self::Stdio, - #[cfg(feature = "roblox")] Self::Roblox, - ]; - /** Gets the name of the library, such as `datetime` or `fs`. */ @@ -125,12 +103,7 @@ impl FromStr for LuneStandardLibrary { _ => { return Err(format!( - "Unknown standard library '{low}'\nValid libraries are: {}", - Self::ALL - .iter() - .map(Self::name) - .collect::>() - .join(", ") + "Unknown standard library '{low}'" )) } }) From 370c7f818754fb9d5cc530bd240addfa47b48073 Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 23 Aug 2024 02:26:03 +0330 Subject: [PATCH 05/35] require and cache impl for stds --- crates/lune-std/src/globals/require/mod.rs | 17 ++-- .../lune-std/src/globals/require/storage.rs | 92 ++++++++++++++++++- crates/lune-std/src/library.rs | 26 +++++- crates/lune-std/src/luaurc.rs | 21 +++-- 4 files changed, 135 insertions(+), 21 deletions(-) diff --git a/crates/lune-std/src/globals/require/mod.rs b/crates/lune-std/src/globals/require/mod.rs index c4eee70f..7c11b919 100644 --- a/crates/lune-std/src/globals/require/mod.rs +++ b/crates/lune-std/src/globals/require/mod.rs @@ -1,4 +1,4 @@ -use crate::{luaurc::path_to_alias, path::get_parent_path}; +use crate::{luaurc::path_to_alias, path::get_parent_path, LuneStandardLibrary}; use mlua::prelude::*; use std::path::PathBuf; @@ -9,11 +9,8 @@ pub async fn lua_require(lua: &Lua, path: String) -> LuaResult { let require_alias = path_to_alias(&require_path_rel)?; if let Some(require_alias) = require_alias { - if require_alias.alias == "lune" { - Err(LuaError::runtime(format!( - "Tried requiring a lune library '{}'\nbut aliases are not implemented yet.", - require_alias.path, - ))) + if storage::RequireStorage::std_exists(lua, &require_alias.alias)? { + storage::RequireStorage::require_std(lua, require_alias) } else { Err(LuaError::runtime(format!( "Tried requiring a custom alias '{}'\nbut aliases are not implemented yet.", @@ -22,7 +19,7 @@ pub async fn lua_require(lua: &Lua, path: String) -> LuaResult { } } else { let parent_path = get_parent_path(lua)?; - let require_path_abs = parent_path.join(require_path_rel); + let require_path_abs = parent_path.join(&require_path_rel); Err(LuaError::runtime(format!( "Tried requiring '{}'\nbut requires are not implemented yet.", @@ -34,5 +31,11 @@ pub async fn lua_require(lua: &Lua, path: String) -> LuaResult { pub fn create(lua: &Lua) -> LuaResult { let f = lua.create_async_function(lua_require)?; + storage::RequireStorage::init(lua)?; + + for std in LuneStandardLibrary::ALL { + storage::RequireStorage::inject_std(lua, "lune", *std)?; + } + f.into_lua(lua) } diff --git a/crates/lune-std/src/globals/require/storage.rs b/crates/lune-std/src/globals/require/storage.rs index 8c1eda95..777dbdf9 100644 --- a/crates/lune-std/src/globals/require/storage.rs +++ b/crates/lune-std/src/globals/require/storage.rs @@ -1,6 +1,92 @@ -use crate::library::StandardLibrary; +use crate::{library::StandardLibrary, luaurc::RequireAlias}; +use mlua::prelude::*; use std::collections::HashMap; -pub struct RequireStorage<'a> { - stds: HashMap<&'a str, Box>, +/// The private struct that's stored in mlua's app data container +#[derive(Debug, Default)] +struct RequireStorageData<'a> { + std: HashMap<&'a str, HashMap<&'a str, Box>>, + std_cache: HashMap, + cache: HashMap<&'a str, LuaRegistryKey>, +} + +#[derive(Debug)] +pub struct RequireStorage {} + +impl RequireStorage { + pub fn init(lua: &Lua) -> LuaResult<()> { + if lua.set_app_data(RequireStorageData::default()).is_some() { + Err(LuaError::runtime("RequireStorage::init got called twice")) + } else { + Ok(()) + } + } + + pub fn std_exists(lua: &Lua, alias: &str) -> LuaResult { + let data_ref = lua + .app_data_ref::() + .ok_or(LuaError::runtime("Couldn't find RequireStorageData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + + Ok(data_ref.std.contains_key(alias)) + } + + pub fn require_std(lua: &Lua, require_alias: RequireAlias) -> LuaResult> { + let data_ref = lua + .app_data_ref::() + .ok_or(LuaError::runtime("Couldn't find RequireStorageData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + + if let Some(cached) = data_ref.std_cache.get(&require_alias) { + return cached.into_lua(lua)?.into_lua_multi(lua); + } + + let libraries = + data_ref + .std + .get(&require_alias.alias.as_str()) + .ok_or(mlua::Error::runtime(format!( + "Alias '{}' does not point to a built-in standard library", + require_alias.alias + )))?; + + let std = libraries + .get(require_alias.path.as_str()) + .ok_or(mlua::Error::runtime(format!( + "Library '{}' does not point to a member of '{}' standard libraries", + require_alias.path, require_alias.alias + )))?; + + let multi = std.module(lua)?; + let mutli_clone = multi.clone(); + let multi_reg = lua.create_registry_value(mutli_clone.into_vec())?; + + let mut data = lua + .app_data_mut::() + .ok_or(LuaError::runtime("Couldn't find RequireStorageData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + + data.std_cache.insert(require_alias, multi_reg); + + Ok(multi) + } + + pub fn inject_std( + lua: &Lua, + alias: &'static str, + std: impl StandardLibrary + 'static, + ) -> LuaResult<()> { + let mut data = lua + .app_data_mut::() + .ok_or(LuaError::runtime("Couldn't find RequireStorageData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + + if let Some(map) = data.std.get_mut(alias) { + map.insert(std.name(), Box::new(std)); + } else { + let mut map: HashMap<&str, Box> = HashMap::new(); + + map.insert(std.name(), Box::new(std)); + + data.std.insert(alias, map); + }; + + Ok(()) + } } diff --git a/crates/lune-std/src/library.rs b/crates/lune-std/src/library.rs index 96961c0b..4f7745e8 100644 --- a/crates/lune-std/src/library.rs +++ b/crates/lune-std/src/library.rs @@ -1,8 +1,11 @@ -use std::str::FromStr; +use std::{fmt::Debug, str::FromStr}; use mlua::prelude::*; -pub trait StandardLibrary { +pub trait StandardLibrary +where + Self: Debug, +{ fn name(&self) -> &'static str; fn module<'lua>(&self, lua: &'lua Lua) -> LuaResult>; @@ -26,6 +29,25 @@ pub enum LuneStandardLibrary { #[cfg(feature = "roblox")] Roblox, } +impl LuneStandardLibrary { + /** + All available standard libraries. + */ + #[rustfmt::skip] + pub const ALL: &'static [Self] = &[ + #[cfg(feature = "datetime")] Self::DateTime, + #[cfg(feature = "fs")] Self::Fs, + #[cfg(feature = "luau")] Self::Luau, + #[cfg(feature = "net")] Self::Net, + #[cfg(feature = "task")] Self::Task, + #[cfg(feature = "process")] Self::Process, + #[cfg(feature = "regex")] Self::Regex, + #[cfg(feature = "serde")] Self::Serde, + #[cfg(feature = "stdio")] Self::Stdio, + #[cfg(feature = "roblox")] Self::Roblox, + ]; +} + impl StandardLibrary for LuneStandardLibrary { /** Gets the name of the library, such as `datetime` or `fs`. diff --git a/crates/lune-std/src/luaurc.rs b/crates/lune-std/src/luaurc.rs index c4fb6551..4a5606a3 100644 --- a/crates/lune-std/src/luaurc.rs +++ b/crates/lune-std/src/luaurc.rs @@ -8,10 +8,10 @@ use std::{ }; use tokio::fs; -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct RequireAlias<'a> { - pub alias: &'a str, - pub path: &'a str, +#[derive(Debug, Clone, Eq, Hash, PartialEq)] +pub struct RequireAlias { + pub alias: String, + pub path: String, } #[derive(Debug, Clone, PartialEq, Deserialize)] @@ -26,7 +26,7 @@ pub struct Luaurc { /// `@lune/task` becomes `Some({ alias: "lune", path: "task" })` /// /// `../path/script` becomes `None` -pub fn path_to_alias(path: &Path) -> Result>, mlua::Error> { +pub fn path_to_alias(path: &Path) -> Result, mlua::Error> { if let Some(aliased_path) = path .to_str() .ok_or(mlua::Error::runtime("Couldn't turn path into string"))? @@ -36,7 +36,10 @@ pub fn path_to_alias(path: &Path) -> Result>, mlua::Erro "Require with alias doesn't contain '/'", ))?; - Ok(Some(RequireAlias { alias, path })) + Ok(Some(RequireAlias { + alias: alias.to_string(), + path: path.to_string(), + })) } else { Ok(None) } @@ -55,7 +58,7 @@ async fn parse_luaurc(_: &mlua::Lua, path: &PathBuf) -> Result, m /// until an alias for the provided `RequireAlias` is found pub async fn resolve_require_alias<'lua>( lua: &'lua mlua::Lua, - alias: &'lua RequireAlias<'lua>, + alias: &'lua RequireAlias, ) -> Result { let cwd = current_dir()?; let parent = cwd.join(get_parent_path(lua)?); @@ -65,8 +68,8 @@ pub async fn resolve_require_alias<'lua>( if path.starts_with(&cwd) { if let Some(luaurc) = parse_luaurc(lua, &parent.join(".luaurc")).await? { if let Some(aliases) = luaurc.aliases { - if let Some(alias_path) = aliases.get(alias.alias) { - let resolved = path.join(alias_path.join(alias.path)); + if let Some(alias_path) = aliases.get(&alias.alias) { + let resolved = path.join(alias_path.join(&alias.path)); return Ok(resolved); } From cbd7cb64805bf10144e6fbeb6fb5e2a8403e5089 Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 23 Aug 2024 02:27:10 +0330 Subject: [PATCH 06/35] rename storage to context --- .../require/{storage.rs => context.rs} | 26 +++++++++---------- crates/lune-std/src/globals/require/mod.rs | 10 +++---- 2 files changed, 18 insertions(+), 18 deletions(-) rename crates/lune-std/src/globals/require/{storage.rs => context.rs} (74%) diff --git a/crates/lune-std/src/globals/require/storage.rs b/crates/lune-std/src/globals/require/context.rs similarity index 74% rename from crates/lune-std/src/globals/require/storage.rs rename to crates/lune-std/src/globals/require/context.rs index 777dbdf9..907baab0 100644 --- a/crates/lune-std/src/globals/require/storage.rs +++ b/crates/lune-std/src/globals/require/context.rs @@ -4,19 +4,19 @@ use std::collections::HashMap; /// The private struct that's stored in mlua's app data container #[derive(Debug, Default)] -struct RequireStorageData<'a> { +struct RequireContextData<'a> { std: HashMap<&'a str, HashMap<&'a str, Box>>, std_cache: HashMap, cache: HashMap<&'a str, LuaRegistryKey>, } #[derive(Debug)] -pub struct RequireStorage {} +pub struct RequireContext {} -impl RequireStorage { +impl RequireContext { pub fn init(lua: &Lua) -> LuaResult<()> { - if lua.set_app_data(RequireStorageData::default()).is_some() { - Err(LuaError::runtime("RequireStorage::init got called twice")) + if lua.set_app_data(RequireContextData::default()).is_some() { + Err(LuaError::runtime("RequireContext::init got called twice")) } else { Ok(()) } @@ -24,16 +24,16 @@ impl RequireStorage { pub fn std_exists(lua: &Lua, alias: &str) -> LuaResult { let data_ref = lua - .app_data_ref::() - .ok_or(LuaError::runtime("Couldn't find RequireStorageData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + .app_data_ref::() + .ok_or(LuaError::runtime("Couldn't find RequireContextData in app data container, make sure RequireStorage::init is called on this lua instance"))?; Ok(data_ref.std.contains_key(alias)) } pub fn require_std(lua: &Lua, require_alias: RequireAlias) -> LuaResult> { let data_ref = lua - .app_data_ref::() - .ok_or(LuaError::runtime("Couldn't find RequireStorageData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + .app_data_ref::() + .ok_or(LuaError::runtime("Couldn't find RequireContextData in app data container, make sure RequireStorage::init is called on this lua instance"))?; if let Some(cached) = data_ref.std_cache.get(&require_alias) { return cached.into_lua(lua)?.into_lua_multi(lua); @@ -60,8 +60,8 @@ impl RequireStorage { let multi_reg = lua.create_registry_value(mutli_clone.into_vec())?; let mut data = lua - .app_data_mut::() - .ok_or(LuaError::runtime("Couldn't find RequireStorageData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + .app_data_mut::() + .ok_or(LuaError::runtime("Couldn't find RequireContextData in app data container, make sure RequireStorage::init is called on this lua instance"))?; data.std_cache.insert(require_alias, multi_reg); @@ -74,8 +74,8 @@ impl RequireStorage { std: impl StandardLibrary + 'static, ) -> LuaResult<()> { let mut data = lua - .app_data_mut::() - .ok_or(LuaError::runtime("Couldn't find RequireStorageData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + .app_data_mut::() + .ok_or(LuaError::runtime("Couldn't find RequireContextData in app data container, make sure RequireStorage::init is called on this lua instance"))?; if let Some(map) = data.std.get_mut(alias) { map.insert(std.name(), Box::new(std)); diff --git a/crates/lune-std/src/globals/require/mod.rs b/crates/lune-std/src/globals/require/mod.rs index 7c11b919..30cc04f6 100644 --- a/crates/lune-std/src/globals/require/mod.rs +++ b/crates/lune-std/src/globals/require/mod.rs @@ -2,15 +2,15 @@ use crate::{luaurc::path_to_alias, path::get_parent_path, LuneStandardLibrary}; use mlua::prelude::*; use std::path::PathBuf; -pub mod storage; +pub mod context; pub async fn lua_require(lua: &Lua, path: String) -> LuaResult { let require_path_rel = PathBuf::from(path); let require_alias = path_to_alias(&require_path_rel)?; if let Some(require_alias) = require_alias { - if storage::RequireStorage::std_exists(lua, &require_alias.alias)? { - storage::RequireStorage::require_std(lua, require_alias) + if context::RequireContext::std_exists(lua, &require_alias.alias)? { + context::RequireContext::require_std(lua, require_alias) } else { Err(LuaError::runtime(format!( "Tried requiring a custom alias '{}'\nbut aliases are not implemented yet.", @@ -31,10 +31,10 @@ pub async fn lua_require(lua: &Lua, path: String) -> LuaResult { pub fn create(lua: &Lua) -> LuaResult { let f = lua.create_async_function(lua_require)?; - storage::RequireStorage::init(lua)?; + context::RequireContext::init(lua)?; for std in LuneStandardLibrary::ALL { - storage::RequireStorage::inject_std(lua, "lune", *std)?; + context::RequireContext::inject_std(lua, "lune", *std)?; } f.into_lua(lua) From 16b3cde97ed805007bb3eb49023f8fb2381dfeff Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 23 Aug 2024 02:34:14 +0330 Subject: [PATCH 07/35] make cache arc mutex --- crates/lune-std/src/globals/require/context.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/lune-std/src/globals/require/context.rs b/crates/lune-std/src/globals/require/context.rs index 907baab0..c659b258 100644 --- a/crates/lune-std/src/globals/require/context.rs +++ b/crates/lune-std/src/globals/require/context.rs @@ -1,13 +1,14 @@ use crate::{library::StandardLibrary, luaurc::RequireAlias}; use mlua::prelude::*; -use std::collections::HashMap; +use std::{collections::HashMap, sync::Arc}; +use tokio::sync::Mutex; /// The private struct that's stored in mlua's app data container #[derive(Debug, Default)] struct RequireContextData<'a> { std: HashMap<&'a str, HashMap<&'a str, Box>>, std_cache: HashMap, - cache: HashMap<&'a str, LuaRegistryKey>, + cache: Arc>>, } #[derive(Debug)] From 62fd0d00b18c399f231445dc601c9fff9590c92e Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 23 Aug 2024 02:37:11 +0330 Subject: [PATCH 08/35] make data struct ready for impl --- crates/lune-std/src/globals/require/context.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/lune-std/src/globals/require/context.rs b/crates/lune-std/src/globals/require/context.rs index c659b258..fcaeb364 100644 --- a/crates/lune-std/src/globals/require/context.rs +++ b/crates/lune-std/src/globals/require/context.rs @@ -1,14 +1,15 @@ use crate::{library::StandardLibrary, luaurc::RequireAlias}; use mlua::prelude::*; -use std::{collections::HashMap, sync::Arc}; -use tokio::sync::Mutex; +use std::{collections::HashMap, path::PathBuf, sync::Arc}; +use tokio::sync::{broadcast::Sender, Mutex}; /// The private struct that's stored in mlua's app data container #[derive(Debug, Default)] struct RequireContextData<'a> { std: HashMap<&'a str, HashMap<&'a str, Box>>, std_cache: HashMap, - cache: Arc>>, + cache: Arc>>, + pending: Arc>>>, } #[derive(Debug)] From 27a98f9671c11bd18be473b1bac138a5e79292e7 Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 23 Aug 2024 02:57:38 +0330 Subject: [PATCH 09/35] move fn to impl --- crates/lune-std/src/luaurc.rs | 48 ++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/crates/lune-std/src/luaurc.rs b/crates/lune-std/src/luaurc.rs index 4a5606a3..b9d5d4b3 100644 --- a/crates/lune-std/src/luaurc.rs +++ b/crates/lune-std/src/luaurc.rs @@ -54,34 +54,36 @@ async fn parse_luaurc(_: &mlua::Lua, path: &PathBuf) -> Result, m } } -/// Searches for .luaurc recursively -/// until an alias for the provided `RequireAlias` is found -pub async fn resolve_require_alias<'lua>( - lua: &'lua mlua::Lua, - alias: &'lua RequireAlias, -) -> Result { - let cwd = current_dir()?; - let parent = cwd.join(get_parent_path(lua)?); - let ancestors = parent.ancestors(); +impl Luaurc { + /// Searches for .luaurc recursively + /// until an alias for the provided `RequireAlias` is found + pub async fn resolve_path<'lua>( + lua: &'lua mlua::Lua, + alias: &'lua RequireAlias, + ) -> Result { + let cwd = current_dir()?; + let parent = cwd.join(get_parent_path(lua)?); + let ancestors = parent.ancestors(); - for path in ancestors { - if path.starts_with(&cwd) { - if let Some(luaurc) = parse_luaurc(lua, &parent.join(".luaurc")).await? { - if let Some(aliases) = luaurc.aliases { - if let Some(alias_path) = aliases.get(&alias.alias) { - let resolved = path.join(alias_path.join(&alias.path)); + for path in ancestors { + if path.starts_with(&cwd) { + if let Some(luaurc) = parse_luaurc(lua, &parent.join(".luaurc")).await? { + if let Some(aliases) = luaurc.aliases { + if let Some(alias_path) = aliases.get(&alias.alias) { + let resolved = path.join(alias_path.join(&alias.path)); - return Ok(resolved); + return Ok(resolved); + } } } + } else { + break; } - } else { - break; } - } - Err(mlua::Error::runtime(format!( - "Coudln't find the alias '{}' in any .luaurc file", - alias.alias - ))) + Err(mlua::Error::runtime(format!( + "Coudln't find the alias '{}' in any .luaurc file", + alias.alias + ))) + } } From 505ff977f0af36007cb010eb17860c9fa9a2cbbe Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 23 Aug 2024 03:11:53 +0330 Subject: [PATCH 10/35] require impl (no caching) --- .../lune-std/src/globals/require/context.rs | 25 ++++++++- crates/lune-std/src/globals/require/mod.rs | 53 ++++++++++++++++--- 2 files changed, 71 insertions(+), 7 deletions(-) diff --git a/crates/lune-std/src/globals/require/context.rs b/crates/lune-std/src/globals/require/context.rs index fcaeb364..7072c41c 100644 --- a/crates/lune-std/src/globals/require/context.rs +++ b/crates/lune-std/src/globals/require/context.rs @@ -1,7 +1,10 @@ use crate::{library::StandardLibrary, luaurc::RequireAlias}; use mlua::prelude::*; use std::{collections::HashMap, path::PathBuf, sync::Arc}; -use tokio::sync::{broadcast::Sender, Mutex}; +use tokio::{ + fs, + sync::{broadcast::Sender, Mutex}, +}; /// The private struct that's stored in mlua's app data container #[derive(Debug, Default)] @@ -70,6 +73,26 @@ impl RequireContext { Ok(multi) } + pub async fn require( + lua: &Lua, + path_rel: PathBuf, + path_abs: PathBuf, + ) -> LuaResult { + if !fs::try_exists(&path_abs).await? { + return Err(LuaError::runtime(format!( + "Can not require '{}' as it does not exist", + path_rel.to_string_lossy() + ))); + } + + let content = fs::read_to_string(&path_abs).await?; + + lua.load(content) + .set_name(path_abs.to_string_lossy()) + .eval_async() + .await + } + pub fn inject_std( lua: &Lua, alias: &'static str, diff --git a/crates/lune-std/src/globals/require/mod.rs b/crates/lune-std/src/globals/require/mod.rs index 30cc04f6..27ed0192 100644 --- a/crates/lune-std/src/globals/require/mod.rs +++ b/crates/lune-std/src/globals/require/mod.rs @@ -1,9 +1,36 @@ use crate::{luaurc::path_to_alias, path::get_parent_path, LuneStandardLibrary}; use mlua::prelude::*; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; +use tokio::fs; pub mod context; +/// tries these alternatives on given path: +/// +/// * .lua and .luau extension +/// * path.join("init.luau") and path.join("init.lua") +pub async fn resolve_path(path: &Path) -> LuaResult { + let init_path = &path.join("init"); + + for ext in ["lua", "luau"] { + // try extension on given path + let path = append_extension(path, ext); + + if fs::try_exists(&path).await? { + return Ok(path); + }; + + // try extension on given path's init + let init_path = append_extension(init_path, ext); + + if fs::try_exists(&init_path).await? { + return Ok(init_path); + }; + } + + Err(LuaError::runtime("Could not resolve path")) +} + pub async fn lua_require(lua: &Lua, path: String) -> LuaResult { let require_path_rel = PathBuf::from(path); let require_alias = path_to_alias(&require_path_rel)?; @@ -19,12 +46,16 @@ pub async fn lua_require(lua: &Lua, path: String) -> LuaResult { } } else { let parent_path = get_parent_path(lua)?; - let require_path_abs = parent_path.join(&require_path_rel); + let require_path_abs = resolve_path(&parent_path.join(&require_path_rel)) + .await + .map_err(|_| { + LuaError::runtime(format!( + "Can not require '{}' as it does not exist", + require_path_rel.to_string_lossy(), + )) + })?; - Err(LuaError::runtime(format!( - "Tried requiring '{}'\nbut requires are not implemented yet.", - require_path_abs.to_string_lossy(), - ))) + context::RequireContext::require(lua, require_path_rel, require_path_abs).await } } @@ -39,3 +70,13 @@ pub fn create(lua: &Lua) -> LuaResult { f.into_lua(lua) } + +fn append_extension(path: impl Into, ext: &'static str) -> PathBuf { + let mut new = path.into(); + match new.extension() { + // FUTURE: There's probably a better way to do this than converting to a lossy string + Some(e) => new.set_extension(format!("{}.{ext}", e.to_string_lossy())), + None => new.set_extension(ext), + }; + new +} From 13cff6ff0080329d22f53650d45c2a7c519bb4e6 Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 23 Aug 2024 03:27:32 +0330 Subject: [PATCH 11/35] move path functions to path.rs --- crates/lune-std/src/globals/require/mod.rs | 43 ++------------------- crates/lune-std/src/globals/require/path.rs | 39 +++++++++++++++++++ 2 files changed, 43 insertions(+), 39 deletions(-) create mode 100644 crates/lune-std/src/globals/require/path.rs diff --git a/crates/lune-std/src/globals/require/mod.rs b/crates/lune-std/src/globals/require/mod.rs index 27ed0192..1bc98fd3 100644 --- a/crates/lune-std/src/globals/require/mod.rs +++ b/crates/lune-std/src/globals/require/mod.rs @@ -1,35 +1,10 @@ use crate::{luaurc::path_to_alias, path::get_parent_path, LuneStandardLibrary}; use mlua::prelude::*; -use std::path::{Path, PathBuf}; -use tokio::fs; +use path::resolve_path; +use std::path::PathBuf; -pub mod context; - -/// tries these alternatives on given path: -/// -/// * .lua and .luau extension -/// * path.join("init.luau") and path.join("init.lua") -pub async fn resolve_path(path: &Path) -> LuaResult { - let init_path = &path.join("init"); - - for ext in ["lua", "luau"] { - // try extension on given path - let path = append_extension(path, ext); - - if fs::try_exists(&path).await? { - return Ok(path); - }; - - // try extension on given path's init - let init_path = append_extension(init_path, ext); - - if fs::try_exists(&init_path).await? { - return Ok(init_path); - }; - } - - Err(LuaError::runtime("Could not resolve path")) -} +mod context; +mod path; pub async fn lua_require(lua: &Lua, path: String) -> LuaResult { let require_path_rel = PathBuf::from(path); @@ -70,13 +45,3 @@ pub fn create(lua: &Lua) -> LuaResult { f.into_lua(lua) } - -fn append_extension(path: impl Into, ext: &'static str) -> PathBuf { - let mut new = path.into(); - match new.extension() { - // FUTURE: There's probably a better way to do this than converting to a lossy string - Some(e) => new.set_extension(format!("{}.{ext}", e.to_string_lossy())), - None => new.set_extension(ext), - }; - new -} diff --git a/crates/lune-std/src/globals/require/path.rs b/crates/lune-std/src/globals/require/path.rs new file mode 100644 index 00000000..e11b3c7b --- /dev/null +++ b/crates/lune-std/src/globals/require/path.rs @@ -0,0 +1,39 @@ +use mlua::prelude::*; +use std::path::{Path, PathBuf}; +use tokio::fs; + +/// tries these alternatives on given path: +/// +/// * .lua and .luau extension +/// * path.join("init.luau") and path.join("init.lua") +pub async fn resolve_path(path: &Path) -> LuaResult { + let init_path = &path.join("init"); + + for ext in ["lua", "luau"] { + // try extension on given path + let path = append_extension(path, ext); + + if fs::try_exists(&path).await? { + return Ok(path); + }; + + // try extension on given path's init + let init_path = append_extension(init_path, ext); + + if fs::try_exists(&init_path).await? { + return Ok(init_path); + }; + } + + Err(LuaError::runtime("Could not resolve path")) +} + +fn append_extension(path: impl Into, ext: &'static str) -> PathBuf { + let mut new = path.into(); + match new.extension() { + // FUTURE: There's probably a better way to do this than converting to a lossy string + Some(e) => new.set_extension(format!("{}.{ext}", e.to_string_lossy())), + None => new.set_extension(ext), + }; + new +} From 2eb167025a59f688ffff1c12a21374400f2b3fb1 Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 23 Aug 2024 03:36:38 +0330 Subject: [PATCH 12/35] luaurc error impl --- Cargo.lock | 1 + crates/lune-std/Cargo.toml | 2 ++ crates/lune-std/src/globals/require/mod.rs | 2 +- crates/lune-std/src/luaurc.rs | 42 +++++++++++++++------- 4 files changed, 33 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 59d32a97..d316fca3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1571,6 +1571,7 @@ dependencies = [ "mlua-luau-scheduler", "serde", "serde_json", + "thiserror", "tokio", ] diff --git a/crates/lune-std/Cargo.toml b/crates/lune-std/Cargo.toml index 07762b64..f6901e84 100644 --- a/crates/lune-std/Cargo.toml +++ b/crates/lune-std/Cargo.toml @@ -45,6 +45,8 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tokio = { version = "1", default-features = false, features = ["fs", "sync"] } +thiserror = "1.0.63" + lune-utils = { version = "0.1.3", path = "../lune-utils" } lune-std-datetime = { optional = true, version = "0.1.3", path = "../lune-std-datetime" } diff --git a/crates/lune-std/src/globals/require/mod.rs b/crates/lune-std/src/globals/require/mod.rs index 1bc98fd3..7472310b 100644 --- a/crates/lune-std/src/globals/require/mod.rs +++ b/crates/lune-std/src/globals/require/mod.rs @@ -8,7 +8,7 @@ mod path; pub async fn lua_require(lua: &Lua, path: String) -> LuaResult { let require_path_rel = PathBuf::from(path); - let require_alias = path_to_alias(&require_path_rel)?; + let require_alias = path_to_alias(&require_path_rel).into_lua_err()?; if let Some(require_alias) = require_alias { if context::RequireContext::std_exists(lua, &require_alias.alias)? { diff --git a/crates/lune-std/src/luaurc.rs b/crates/lune-std/src/luaurc.rs index b9d5d4b3..95c73203 100644 --- a/crates/lune-std/src/luaurc.rs +++ b/crates/lune-std/src/luaurc.rs @@ -1,11 +1,11 @@ use crate::path::get_parent_path; -use mlua::ExternalResult; use serde::Deserialize; use std::{ collections::HashMap, env::current_dir, path::{Path, PathBuf}, }; +use thiserror::Error; use tokio::fs; #[derive(Debug, Clone, Eq, Hash, PartialEq)] @@ -19,6 +19,23 @@ pub struct Luaurc { aliases: Option>, } +#[derive(Debug, Error)] +pub enum LuaurcError { + #[error("Require with alias doesn't contain '/'")] + UsedAliasWithoutSlash, + #[error("Failed to convert string to path")] + FailedStringToPathConversion, + #[error("Failed to find a path for alias '{0}' in .luaurc files")] + FailedToFindAlias(String), + + #[error("IOError: {0}")] + IOError(#[from] std::io::Error), + #[error("JsonError: {0}")] + JsonError(#[from] serde_json::Error), + #[error("LuaError: {0}")] + LuaError(#[from] mlua::Error), +} + /// Parses path into `RequireAlias` struct /// /// ### Examples @@ -26,15 +43,15 @@ pub struct Luaurc { /// `@lune/task` becomes `Some({ alias: "lune", path: "task" })` /// /// `../path/script` becomes `None` -pub fn path_to_alias(path: &Path) -> Result, mlua::Error> { +pub fn path_to_alias(path: &Path) -> Result, LuaurcError> { if let Some(aliased_path) = path .to_str() - .ok_or(mlua::Error::runtime("Couldn't turn path into string"))? + .ok_or(LuaurcError::FailedStringToPathConversion)? .strip_prefix('@') { - let (alias, path) = aliased_path.split_once('/').ok_or(mlua::Error::runtime( - "Require with alias doesn't contain '/'", - ))?; + let (alias, path) = aliased_path + .split_once('/') + .ok_or(LuaurcError::UsedAliasWithoutSlash)?; Ok(Some(RequireAlias { alias: alias.to_string(), @@ -45,10 +62,12 @@ pub fn path_to_alias(path: &Path) -> Result, mlua::Error> { } } -async fn parse_luaurc(_: &mlua::Lua, path: &PathBuf) -> Result, mlua::Error> { +async fn parse_luaurc(_: &mlua::Lua, path: &PathBuf) -> Result, LuaurcError> { if fs::try_exists(path).await? { let content = fs::read(path).await?; - serde_json::from_slice(&content).map(Some).into_lua_err() + serde_json::from_slice(&content) + .map(Some) + .map_err(|x| x.into()) } else { Ok(None) } @@ -60,7 +79,7 @@ impl Luaurc { pub async fn resolve_path<'lua>( lua: &'lua mlua::Lua, alias: &'lua RequireAlias, - ) -> Result { + ) -> Result { let cwd = current_dir()?; let parent = cwd.join(get_parent_path(lua)?); let ancestors = parent.ancestors(); @@ -81,9 +100,6 @@ impl Luaurc { } } - Err(mlua::Error::runtime(format!( - "Coudln't find the alias '{}' in any .luaurc file", - alias.alias - ))) + Err(LuaurcError::FailedToFindAlias(alias.alias.to_string())) } } From a633afc828a6a7e5de65b149bbf0f14fc42c69c4 Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 23 Aug 2024 03:48:28 +0330 Subject: [PATCH 13/35] impl custom aliases --- crates/lune-std/src/globals/require/mod.rs | 18 +++++++++++++----- crates/lune-std/src/luaurc.rs | 4 ++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/crates/lune-std/src/globals/require/mod.rs b/crates/lune-std/src/globals/require/mod.rs index 7472310b..6de386b7 100644 --- a/crates/lune-std/src/globals/require/mod.rs +++ b/crates/lune-std/src/globals/require/mod.rs @@ -1,4 +1,8 @@ -use crate::{luaurc::path_to_alias, path::get_parent_path, LuneStandardLibrary}; +use crate::{ + luaurc::{path_to_alias, Luaurc}, + path::get_parent_path, + LuneStandardLibrary, +}; use mlua::prelude::*; use path::resolve_path; use std::path::PathBuf; @@ -14,10 +18,14 @@ pub async fn lua_require(lua: &Lua, path: String) -> LuaResult { if context::RequireContext::std_exists(lua, &require_alias.alias)? { context::RequireContext::require_std(lua, require_alias) } else { - Err(LuaError::runtime(format!( - "Tried requiring a custom alias '{}'\nbut aliases are not implemented yet.", - require_alias.alias, - ))) + let require_path_abs = resolve_path( + &Luaurc::resolve_path(lua, &require_alias) + .await + .into_lua_err()?, + ) + .await?; + + context::RequireContext::require(lua, require_path_rel, require_path_abs).await } } else { let parent_path = get_parent_path(lua)?; diff --git a/crates/lune-std/src/luaurc.rs b/crates/lune-std/src/luaurc.rs index 95c73203..697a2f3b 100644 --- a/crates/lune-std/src/luaurc.rs +++ b/crates/lune-std/src/luaurc.rs @@ -67,7 +67,7 @@ async fn parse_luaurc(_: &mlua::Lua, path: &PathBuf) -> Result, L let content = fs::read(path).await?; serde_json::from_slice(&content) .map(Some) - .map_err(|x| x.into()) + .map_err(std::convert::Into::into) } else { Ok(None) } @@ -86,7 +86,7 @@ impl Luaurc { for path in ancestors { if path.starts_with(&cwd) { - if let Some(luaurc) = parse_luaurc(lua, &parent.join(".luaurc")).await? { + if let Some(luaurc) = parse_luaurc(lua, &path.join(".luaurc")).await? { if let Some(aliases) = luaurc.aliases { if let Some(alias_path) = aliases.get(&alias.alias) { let resolved = path.join(alias_path.join(&alias.path)); From fedaf9a625c36b72f78677682c1dc1c65d983b3e Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 23 Aug 2024 04:06:46 +0330 Subject: [PATCH 14/35] impl cache --- .../lune-std/src/globals/require/context.rs | 76 ++++++++++++++++++- 1 file changed, 73 insertions(+), 3 deletions(-) diff --git a/crates/lune-std/src/globals/require/context.rs b/crates/lune-std/src/globals/require/context.rs index 7072c41c..b3f7927b 100644 --- a/crates/lune-std/src/globals/require/context.rs +++ b/crates/lune-std/src/globals/require/context.rs @@ -3,7 +3,10 @@ use mlua::prelude::*; use std::{collections::HashMap, path::PathBuf, sync::Arc}; use tokio::{ fs, - sync::{broadcast::Sender, Mutex}, + sync::{ + broadcast::{self, Sender}, + Mutex, + }, }; /// The private struct that's stored in mlua's app data container @@ -78,6 +81,48 @@ impl RequireContext { path_rel: PathBuf, path_abs: PathBuf, ) -> LuaResult { + // wait for module to be required + // if its pending somewhere else + { + let data_ref = lua + .app_data_ref::() + .ok_or(LuaError::runtime("Couldn't find RequireContextData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + + let pending = data_ref.pending.try_lock().into_lua_err()?; + + if let Some(a) = pending.get(&path_abs) { + a.subscribe().recv().await.into_lua_err()?; + } + } + + // get module from cache + // *if* its cached + { + let data_ref = lua + .app_data_ref::() + .ok_or(LuaError::runtime("Couldn't find RequireContextData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + + let cache = data_ref.cache.lock().await; + + if let Some(cached) = cache.get(&path_abs) { + return cached.into_lua(lua).into_lua_multi(lua); + } + } + + // create a broadcast channel + { + let data_ref = lua + .app_data_ref::() + .ok_or(LuaError::runtime("Couldn't find RequireContextData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + + let (broadcast_tx, _) = broadcast::channel(1); + + { + let mut pending = data_ref.pending.try_lock().into_lua_err()?; + pending.insert(path_abs.clone(), broadcast_tx); + } + } + if !fs::try_exists(&path_abs).await? { return Err(LuaError::runtime(format!( "Can not require '{}' as it does not exist", @@ -87,10 +132,35 @@ impl RequireContext { let content = fs::read_to_string(&path_abs).await?; - lua.load(content) + let multi = lua + .load(content) .set_name(path_abs.to_string_lossy()) - .eval_async() + .eval_async::() + .await?; + + let mutli_clone = multi.clone(); + let multi_reg = lua.create_registry_value(mutli_clone.into_vec())?; + + let data_ref = lua + .app_data_ref::() + .ok_or(LuaError::runtime("Couldn't find RequireContextData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + + data_ref + .cache + .lock() + .await + .insert(path_abs.clone(), multi_reg); + + let broadcast_tx = data_ref + .pending + .lock() .await + .remove(&path_abs) + .expect("Pending require broadcaster was unexpectedly removed"); + + broadcast_tx.send(()).ok(); + + Ok(multi) } pub fn inject_std( From 25b09b18ac7563ee2ef1e95b399d4830622de0ae Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 23 Aug 2024 04:21:36 +0330 Subject: [PATCH 15/35] fixed not converting cached results --- .../lune-std/src/globals/require/context.rs | 17 +++++++--- crates/lune-std/src/globals/require/mod.rs | 2 +- crates/lune-std/src/globals/require/path.rs | 33 +++++++++++++++++-- crates/lune-std/src/lib.rs | 2 ++ 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/crates/lune-std/src/globals/require/context.rs b/crates/lune-std/src/globals/require/context.rs index b3f7927b..e3ab9991 100644 --- a/crates/lune-std/src/globals/require/context.rs +++ b/crates/lune-std/src/globals/require/context.rs @@ -30,7 +30,7 @@ impl RequireContext { } } - pub fn std_exists(lua: &Lua, alias: &str) -> LuaResult { + pub(crate) fn std_exists(lua: &Lua, alias: &str) -> LuaResult { let data_ref = lua .app_data_ref::() .ok_or(LuaError::runtime("Couldn't find RequireContextData in app data container, make sure RequireStorage::init is called on this lua instance"))?; @@ -38,13 +38,18 @@ impl RequireContext { Ok(data_ref.std.contains_key(alias)) } - pub fn require_std(lua: &Lua, require_alias: RequireAlias) -> LuaResult> { + pub(crate) fn require_std( + lua: &Lua, + require_alias: RequireAlias, + ) -> LuaResult> { let data_ref = lua .app_data_ref::() .ok_or(LuaError::runtime("Couldn't find RequireContextData in app data container, make sure RequireStorage::init is called on this lua instance"))?; if let Some(cached) = data_ref.std_cache.get(&require_alias) { - return cached.into_lua(lua)?.into_lua_multi(lua); + let multi_vec = lua.registry_value::>(cached)?; + + return Ok(LuaMultiValue::from_vec(multi_vec)); } let libraries = @@ -76,7 +81,7 @@ impl RequireContext { Ok(multi) } - pub async fn require( + pub(crate) async fn require( lua: &Lua, path_rel: PathBuf, path_abs: PathBuf, @@ -105,7 +110,9 @@ impl RequireContext { let cache = data_ref.cache.lock().await; if let Some(cached) = cache.get(&path_abs) { - return cached.into_lua(lua).into_lua_multi(lua); + let multi_vec = lua.registry_value::>(cached)?; + + return Ok(LuaMultiValue::from_vec(multi_vec)); } } diff --git a/crates/lune-std/src/globals/require/mod.rs b/crates/lune-std/src/globals/require/mod.rs index 6de386b7..7911c3f0 100644 --- a/crates/lune-std/src/globals/require/mod.rs +++ b/crates/lune-std/src/globals/require/mod.rs @@ -7,7 +7,7 @@ use mlua::prelude::*; use path::resolve_path; use std::path::PathBuf; -mod context; +pub mod context; mod path; pub async fn lua_require(lua: &Lua, path: String) -> LuaResult { diff --git a/crates/lune-std/src/globals/require/path.rs b/crates/lune-std/src/globals/require/path.rs index e11b3c7b..e889b82e 100644 --- a/crates/lune-std/src/globals/require/path.rs +++ b/crates/lune-std/src/globals/require/path.rs @@ -1,5 +1,5 @@ use mlua::prelude::*; -use std::path::{Path, PathBuf}; +use std::path::{Component, Path, PathBuf}; use tokio::fs; /// tries these alternatives on given path: @@ -14,20 +14,47 @@ pub async fn resolve_path(path: &Path) -> LuaResult { let path = append_extension(path, ext); if fs::try_exists(&path).await? { - return Ok(path); + return Ok(normalize_path(&path)); }; // try extension on given path's init let init_path = append_extension(init_path, ext); if fs::try_exists(&init_path).await? { - return Ok(init_path); + return Ok(normalize_path(&init_path)); }; } Err(LuaError::runtime("Could not resolve path")) } +pub fn normalize_path(path: &Path) -> PathBuf { + let mut components = path.components().peekable(); + let mut ret = if let Some(c @ Component::Prefix(..)) = components.clone().peek() { + components.next(); + PathBuf::from(c.as_os_str()) + } else { + PathBuf::new() + }; + + for component in components { + match component { + Component::Prefix(..) => unreachable!(), + Component::RootDir => { + ret.push(component.as_os_str()); + } + Component::CurDir => {} + Component::ParentDir => { + ret.pop(); + } + Component::Normal(c) => { + ret.push(c); + } + } + } + ret +} + fn append_extension(path: impl Into, ext: &'static str) -> PathBuf { let mut new = path.into(); match new.extension() { diff --git a/crates/lune-std/src/lib.rs b/crates/lune-std/src/lib.rs index 8de5d4aa..f91d9b34 100644 --- a/crates/lune-std/src/lib.rs +++ b/crates/lune-std/src/lib.rs @@ -1,5 +1,7 @@ #![allow(clippy::cargo_common_metadata)] +pub use globals::require::context::RequireContext; + use mlua::prelude::*; mod global; From 3d9bb892558a642e2b0f7e5af9afc8d0f15ecc6a Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 23 Aug 2024 04:29:17 +0330 Subject: [PATCH 16/35] drop app container early --- crates/lune-std/src/globals/require/context.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/lune-std/src/globals/require/context.rs b/crates/lune-std/src/globals/require/context.rs index e3ab9991..ab7f693e 100644 --- a/crates/lune-std/src/globals/require/context.rs +++ b/crates/lune-std/src/globals/require/context.rs @@ -72,6 +72,8 @@ impl RequireContext { let mutli_clone = multi.clone(); let multi_reg = lua.create_registry_value(mutli_clone.into_vec())?; + drop(data_ref); + let mut data = lua .app_data_mut::() .ok_or(LuaError::runtime("Couldn't find RequireContextData in app data container, make sure RequireStorage::init is called on this lua instance"))?; From eaa1c4d16b3e7982de64e05868cfd90045476845 Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 23 Aug 2024 15:00:31 +0330 Subject: [PATCH 17/35] document requirecontext --- .../lune-std/src/globals/require/context.rs | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/crates/lune-std/src/globals/require/context.rs b/crates/lune-std/src/globals/require/context.rs index ab7f693e..b827ae20 100644 --- a/crates/lune-std/src/globals/require/context.rs +++ b/crates/lune-std/src/globals/require/context.rs @@ -22,9 +22,18 @@ struct RequireContextData<'a> { pub struct RequireContext {} impl RequireContext { + /** + + # Errors + + - when `RequireContext::init` is called more than once on the same `Lua` instance + + */ pub fn init(lua: &Lua) -> LuaResult<()> { if lua.set_app_data(RequireContextData::default()).is_some() { - Err(LuaError::runtime("RequireContext::init got called twice")) + Err(LuaError::runtime( + "RequireContext::init got called twice on the same Lua instance", + )) } else { Ok(()) } @@ -172,6 +181,26 @@ impl RequireContext { Ok(multi) } + /** + + add a standard library into the require function + + # Example + + ```rs + inject_std(lua, "lune", LuneStandardLibrary::Task)?; + ``` + + ```luau + -- luau + local task = require("@lune/task") + ``` + + # Errors + + - when `RequireStorage::init` isn't called + + */ pub fn inject_std( lua: &Lua, alias: &'static str, From edb3041090a8872c0c949179e1efb08acb39771e Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 23 Aug 2024 15:15:24 +0330 Subject: [PATCH 18/35] impl require error enum --- .../lune-std/src/globals/require/context.rs | 79 +++++++++---------- crates/lune-std/src/globals/require/mod.rs | 42 ++++++++-- 2 files changed, 72 insertions(+), 49 deletions(-) diff --git a/crates/lune-std/src/globals/require/context.rs b/crates/lune-std/src/globals/require/context.rs index b827ae20..c38c694a 100644 --- a/crates/lune-std/src/globals/require/context.rs +++ b/crates/lune-std/src/globals/require/context.rs @@ -9,6 +9,8 @@ use tokio::{ }, }; +use super::RequireError; + /// The private struct that's stored in mlua's app data container #[derive(Debug, Default)] struct RequireContextData<'a> { @@ -29,20 +31,18 @@ impl RequireContext { - when `RequireContext::init` is called more than once on the same `Lua` instance */ - pub fn init(lua: &Lua) -> LuaResult<()> { + pub fn init(lua: &Lua) -> Result<(), RequireError> { if lua.set_app_data(RequireContextData::default()).is_some() { - Err(LuaError::runtime( - "RequireContext::init got called twice on the same Lua instance", - )) + Err(RequireError::RequireContextInitCalledTwice) } else { Ok(()) } } - pub(crate) fn std_exists(lua: &Lua, alias: &str) -> LuaResult { + pub(crate) fn std_exists(lua: &Lua, alias: &str) -> Result { let data_ref = lua .app_data_ref::() - .ok_or(LuaError::runtime("Couldn't find RequireContextData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + .ok_or(RequireError::RequireContextNotFound)?; Ok(data_ref.std.contains_key(alias)) } @@ -50,10 +50,10 @@ impl RequireContext { pub(crate) fn require_std( lua: &Lua, require_alias: RequireAlias, - ) -> LuaResult> { + ) -> Result, RequireError> { let data_ref = lua .app_data_ref::() - .ok_or(LuaError::runtime("Couldn't find RequireContextData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + .ok_or(RequireError::RequireContextNotFound)?; if let Some(cached) = data_ref.std_cache.get(&require_alias) { let multi_vec = lua.registry_value::>(cached)?; @@ -61,21 +61,17 @@ impl RequireContext { return Ok(LuaMultiValue::from_vec(multi_vec)); } - let libraries = - data_ref - .std - .get(&require_alias.alias.as_str()) - .ok_or(mlua::Error::runtime(format!( - "Alias '{}' does not point to a built-in standard library", - require_alias.alias - )))?; - - let std = libraries - .get(require_alias.path.as_str()) - .ok_or(mlua::Error::runtime(format!( - "Library '{}' does not point to a member of '{}' standard libraries", - require_alias.path, require_alias.alias - )))?; + let libraries = data_ref.std.get(&require_alias.alias.as_str()).ok_or( + RequireError::InvalidStdAlias(require_alias.alias.to_string()), + )?; + + let std = + libraries + .get(require_alias.path.as_str()) + .ok_or(RequireError::StdMemberNotFound( + require_alias.path.to_string(), + require_alias.alias.to_string(), + ))?; let multi = std.module(lua)?; let mutli_clone = multi.clone(); @@ -84,8 +80,8 @@ impl RequireContext { drop(data_ref); let mut data = lua - .app_data_mut::() - .ok_or(LuaError::runtime("Couldn't find RequireContextData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + .app_data_mut::() + .ok_or(RequireError::RequireContextNotFound)?; data.std_cache.insert(require_alias, multi_reg); @@ -96,18 +92,18 @@ impl RequireContext { lua: &Lua, path_rel: PathBuf, path_abs: PathBuf, - ) -> LuaResult { + ) -> Result { // wait for module to be required // if its pending somewhere else { let data_ref = lua - .app_data_ref::() - .ok_or(LuaError::runtime("Couldn't find RequireContextData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + .app_data_ref::() + .ok_or(RequireError::RequireContextNotFound)?; - let pending = data_ref.pending.try_lock().into_lua_err()?; + let pending = data_ref.pending.try_lock()?; if let Some(a) = pending.get(&path_abs) { - a.subscribe().recv().await.into_lua_err()?; + a.subscribe().recv().await?; } } @@ -115,8 +111,8 @@ impl RequireContext { // *if* its cached { let data_ref = lua - .app_data_ref::() - .ok_or(LuaError::runtime("Couldn't find RequireContextData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + .app_data_ref::() + .ok_or(RequireError::RequireContextNotFound)?; let cache = data_ref.cache.lock().await; @@ -130,22 +126,21 @@ impl RequireContext { // create a broadcast channel { let data_ref = lua - .app_data_ref::() - .ok_or(LuaError::runtime("Couldn't find RequireContextData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + .app_data_ref::() + .ok_or(RequireError::RequireContextNotFound)?; let (broadcast_tx, _) = broadcast::channel(1); { - let mut pending = data_ref.pending.try_lock().into_lua_err()?; + let mut pending = data_ref.pending.try_lock()?; pending.insert(path_abs.clone(), broadcast_tx); } } if !fs::try_exists(&path_abs).await? { - return Err(LuaError::runtime(format!( - "Can not require '{}' as it does not exist", - path_rel.to_string_lossy() - ))); + return Err(RequireError::InvalidRequire( + path_rel.to_string_lossy().to_string(), + )); } let content = fs::read_to_string(&path_abs).await?; @@ -161,7 +156,7 @@ impl RequireContext { let data_ref = lua .app_data_ref::() - .ok_or(LuaError::runtime("Couldn't find RequireContextData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + .ok_or(RequireError::RequireContextNotFound)?; data_ref .cache @@ -205,10 +200,10 @@ impl RequireContext { lua: &Lua, alias: &'static str, std: impl StandardLibrary + 'static, - ) -> LuaResult<()> { + ) -> Result<(), RequireError> { let mut data = lua .app_data_mut::() - .ok_or(LuaError::runtime("Couldn't find RequireContextData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + .ok_or(RequireError::RequireContextNotFound)?; if let Some(map) = data.std.get_mut(alias) { map.insert(std.name(), Box::new(std)); diff --git a/crates/lune-std/src/globals/require/mod.rs b/crates/lune-std/src/globals/require/mod.rs index 7911c3f0..1326c46d 100644 --- a/crates/lune-std/src/globals/require/mod.rs +++ b/crates/lune-std/src/globals/require/mod.rs @@ -6,17 +6,41 @@ use crate::{ use mlua::prelude::*; use path::resolve_path; use std::path::PathBuf; +use thiserror::Error; pub mod context; mod path; +#[derive(Error, Debug)] +pub enum RequireError { + #[error("failed to find RequireContextData in the app data container, make sure to call RequireContext::init first")] + RequireContextNotFound, + #[error("make sure to call RequireContext::init")] + RequireContextInitCalledTwice, + #[error("Can not require '{0}' as it does not exist")] + InvalidRequire(String), + #[error("Alias '{0}' does not point to a built-in standard library")] + InvalidStdAlias(String), + #[error("Library '{0}' does not point to a member of '{1}' standard libraries")] + StdMemberNotFound(String, String), + + #[error("IOError: {0}")] + IOError(#[from] std::io::Error), + #[error("TryLockError: {0}")] + TryLockError(#[from] tokio::sync::TryLockError), + #[error("BroadcastRecvError: {0}")] + BroadcastRecvError(#[from] tokio::sync::broadcast::error::RecvError), + #[error("LuaError: {0}")] + LuaError(#[from] mlua::Error), +} + pub async fn lua_require(lua: &Lua, path: String) -> LuaResult { let require_path_rel = PathBuf::from(path); let require_alias = path_to_alias(&require_path_rel).into_lua_err()?; if let Some(require_alias) = require_alias { - if context::RequireContext::std_exists(lua, &require_alias.alias)? { - context::RequireContext::require_std(lua, require_alias) + if context::RequireContext::std_exists(lua, &require_alias.alias).into_lua_err()? { + context::RequireContext::require_std(lua, require_alias).into_lua_err() } else { let require_path_abs = resolve_path( &Luaurc::resolve_path(lua, &require_alias) @@ -25,7 +49,9 @@ pub async fn lua_require(lua: &Lua, path: String) -> LuaResult { ) .await?; - context::RequireContext::require(lua, require_path_rel, require_path_abs).await + context::RequireContext::require(lua, require_path_rel, require_path_abs) + .await + .into_lua_err() } } else { let parent_path = get_parent_path(lua)?; @@ -38,17 +64,19 @@ pub async fn lua_require(lua: &Lua, path: String) -> LuaResult { )) })?; - context::RequireContext::require(lua, require_path_rel, require_path_abs).await + context::RequireContext::require(lua, require_path_rel, require_path_abs) + .await + .into_lua_err() } } pub fn create(lua: &Lua) -> LuaResult { - let f = lua.create_async_function(lua_require)?; + let f = lua.create_async_function(lua_require).into_lua_err()?; - context::RequireContext::init(lua)?; + context::RequireContext::init(lua).into_lua_err()?; for std in LuneStandardLibrary::ALL { - context::RequireContext::inject_std(lua, "lune", *std)?; + context::RequireContext::inject_std(lua, "lune", *std).into_lua_err()?; } f.into_lua(lua) From 9414a9fc2992239118c0d67633ca78a29c2d8b11 Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 23 Aug 2024 15:27:51 +0330 Subject: [PATCH 19/35] unlock mutex before using async --- crates/lune-std/src/globals/require/context.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/lune-std/src/globals/require/context.rs b/crates/lune-std/src/globals/require/context.rs index c38c694a..5f0329cc 100644 --- a/crates/lune-std/src/globals/require/context.rs +++ b/crates/lune-std/src/globals/require/context.rs @@ -102,8 +102,13 @@ impl RequireContext { let pending = data_ref.pending.try_lock()?; - if let Some(a) = pending.get(&path_abs) { - a.subscribe().recv().await?; + if let Some(sender) = pending.get(&path_abs) { + let mut receiver = sender.subscribe(); + + // unlock mutex before using async + drop(pending); + + receiver.recv().await?; } } From 1708c05522c601bf4a6f5368e0e09bb4dd6bc642 Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 23 Aug 2024 16:24:18 +0330 Subject: [PATCH 20/35] replace runtime err with require err --- crates/lune-std/src/globals/require/mod.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/crates/lune-std/src/globals/require/mod.rs b/crates/lune-std/src/globals/require/mod.rs index 1326c46d..e3ac55b1 100644 --- a/crates/lune-std/src/globals/require/mod.rs +++ b/crates/lune-std/src/globals/require/mod.rs @@ -58,11 +58,9 @@ pub async fn lua_require(lua: &Lua, path: String) -> LuaResult { let require_path_abs = resolve_path(&parent_path.join(&require_path_rel)) .await .map_err(|_| { - LuaError::runtime(format!( - "Can not require '{}' as it does not exist", - require_path_rel.to_string_lossy(), - )) - })?; + RequireError::InvalidRequire(require_path_rel.to_string_lossy().to_string()) + }) + .into_lua_err()?; context::RequireContext::require(lua, require_path_rel, require_path_abs) .await From 3b56b4159da172708b0cddbeb59fd173ce9be3d9 Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 23 Aug 2024 17:49:29 +0330 Subject: [PATCH 21/35] create thread when requiring module --- crates/lune-std/src/globals/require/context.rs | 14 ++++++++++---- crates/lune-std/src/globals/require/mod.rs | 2 ++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/crates/lune-std/src/globals/require/context.rs b/crates/lune-std/src/globals/require/context.rs index 5f0329cc..87550f2e 100644 --- a/crates/lune-std/src/globals/require/context.rs +++ b/crates/lune-std/src/globals/require/context.rs @@ -1,5 +1,6 @@ use crate::{library::StandardLibrary, luaurc::RequireAlias}; use mlua::prelude::*; +use mlua_luau_scheduler::{IntoLuaThread, LuaSchedulerExt}; use std::{collections::HashMap, path::PathBuf, sync::Arc}; use tokio::{ fs, @@ -149,12 +150,17 @@ impl RequireContext { } let content = fs::read_to_string(&path_abs).await?; + let thread = lua + .load(&content) + .set_name(path_abs.to_string_lossy()) + .into_lua_thread(lua)?; + + let thread_id = lua.push_thread_back(thread, ())?; + lua.wait_for_thread(thread_id).await; let multi = lua - .load(content) - .set_name(path_abs.to_string_lossy()) - .eval_async::() - .await?; + .get_thread_result(thread_id) + .ok_or(RequireError::ThreadReturnedNone)??; let mutli_clone = multi.clone(); let multi_reg = lua.create_registry_value(mutli_clone.into_vec())?; diff --git a/crates/lune-std/src/globals/require/mod.rs b/crates/lune-std/src/globals/require/mod.rs index e3ac55b1..ca6d3393 100644 --- a/crates/lune-std/src/globals/require/mod.rs +++ b/crates/lune-std/src/globals/require/mod.rs @@ -23,6 +23,8 @@ pub enum RequireError { InvalidStdAlias(String), #[error("Library '{0}' does not point to a member of '{1}' standard libraries")] StdMemberNotFound(String, String), + #[error("Thread result returned none")] + ThreadReturnedNone, #[error("IOError: {0}")] IOError(#[from] std::io::Error), From 42873f63831207e1aa0edb0b6d86e80d2ecc9d70 Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 23 Aug 2024 18:31:39 +0330 Subject: [PATCH 22/35] split require into functions --- .../lune-std/src/globals/require/context.rs | 79 +++++++++++-------- crates/lune-std/src/globals/require/mod.rs | 2 + 2 files changed, 50 insertions(+), 31 deletions(-) diff --git a/crates/lune-std/src/globals/require/context.rs b/crates/lune-std/src/globals/require/context.rs index 87550f2e..9199b8fd 100644 --- a/crates/lune-std/src/globals/require/context.rs +++ b/crates/lune-std/src/globals/require/context.rs @@ -89,44 +89,57 @@ impl RequireContext { Ok(multi) } - pub(crate) async fn require( - lua: &Lua, - path_rel: PathBuf, - path_abs: PathBuf, - ) -> Result { - // wait for module to be required - // if its pending somewhere else - { - let data_ref = lua - .app_data_ref::() - .ok_or(RequireError::RequireContextNotFound)?; + pub(crate) async fn wait_for_pending<'lua>( + lua: &'lua Lua, + path_abs: &'_ PathBuf, + ) -> Result<(), RequireError> { + let data_ref = lua + .app_data_ref::() + .ok_or(RequireError::RequireContextNotFound)?; - let pending = data_ref.pending.try_lock()?; + let pending = data_ref.pending.try_lock()?; - if let Some(sender) = pending.get(&path_abs) { - let mut receiver = sender.subscribe(); + if let Some(sender) = pending.get(path_abs) { + let mut receiver = sender.subscribe(); - // unlock mutex before using async - drop(pending); + // unlock mutex before using async + drop(pending); - receiver.recv().await?; - } + receiver.recv().await?; } - // get module from cache - // *if* its cached - { - let data_ref = lua - .app_data_ref::() - .ok_or(RequireError::RequireContextNotFound)?; + Ok(()) + } + + pub(crate) async fn from_cache<'lua>( + lua: &'lua Lua, + path_abs: &'_ PathBuf, + ) -> Result>, RequireError> { + let data_ref = lua + .app_data_ref::() + .ok_or(RequireError::RequireContextNotFound)?; - let cache = data_ref.cache.lock().await; + let cache = data_ref.cache.lock().await; - if let Some(cached) = cache.get(&path_abs) { + match cache.get(path_abs) { + Some(cached) => { let multi_vec = lua.registry_value::>(cached)?; - return Ok(LuaMultiValue::from_vec(multi_vec)); + Ok(Some(LuaMultiValue::from_vec(multi_vec))) } + None => Ok(None), + } + } + + pub(crate) async fn require( + lua: &Lua, + path_rel: PathBuf, + path_abs: PathBuf, + ) -> Result { + Self::wait_for_pending(lua, &path_abs).await?; + + if let Some(cached) = Self::from_cache(lua, &path_abs).await? { + return Ok(cached); } // create a broadcast channel @@ -135,7 +148,7 @@ impl RequireContext { .app_data_ref::() .ok_or(RequireError::RequireContextNotFound)?; - let (broadcast_tx, _) = broadcast::channel(1); + let broadcast_tx = broadcast::Sender::new(1); { let mut pending = data_ref.pending.try_lock()?; @@ -162,8 +175,7 @@ impl RequireContext { .get_thread_result(thread_id) .ok_or(RequireError::ThreadReturnedNone)??; - let mutli_clone = multi.clone(); - let multi_reg = lua.create_registry_value(mutli_clone.into_vec())?; + let multi_reg = lua.create_registry_value(multi.into_vec())?; let data_ref = lua .app_data_ref::() @@ -184,7 +196,12 @@ impl RequireContext { broadcast_tx.send(()).ok(); - Ok(multi) + match Self::from_cache(lua, &path_abs).await? { + Some(cached) => Ok(cached), + None => Err(RequireError::CacheNotFound( + path_rel.to_string_lossy().to_string(), + )), + } } /** diff --git a/crates/lune-std/src/globals/require/mod.rs b/crates/lune-std/src/globals/require/mod.rs index ca6d3393..0d2735c0 100644 --- a/crates/lune-std/src/globals/require/mod.rs +++ b/crates/lune-std/src/globals/require/mod.rs @@ -25,6 +25,8 @@ pub enum RequireError { StdMemberNotFound(String, String), #[error("Thread result returned none")] ThreadReturnedNone, + #[error("Could not get '{0}' from cache")] + CacheNotFound(String), #[error("IOError: {0}")] IOError(#[from] std::io::Error), From 14d0bcbe9cae7f059fe2344a40fda5653bdd5044 Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 23 Aug 2024 18:34:28 +0330 Subject: [PATCH 23/35] track thread --- crates/lune-std/src/globals/require/context.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/lune-std/src/globals/require/context.rs b/crates/lune-std/src/globals/require/context.rs index 9199b8fd..8e9338ad 100644 --- a/crates/lune-std/src/globals/require/context.rs +++ b/crates/lune-std/src/globals/require/context.rs @@ -169,6 +169,7 @@ impl RequireContext { .into_lua_thread(lua)?; let thread_id = lua.push_thread_back(thread, ())?; + lua.track_thread(thread_id); lua.wait_for_thread(thread_id).await; let multi = lua From e47555fd86da3267701da2cb323de0d988565127 Mon Sep 17 00:00:00 2001 From: highflowey Date: Sat, 24 Aug 2024 16:27:47 +0330 Subject: [PATCH 24/35] make StandardLibrary trait public --- crates/lune-std/src/lib.rs | 5 ++--- crates/lune-std/src/library.rs | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/crates/lune-std/src/lib.rs b/crates/lune-std/src/lib.rs index f91d9b34..508a8919 100644 --- a/crates/lune-std/src/lib.rs +++ b/crates/lune-std/src/lib.rs @@ -1,7 +1,5 @@ #![allow(clippy::cargo_common_metadata)] -pub use globals::require::context::RequireContext; - use mlua::prelude::*; mod global; @@ -11,8 +9,9 @@ mod luaurc; mod path; pub use self::global::LuneStandardGlobal; +pub use self::globals::require::context::RequireContext; pub use self::globals::version::set_global_version; -pub use self::library::LuneStandardLibrary; +pub use self::library::{LuneStandardLibrary, StandardLibrary}; /** Injects all standard globals into the given Lua state / VM. diff --git a/crates/lune-std/src/library.rs b/crates/lune-std/src/library.rs index 4f7745e8..2097bb6d 100644 --- a/crates/lune-std/src/library.rs +++ b/crates/lune-std/src/library.rs @@ -6,8 +6,18 @@ pub trait StandardLibrary where Self: Debug, { + /** + Gets the name of the library, such as `datetime` or `fs`. + */ fn name(&self) -> &'static str; + /** + Creates the Lua module for the library. + + # Errors + + If the library could not be created. + */ fn module<'lua>(&self, lua: &'lua Lua) -> LuaResult>; } @@ -49,9 +59,6 @@ impl LuneStandardLibrary { } impl StandardLibrary for LuneStandardLibrary { - /** - Gets the name of the library, such as `datetime` or `fs`. - */ #[must_use] #[rustfmt::skip] #[allow(unreachable_patterns)] @@ -72,13 +79,6 @@ impl StandardLibrary for LuneStandardLibrary { } } - /** - Creates the Lua module for the library. - - # Errors - - If the library could not be created. - */ #[rustfmt::skip] #[allow(unreachable_patterns)] fn module<'lua>(&self, lua: &'lua Lua) -> LuaResult> { From ddcda9952f7c07fd996a63724cecbcb596795e55 Mon Sep 17 00:00:00 2001 From: highflowey Date: Sun, 25 Aug 2024 20:30:46 +0330 Subject: [PATCH 25/35] document functions --- crates/lune-std/src/globals/require/path.rs | 32 +++++++++++++++++---- crates/lune-std/src/path.rs | 10 +++++++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/crates/lune-std/src/globals/require/path.rs b/crates/lune-std/src/globals/require/path.rs index e889b82e..c00ad555 100644 --- a/crates/lune-std/src/globals/require/path.rs +++ b/crates/lune-std/src/globals/require/path.rs @@ -2,10 +2,14 @@ use mlua::prelude::*; use std::path::{Component, Path, PathBuf}; use tokio::fs; -/// tries these alternatives on given path: -/// -/// * .lua and .luau extension -/// * path.join("init.luau") and path.join("init.lua") +/** + +tries these alternatives on given path if path doesn't exist + +* .lua and .luau extension +* path.join("init.luau") and path.join("init.lua") + + */ pub async fn resolve_path(path: &Path) -> LuaResult { let init_path = &path.join("init"); @@ -28,6 +32,15 @@ pub async fn resolve_path(path: &Path) -> LuaResult { Err(LuaError::runtime("Could not resolve path")) } +/** + +Removes useless components from the given path + +### Example + +`./path/./path` turns into `./path/path` + + */ pub fn normalize_path(path: &Path) -> PathBuf { let mut components = path.components().peekable(); let mut ret = if let Some(c @ Component::Prefix(..)) = components.clone().peek() { @@ -55,8 +68,17 @@ pub fn normalize_path(path: &Path) -> PathBuf { ret } +/** + +adds extension to path without replacing it's current extensions + +### Example + +appending `.luau` to `path/path.config` will return `path/path.config.luau` + + */ fn append_extension(path: impl Into, ext: &'static str) -> PathBuf { - let mut new = path.into(); + let mut new: PathBuf = path.into(); match new.extension() { // FUTURE: There's probably a better way to do this than converting to a lossy string Some(e) => new.set_extension(format!("{}.{ext}", e.to_string_lossy())), diff --git a/crates/lune-std/src/path.rs b/crates/lune-std/src/path.rs index e35f9f56..bf333095 100644 --- a/crates/lune-std/src/path.rs +++ b/crates/lune-std/src/path.rs @@ -1,5 +1,10 @@ use std::path::PathBuf; +/** + +return's the path of the script that called this function + + */ pub fn get_script_path(lua: &mlua::Lua) -> Result { let Some(debug) = lua.inspect_stack(2) else { return Err(mlua::Error::runtime("Failed to inspect stack")); @@ -17,6 +22,11 @@ pub fn get_script_path(lua: &mlua::Lua) -> Result { } } +/** + +return's the parent directory of the script that called this function + + */ pub fn get_parent_path(lua: &mlua::Lua) -> Result { let script = get_script_path(lua)?; From 35f24228cc7a7a11f58a1f8255701546dc333153 Mon Sep 17 00:00:00 2001 From: highflowey Date: Sun, 25 Aug 2024 20:36:37 +0330 Subject: [PATCH 26/35] make some functions private --- crates/lune-std/src/globals/require/context.rs | 4 ++-- crates/lune-std/src/globals/require/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/lune-std/src/globals/require/context.rs b/crates/lune-std/src/globals/require/context.rs index 8e9338ad..5ee0a370 100644 --- a/crates/lune-std/src/globals/require/context.rs +++ b/crates/lune-std/src/globals/require/context.rs @@ -89,7 +89,7 @@ impl RequireContext { Ok(multi) } - pub(crate) async fn wait_for_pending<'lua>( + async fn wait_for_pending<'lua>( lua: &'lua Lua, path_abs: &'_ PathBuf, ) -> Result<(), RequireError> { @@ -111,7 +111,7 @@ impl RequireContext { Ok(()) } - pub(crate) async fn from_cache<'lua>( + async fn from_cache<'lua>( lua: &'lua Lua, path_abs: &'_ PathBuf, ) -> Result>, RequireError> { diff --git a/crates/lune-std/src/globals/require/mod.rs b/crates/lune-std/src/globals/require/mod.rs index 0d2735c0..26d16cc1 100644 --- a/crates/lune-std/src/globals/require/mod.rs +++ b/crates/lune-std/src/globals/require/mod.rs @@ -38,7 +38,7 @@ pub enum RequireError { LuaError(#[from] mlua::Error), } -pub async fn lua_require(lua: &Lua, path: String) -> LuaResult { +async fn lua_require(lua: &Lua, path: String) -> LuaResult { let require_path_rel = PathBuf::from(path); let require_alias = path_to_alias(&require_path_rel).into_lua_err()?; From 42f5688993427f90cd4beb846048aef34ccc5210 Mon Sep 17 00:00:00 2001 From: highflowey Date: Sun, 25 Aug 2024 21:01:19 +0330 Subject: [PATCH 27/35] better error message --- crates/lune-std/src/luaurc.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/lune-std/src/luaurc.rs b/crates/lune-std/src/luaurc.rs index 697a2f3b..30e8866a 100644 --- a/crates/lune-std/src/luaurc.rs +++ b/crates/lune-std/src/luaurc.rs @@ -27,11 +27,11 @@ pub enum LuaurcError { FailedStringToPathConversion, #[error("Failed to find a path for alias '{0}' in .luaurc files")] FailedToFindAlias(String), + #[error("Failed to parse {0}\nParserError: {1}")] + FilaedToParse(PathBuf, serde_json::Error), #[error("IOError: {0}")] IOError(#[from] std::io::Error), - #[error("JsonError: {0}")] - JsonError(#[from] serde_json::Error), #[error("LuaError: {0}")] LuaError(#[from] mlua::Error), } @@ -67,7 +67,7 @@ async fn parse_luaurc(_: &mlua::Lua, path: &PathBuf) -> Result, L let content = fs::read(path).await?; serde_json::from_slice(&content) .map(Some) - .map_err(std::convert::Into::into) + .map_err(|err| LuaurcError::FilaedToParse(path.clone(), err)) } else { Ok(None) } From c6a1808e71cec61ba109f93175aaadb1318f8407 Mon Sep 17 00:00:00 2001 From: highflowey Date: Sun, 25 Aug 2024 22:05:30 +0330 Subject: [PATCH 28/35] fixed wrong error message --- crates/lune-std/src/globals/require/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/lune-std/src/globals/require/mod.rs b/crates/lune-std/src/globals/require/mod.rs index 26d16cc1..9629da71 100644 --- a/crates/lune-std/src/globals/require/mod.rs +++ b/crates/lune-std/src/globals/require/mod.rs @@ -15,7 +15,7 @@ mod path; pub enum RequireError { #[error("failed to find RequireContextData in the app data container, make sure to call RequireContext::init first")] RequireContextNotFound, - #[error("make sure to call RequireContext::init")] + #[error("RequireContext::init has been called twice on the same Lua instance")] RequireContextInitCalledTwice, #[error("Can not require '{0}' as it does not exist")] InvalidRequire(String), From a7f9c5f4f9f6dc6f7438ec9a5f438148a5ac44c2 Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 30 Aug 2024 20:38:51 +0330 Subject: [PATCH 29/35] move path_to_alias to RequireAlias::from_path --- crates/lune-std/src/globals/require/mod.rs | 4 +- crates/lune-std/src/luaurc.rs | 46 +++++++++++----------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/crates/lune-std/src/globals/require/mod.rs b/crates/lune-std/src/globals/require/mod.rs index 9629da71..7b9415b1 100644 --- a/crates/lune-std/src/globals/require/mod.rs +++ b/crates/lune-std/src/globals/require/mod.rs @@ -1,5 +1,5 @@ use crate::{ - luaurc::{path_to_alias, Luaurc}, + luaurc::{Luaurc, RequireAlias}, path::get_parent_path, LuneStandardLibrary, }; @@ -40,7 +40,7 @@ pub enum RequireError { async fn lua_require(lua: &Lua, path: String) -> LuaResult { let require_path_rel = PathBuf::from(path); - let require_alias = path_to_alias(&require_path_rel).into_lua_err()?; + let require_alias = RequireAlias::from_path(&require_path_rel).into_lua_err()?; if let Some(require_alias) = require_alias { if context::RequireContext::std_exists(lua, &require_alias.alias).into_lua_err()? { diff --git a/crates/lune-std/src/luaurc.rs b/crates/lune-std/src/luaurc.rs index 30e8866a..246e132a 100644 --- a/crates/lune-std/src/luaurc.rs +++ b/crates/lune-std/src/luaurc.rs @@ -36,29 +36,31 @@ pub enum LuaurcError { LuaError(#[from] mlua::Error), } -/// Parses path into `RequireAlias` struct -/// -/// ### Examples -/// -/// `@lune/task` becomes `Some({ alias: "lune", path: "task" })` -/// -/// `../path/script` becomes `None` -pub fn path_to_alias(path: &Path) -> Result, LuaurcError> { - if let Some(aliased_path) = path - .to_str() - .ok_or(LuaurcError::FailedStringToPathConversion)? - .strip_prefix('@') - { - let (alias, path) = aliased_path - .split_once('/') - .ok_or(LuaurcError::UsedAliasWithoutSlash)?; +impl RequireAlias { + /// Parses path into `RequireAlias` struct + /// + /// ### Examples + /// + /// `@lune/task` becomes `Some({ alias: "lune", path: "task" })` + /// + /// `../path/script` becomes `None` + pub fn from_path(path: &Path) -> Result, LuaurcError> { + if let Some(aliased_path) = path + .to_str() + .ok_or(LuaurcError::FailedStringToPathConversion)? + .strip_prefix('@') + { + let (alias, path) = aliased_path + .split_once('/') + .ok_or(LuaurcError::UsedAliasWithoutSlash)?; - Ok(Some(RequireAlias { - alias: alias.to_string(), - path: path.to_string(), - })) - } else { - Ok(None) + Ok(Some(RequireAlias { + alias: alias.to_string(), + path: path.to_string(), + })) + } else { + Ok(None) + } } } From 9279b92a1b80364969efa3ec84436b37f39c0791 Mon Sep 17 00:00:00 2001 From: highflowey Date: Fri, 30 Aug 2024 20:42:11 +0330 Subject: [PATCH 30/35] make RequireContext::init public only to crate --- crates/lune-std/src/globals/require/context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/lune-std/src/globals/require/context.rs b/crates/lune-std/src/globals/require/context.rs index 5ee0a370..75b4cd69 100644 --- a/crates/lune-std/src/globals/require/context.rs +++ b/crates/lune-std/src/globals/require/context.rs @@ -32,7 +32,7 @@ impl RequireContext { - when `RequireContext::init` is called more than once on the same `Lua` instance */ - pub fn init(lua: &Lua) -> Result<(), RequireError> { + pub(crate) fn init(lua: &Lua) -> Result<(), RequireError> { if lua.set_app_data(RequireContextData::default()).is_some() { Err(RequireError::RequireContextInitCalledTwice) } else { From 7f07c9d1071d3aed0632078b9964350199a6704b Mon Sep 17 00:00:00 2001 From: AshleyFlow Date: Thu, 17 Oct 2024 12:30:31 +0330 Subject: [PATCH 31/35] validate every field in luaurc files --- crates/lune-std/src/luaurc.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/crates/lune-std/src/luaurc.rs b/crates/lune-std/src/luaurc.rs index 246e132a..50fb0662 100644 --- a/crates/lune-std/src/luaurc.rs +++ b/crates/lune-std/src/luaurc.rs @@ -1,5 +1,6 @@ use crate::path::get_parent_path; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; +use serde_json::Value as JsonValue; use std::{ collections::HashMap, env::current_dir, @@ -14,8 +15,27 @@ pub struct RequireAlias { pub path: String, } +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +enum LuauLanguageMode { + NoCheck, + NonStrict, + Strict, +} + #[derive(Debug, Clone, PartialEq, Deserialize)] pub struct Luaurc { + #[serde(skip_serializing_if = "Option::is_none")] + language_mode: Option, + #[serde(skip_serializing_if = "Option::is_none")] + lint: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + lint_errors: Option, + #[serde(skip_serializing_if = "Option::is_none")] + type_errors: Option, + #[serde(skip_serializing_if = "Option::is_none")] + globals: Option>, + #[serde(skip_serializing_if = "Option::is_none")] aliases: Option>, } From 76da13d99ea4246c0184fb0b1623b70bb87bd4fa Mon Sep 17 00:00:00 2001 From: AshleyFlow Date: Thu, 17 Oct 2024 12:39:56 +0330 Subject: [PATCH 32/35] eliminate unnecessary checks for files --- crates/lune-std/src/globals/require/context.rs | 6 ------ crates/lune-std/src/luaurc.rs | 9 +++++++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/crates/lune-std/src/globals/require/context.rs b/crates/lune-std/src/globals/require/context.rs index 75b4cd69..0688e449 100644 --- a/crates/lune-std/src/globals/require/context.rs +++ b/crates/lune-std/src/globals/require/context.rs @@ -156,12 +156,6 @@ impl RequireContext { } } - if !fs::try_exists(&path_abs).await? { - return Err(RequireError::InvalidRequire( - path_rel.to_string_lossy().to_string(), - )); - } - let content = fs::read_to_string(&path_abs).await?; let thread = lua .load(&content) diff --git a/crates/lune-std/src/luaurc.rs b/crates/lune-std/src/luaurc.rs index 50fb0662..b5d6d0be 100644 --- a/crates/lune-std/src/luaurc.rs +++ b/crates/lune-std/src/luaurc.rs @@ -84,9 +84,14 @@ impl RequireAlias { } } +/** +# Errors + +* when `serde_json` fails to deserialize content of the file + + */ async fn parse_luaurc(_: &mlua::Lua, path: &PathBuf) -> Result, LuaurcError> { - if fs::try_exists(path).await? { - let content = fs::read(path).await?; + if let Ok(content) = fs::read(path).await { serde_json::from_slice(&content) .map(Some) .map_err(|err| LuaurcError::FilaedToParse(path.clone(), err)) From 6ce45636558cedc689e4dce08c0447ec33720170 Mon Sep 17 00:00:00 2001 From: AshleyFlow Date: Thu, 17 Oct 2024 12:40:29 +0330 Subject: [PATCH 33/35] fix typo --- crates/lune-std/src/luaurc.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/lune-std/src/luaurc.rs b/crates/lune-std/src/luaurc.rs index b5d6d0be..ef61d127 100644 --- a/crates/lune-std/src/luaurc.rs +++ b/crates/lune-std/src/luaurc.rs @@ -48,7 +48,7 @@ pub enum LuaurcError { #[error("Failed to find a path for alias '{0}' in .luaurc files")] FailedToFindAlias(String), #[error("Failed to parse {0}\nParserError: {1}")] - FilaedToParse(PathBuf, serde_json::Error), + FailedToParse(PathBuf, serde_json::Error), #[error("IOError: {0}")] IOError(#[from] std::io::Error), @@ -94,7 +94,7 @@ async fn parse_luaurc(_: &mlua::Lua, path: &PathBuf) -> Result, L if let Ok(content) = fs::read(path).await { serde_json::from_slice(&content) .map(Some) - .map_err(|err| LuaurcError::FilaedToParse(path.clone(), err)) + .map_err(|err| LuaurcError::FailedToParse(path.clone(), err)) } else { Ok(None) } From 981d3235566b76672bf5ef4f883e08f01e7eaa4c Mon Sep 17 00:00:00 2001 From: AshleyFlow Date: Thu, 17 Oct 2024 13:47:53 +0330 Subject: [PATCH 34/35] eliminate unnecessary checks for files in require --- .../lune-std/src/globals/require/context.rs | 86 +++++++++++-------- crates/lune-std/src/globals/require/mod.rs | 65 ++++++++++---- crates/lune-std/src/globals/require/path.rs | 72 +--------------- 3 files changed, 101 insertions(+), 122 deletions(-) diff --git a/crates/lune-std/src/globals/require/context.rs b/crates/lune-std/src/globals/require/context.rs index 0688e449..ceaeee39 100644 --- a/crates/lune-std/src/globals/require/context.rs +++ b/crates/lune-std/src/globals/require/context.rs @@ -43,7 +43,7 @@ impl RequireContext { pub(crate) fn std_exists(lua: &Lua, alias: &str) -> Result { let data_ref = lua .app_data_ref::() - .ok_or(RequireError::RequireContextNotFound)?; + .ok_or_else(|| RequireError::RequireContextNotFound)?; Ok(data_ref.std.contains_key(alias)) } @@ -54,7 +54,7 @@ impl RequireContext { ) -> Result, RequireError> { let data_ref = lua .app_data_ref::() - .ok_or(RequireError::RequireContextNotFound)?; + .ok_or_else(|| RequireError::RequireContextNotFound)?; if let Some(cached) = data_ref.std_cache.get(&require_alias) { let multi_vec = lua.registry_value::>(cached)?; @@ -62,17 +62,17 @@ impl RequireContext { return Ok(LuaMultiValue::from_vec(multi_vec)); } - let libraries = data_ref.std.get(&require_alias.alias.as_str()).ok_or( - RequireError::InvalidStdAlias(require_alias.alias.to_string()), - )?; + let libraries = data_ref + .std + .get(&require_alias.alias.as_str()) + .ok_or_else(|| RequireError::InvalidStdAlias(require_alias.alias.to_string()))?; - let std = - libraries - .get(require_alias.path.as_str()) - .ok_or(RequireError::StdMemberNotFound( - require_alias.path.to_string(), - require_alias.alias.to_string(), - ))?; + let std = libraries.get(require_alias.path.as_str()).ok_or_else(|| { + RequireError::StdMemberNotFound( + require_alias.path.to_string(), + require_alias.alias.to_string(), + ) + })?; let multi = std.module(lua)?; let mutli_clone = multi.clone(); @@ -82,7 +82,7 @@ impl RequireContext { let mut data = lua .app_data_mut::() - .ok_or(RequireError::RequireContextNotFound)?; + .ok_or_else(|| RequireError::RequireContextNotFound)?; data.std_cache.insert(require_alias, multi_reg); @@ -95,7 +95,7 @@ impl RequireContext { ) -> Result<(), RequireError> { let data_ref = lua .app_data_ref::() - .ok_or(RequireError::RequireContextNotFound)?; + .ok_or_else(|| RequireError::RequireContextNotFound)?; let pending = data_ref.pending.try_lock()?; @@ -111,13 +111,33 @@ impl RequireContext { Ok(()) } + fn is_pending(lua: &Lua, path_abs: &PathBuf) -> Result { + let data_ref = lua + .app_data_ref::() + .ok_or_else(|| RequireError::RequireContextNotFound)?; + + let pending = data_ref.pending.try_lock()?; + + Ok(pending.get(path_abs).is_some()) + } + + fn is_cached(lua: &Lua, path_abs: &PathBuf) -> Result { + let data_ref = lua + .app_data_ref::() + .ok_or_else(|| RequireError::RequireContextNotFound)?; + + let cache = data_ref.cache.try_lock()?; + + Ok(cache.get(path_abs).is_some()) + } + async fn from_cache<'lua>( lua: &'lua Lua, path_abs: &'_ PathBuf, - ) -> Result>, RequireError> { + ) -> Result, RequireError> { let data_ref = lua .app_data_ref::() - .ok_or(RequireError::RequireContextNotFound)?; + .ok_or_else(|| RequireError::RequireContextNotFound)?; let cache = data_ref.cache.lock().await; @@ -125,28 +145,32 @@ impl RequireContext { Some(cached) => { let multi_vec = lua.registry_value::>(cached)?; - Ok(Some(LuaMultiValue::from_vec(multi_vec))) + Ok(LuaMultiValue::from_vec(multi_vec)) } - None => Ok(None), + None => Err(RequireError::CacheNotFound( + path_abs.to_string_lossy().to_string(), + )), } } pub(crate) async fn require( lua: &Lua, - path_rel: PathBuf, path_abs: PathBuf, ) -> Result { - Self::wait_for_pending(lua, &path_abs).await?; - - if let Some(cached) = Self::from_cache(lua, &path_abs).await? { - return Ok(cached); + if Self::is_pending(lua, &path_abs)? { + Self::wait_for_pending(lua, &path_abs).await?; + return Self::from_cache(lua, &path_abs).await; + } else if Self::is_cached(lua, &path_abs)? { + return Self::from_cache(lua, &path_abs).await; } + let content = fs::read_to_string(&path_abs).await?; + // create a broadcast channel { let data_ref = lua .app_data_ref::() - .ok_or(RequireError::RequireContextNotFound)?; + .ok_or_else(|| RequireError::RequireContextNotFound)?; let broadcast_tx = broadcast::Sender::new(1); @@ -156,7 +180,6 @@ impl RequireContext { } } - let content = fs::read_to_string(&path_abs).await?; let thread = lua .load(&content) .set_name(path_abs.to_string_lossy()) @@ -168,13 +191,13 @@ impl RequireContext { let multi = lua .get_thread_result(thread_id) - .ok_or(RequireError::ThreadReturnedNone)??; + .ok_or_else(|| RequireError::ThreadReturnedNone)??; let multi_reg = lua.create_registry_value(multi.into_vec())?; let data_ref = lua .app_data_ref::() - .ok_or(RequireError::RequireContextNotFound)?; + .ok_or_else(|| RequireError::RequireContextNotFound)?; data_ref .cache @@ -191,12 +214,7 @@ impl RequireContext { broadcast_tx.send(()).ok(); - match Self::from_cache(lua, &path_abs).await? { - Some(cached) => Ok(cached), - None => Err(RequireError::CacheNotFound( - path_rel.to_string_lossy().to_string(), - )), - } + Self::from_cache(lua, &path_abs).await } /** @@ -226,7 +244,7 @@ impl RequireContext { ) -> Result<(), RequireError> { let mut data = lua .app_data_mut::() - .ok_or(RequireError::RequireContextNotFound)?; + .ok_or_else(|| RequireError::RequireContextNotFound)?; if let Some(map) = data.std.get_mut(alias) { map.insert(std.name(), Box::new(std)); diff --git a/crates/lune-std/src/globals/require/mod.rs b/crates/lune-std/src/globals/require/mod.rs index 7b9415b1..5b67c362 100644 --- a/crates/lune-std/src/globals/require/mod.rs +++ b/crates/lune-std/src/globals/require/mod.rs @@ -3,8 +3,9 @@ use crate::{ path::get_parent_path, LuneStandardLibrary, }; +use lune_utils::path::clean_path_and_make_absolute; use mlua::prelude::*; -use path::resolve_path; +use path::append_extension; use std::path::PathBuf; use thiserror::Error; @@ -38,6 +39,44 @@ pub enum RequireError { LuaError(#[from] mlua::Error), } +/** +tries different extensions on the path and if all alternatives fail, we'll try to look for an init file + */ +async fn try_alternatives(lua: &Lua, require_path_abs: PathBuf) -> LuaResult { + for ext in ["lua", "luau"] { + // try the path with ext + let ext_path = append_extension(&require_path_abs, ext); + + match context::RequireContext::require(lua, ext_path).await { + Ok(res) => return Ok(res), + Err(err) => { + if !matches!(err, RequireError::IOError(_)) { + return Err(err).into_lua_err(); + }; + } + }; + } + + for ext in ["lua", "luau"] { + // append init to path and try it with ext + let ext_path = append_extension(require_path_abs.join("init"), ext); + + match context::RequireContext::require(lua, ext_path).await { + Ok(res) => return Ok(res), + Err(err) => { + if !matches!(err, RequireError::IOError(_)) { + return Err(err).into_lua_err(); + }; + } + }; + } + + Err(RequireError::InvalidRequire( + require_path_abs.to_string_lossy().to_string(), + )) + .into_lua_err() +} + async fn lua_require(lua: &Lua, path: String) -> LuaResult { let require_path_rel = PathBuf::from(path); let require_alias = RequireAlias::from_path(&require_path_rel).into_lua_err()?; @@ -46,29 +85,19 @@ async fn lua_require(lua: &Lua, path: String) -> LuaResult { if context::RequireContext::std_exists(lua, &require_alias.alias).into_lua_err()? { context::RequireContext::require_std(lua, require_alias).into_lua_err() } else { - let require_path_abs = resolve_path( - &Luaurc::resolve_path(lua, &require_alias) + let require_path_abs = clean_path_and_make_absolute( + Luaurc::resolve_path(lua, &require_alias) .await .into_lua_err()?, - ) - .await?; + ); - context::RequireContext::require(lua, require_path_rel, require_path_abs) - .await - .into_lua_err() + try_alternatives(lua, require_path_abs).await } } else { let parent_path = get_parent_path(lua)?; - let require_path_abs = resolve_path(&parent_path.join(&require_path_rel)) - .await - .map_err(|_| { - RequireError::InvalidRequire(require_path_rel.to_string_lossy().to_string()) - }) - .into_lua_err()?; - - context::RequireContext::require(lua, require_path_rel, require_path_abs) - .await - .into_lua_err() + let require_path_abs = clean_path_and_make_absolute(parent_path.join(&require_path_rel)); + + try_alternatives(lua, require_path_abs).await } } diff --git a/crates/lune-std/src/globals/require/path.rs b/crates/lune-std/src/globals/require/path.rs index c00ad555..b1e932ea 100644 --- a/crates/lune-std/src/globals/require/path.rs +++ b/crates/lune-std/src/globals/require/path.rs @@ -1,72 +1,4 @@ -use mlua::prelude::*; -use std::path::{Component, Path, PathBuf}; -use tokio::fs; - -/** - -tries these alternatives on given path if path doesn't exist - -* .lua and .luau extension -* path.join("init.luau") and path.join("init.lua") - - */ -pub async fn resolve_path(path: &Path) -> LuaResult { - let init_path = &path.join("init"); - - for ext in ["lua", "luau"] { - // try extension on given path - let path = append_extension(path, ext); - - if fs::try_exists(&path).await? { - return Ok(normalize_path(&path)); - }; - - // try extension on given path's init - let init_path = append_extension(init_path, ext); - - if fs::try_exists(&init_path).await? { - return Ok(normalize_path(&init_path)); - }; - } - - Err(LuaError::runtime("Could not resolve path")) -} - -/** - -Removes useless components from the given path - -### Example - -`./path/./path` turns into `./path/path` - - */ -pub fn normalize_path(path: &Path) -> PathBuf { - let mut components = path.components().peekable(); - let mut ret = if let Some(c @ Component::Prefix(..)) = components.clone().peek() { - components.next(); - PathBuf::from(c.as_os_str()) - } else { - PathBuf::new() - }; - - for component in components { - match component { - Component::Prefix(..) => unreachable!(), - Component::RootDir => { - ret.push(component.as_os_str()); - } - Component::CurDir => {} - Component::ParentDir => { - ret.pop(); - } - Component::Normal(c) => { - ret.push(c); - } - } - } - ret -} +use std::path::PathBuf; /** @@ -77,7 +9,7 @@ adds extension to path without replacing it's current extensions appending `.luau` to `path/path.config` will return `path/path.config.luau` */ -fn append_extension(path: impl Into, ext: &'static str) -> PathBuf { +pub fn append_extension(path: impl Into, ext: &'static str) -> PathBuf { let mut new: PathBuf = path.into(); match new.extension() { // FUTURE: There's probably a better way to do this than converting to a lossy string From 2a49830700599de1d569396d51d958bc23ebe5fc Mon Sep 17 00:00:00 2001 From: AshleyFlow Date: Thu, 17 Oct 2024 14:32:31 +0330 Subject: [PATCH 35/35] create channel before reading file --- .../lune-std/src/globals/require/context.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/crates/lune-std/src/globals/require/context.rs b/crates/lune-std/src/globals/require/context.rs index ceaeee39..753b3730 100644 --- a/crates/lune-std/src/globals/require/context.rs +++ b/crates/lune-std/src/globals/require/context.rs @@ -164,8 +164,6 @@ impl RequireContext { return Self::from_cache(lua, &path_abs).await; } - let content = fs::read_to_string(&path_abs).await?; - // create a broadcast channel { let data_ref = lua @@ -180,6 +178,23 @@ impl RequireContext { } } + let content = match fs::read_to_string(&path_abs).await { + Ok(content) => content, + Err(err) => { + // this error is expected to happen in most cases + // because this function will be retried on the same path + // with different extensions when it fails here + + let data_ref = lua + .app_data_ref::() + .ok_or_else(|| RequireError::RequireContextNotFound)?; + + data_ref.pending.lock().await.remove(&path_abs); + + return Err(err.into()); + } + }; + let thread = lua .load(&content) .set_name(path_abs.to_string_lossy())