diff --git a/src/cli/cli.rs b/src/cli/cli.rs index e93a991..4195b52 100644 --- a/src/cli/cli.rs +++ b/src/cli/cli.rs @@ -3,7 +3,7 @@ use std::fs::read_to_string; use anyhow::Result; use clap::{CommandFactory, Parser}; -use lune::Lune; +use lune::run_lune; use crate::utils::{files::find_parse_file_path, github::Client as GithubClient}; @@ -96,11 +96,7 @@ impl Cli { // Display the file path relative to cwd with no extensions in stack traces let file_display_name = file_path.with_extension("").display().to_string(); // Create a new lune object with all globals & run the script - Lune::new()? - .with_args(self.script_args)? - .with_default_globals()? - .run_with_name(&file_contents, &file_display_name) - .await?; + run_lune(&file_display_name, &file_contents, self.script_args).await?; Ok(()) } } diff --git a/src/lib/globals/mod.rs b/src/lib/globals/mod.rs index 4ecc1c5..18515f9 100644 --- a/src/lib/globals/mod.rs +++ b/src/lib/globals/mod.rs @@ -9,3 +9,5 @@ pub use fs::new as new_fs; pub use net::new as new_net; pub use process::new as new_process; pub use task::new as new_task; + +pub use task::WaitingThread as WaitingTaskThread; diff --git a/src/lib/globals/task.rs b/src/lib/globals/task.rs index 7c30a15..801af47 100644 --- a/src/lib/globals/task.rs +++ b/src/lib/globals/task.rs @@ -1,13 +1,24 @@ -use std::time::Duration; +use std::{ + sync::{Arc, Mutex}, + time::Duration, +}; -use mlua::{Function, Lua, Result, Table, Value, Variadic}; +use mlua::{Function, Lua, Result, Table, Thread, Value, Variadic}; use tokio::time; use crate::utils::table_builder::ReadonlyTableBuilder; const DEFAULT_SLEEP_DURATION: f32 = 1.0 / 60.0; -pub fn new(lua: &Lua) -> Result { +pub struct WaitingThread<'a> { + is_delayed_for: Option, + is_deferred: Option, + thread: Thread<'a>, + args: Variadic>, +} + +pub fn new<'a>(lua: &'a Lua, threads: &Arc>>>) -> Result> { + // TODO: Figure out how to insert into threads vec ReadonlyTableBuilder::new(lua)? .with_async_function( "defer", diff --git a/src/lib/lib.rs b/src/lib/lib.rs index b3d539d..d5af275 100644 --- a/src/lib/lib.rs +++ b/src/lib/lib.rs @@ -1,4 +1,6 @@ -use anyhow::Result; +use std::sync::{Arc, Mutex}; + +use anyhow::{bail, Result}; use mlua::Lua; pub mod globals; @@ -6,63 +8,39 @@ pub mod utils; use crate::{ globals::{new_console, new_fs, new_net, new_process, new_task}, - utils::formatting::{pretty_print_luau_error, print_label}, + utils::formatting::{format_label, pretty_format_luau_error}, }; -pub struct Lune { - lua: Lua, - args: Vec, -} - -impl Lune { - pub fn new() -> Result { - let lua = Lua::new(); - lua.sandbox(true)?; - Ok(Self { lua, args: vec![] }) +pub async fn run_lune(name: &str, chunk: &str, args: Vec) -> Result<()> { + let lua = Lua::new(); + let threads = Arc::new(Mutex::new(Vec::new())); + lua.sandbox(true)?; + // Add in all globals + { + let globals = lua.globals(); + globals.raw_set("console", new_console(&lua)?)?; + globals.raw_set("fs", new_fs(&lua)?)?; + globals.raw_set("net", new_net(&lua)?)?; + globals.raw_set("process", new_process(&lua, args.clone())?)?; + globals.raw_set("task", new_task(&lua, &threads)?)?; + globals.set_readonly(true); } - - pub fn with_args(mut self, args: Vec) -> Result { - self.args = args; - Ok(self) - } - - pub fn with_default_globals(self) -> Result { - { - let globals = self.lua.globals(); - globals.raw_set("console", new_console(&self.lua)?)?; - globals.raw_set("fs", new_fs(&self.lua)?)?; - globals.raw_set("net", new_net(&self.lua)?)?; - globals.raw_set("process", new_process(&self.lua, self.args.clone())?)?; - globals.raw_set("task", new_task(&self.lua)?)?; - globals.set_readonly(true); - } - Ok(self) - } - - pub async fn run(&self, chunk: &str) -> Result<()> { - self.handle_result(self.lua.load(chunk).exec_async().await) - } - - pub async fn run_with_name(&self, chunk: &str, name: &str) -> Result<()> { - self.handle_result(self.lua.load(chunk).set_name(name)?.exec_async().await) - } - - fn handle_result(&self, result: mlua::Result<()>) -> Result<()> { - match result { - Ok(_) => Ok(()), - Err(e) => { - eprintln!(); - print_label("ERROR").unwrap(); - eprintln!(); - pretty_print_luau_error(&e); - Err(e.into()) - } - } + // Run the requested chunk asynchronously + let result = lua.load(chunk).set_name(name)?.exec_async().await; + match result { + Ok(_) => Ok(()), + Err(e) => bail!( + "\n{}\n{}", + format_label("ERROR"), + pretty_format_luau_error(&e) + ), } } #[cfg(test)] mod tests { + use crate::run_lune; + macro_rules! run_tests { ($($name:ident: $value:expr,)*) => { $( @@ -75,16 +53,10 @@ mod tests { let path = std::env::current_dir() .unwrap() .join(format!("src/tests/{}.luau", $value)); - let lune = crate::Lune::new() - .unwrap() - .with_args(args) - .unwrap() - .with_default_globals() - .unwrap(); let script = tokio::fs::read_to_string(&path) .await .unwrap(); - if let Err(e) = lune.run_with_name(&script, $value).await { + if let Err(e) = run_lune($value, &script, args).await { panic!("Test '{}' failed!\n{}", $value, e.to_string()) } } diff --git a/src/lib/utils/formatting.rs b/src/lib/utils/formatting.rs index b601482..08f85e5 100644 --- a/src/lib/utils/formatting.rs +++ b/src/lib/utils/formatting.rs @@ -39,8 +39,8 @@ fn can_be_plain_lua_table_key(s: &mlua::String) -> bool { } } -pub fn print_label>(s: S) -> mlua::Result<()> { - print!( +pub fn format_label>(s: S) -> String { + format!( "{}[{}{}{}{}]{} ", STYLE_BOLD, match s.as_ref().to_ascii_lowercase().as_str() { @@ -53,7 +53,11 @@ pub fn print_label>(s: S) -> mlua::Result<()> { COLOR_RESET, STYLE_BOLD, STYLE_RESET - ); + ) +} + +pub fn print_label>(s: S) -> mlua::Result<()> { + print!("{}", format_label(s)); flush_stdout()?; Ok(()) } @@ -186,34 +190,45 @@ pub fn pretty_format_multi_value(multi: &MultiValue) -> mlua::Result { Ok(buffer) } -pub fn pretty_print_luau_error(e: &mlua::Error) { +pub fn pretty_format_luau_error(e: &mlua::Error) -> String { match e { mlua::Error::RuntimeError(e) => { - eprintln!("{e}"); + let err_string = e.to_string(); + let mut err_lines = err_string.lines().collect::>(); + for (index, line) in err_lines.clone().iter().enumerate().rev() { + if *line == "stack traceback:" { + err_lines[index] = "Stack Begin"; + break; + } + } + err_lines.push("Stack End"); + err_lines.join("\n") } mlua::Error::CallbackError { cause, traceback } => { - pretty_print_luau_error(cause.as_ref()); - eprintln!("Traceback:"); - eprintln!("{}", traceback.strip_prefix("stack traceback:\n").unwrap()); + format!( + "{}\nStack Begin{}Stack End", + pretty_format_luau_error(cause.as_ref()), + traceback.strip_prefix("stack traceback:\n").unwrap() + ) } mlua::Error::ToLuaConversionError { from, to, message } => { let msg = message .clone() .map_or_else(String::new, |m| format!("\nDetails:\n\t{m}")); - eprintln!( + format!( "Failed to convert Rust type '{}' into Luau type '{}'!{}", from, to, msg - ); + ) } mlua::Error::FromLuaConversionError { from, to, message } => { let msg = message .clone() .map_or_else(String::new, |m| format!("\nDetails:\n\t{m}")); - eprintln!( + format!( "Failed to convert Luau type '{}' into Rust type '{}'!{}", from, to, msg - ); + ) } - e => eprintln!("{e}"), + e => format!("{e}"), } }