diff --git a/src/cli/cli.rs b/src/cli/cli.rs
index 18b5a8f..d9bc98b 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::run_lune;
+use lune::Lune;
use crate::utils::{files::find_parse_file_path, github::Client as GithubClient};
@@ -96,7 +96,8 @@ 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
- if let Err(e) = run_lune(&file_display_name, &file_contents, self.script_args).await {
+ let lune = Lune::new().with_args(self.script_args).with_all_globals();
+ if let Err(e) = lune.run(&file_display_name, &file_contents).await {
eprintln!("{e}");
std::process::exit(1);
};
diff --git a/src/lib/globals/console.rs b/src/lib/globals/console.rs
index e030a29..14b543f 100644
--- a/src/lib/globals/console.rs
+++ b/src/lib/globals/console.rs
@@ -1,11 +1,11 @@
-use mlua::{Lua, MultiValue, Result, Table};
+use mlua::{Lua, MultiValue, Result};
use crate::utils::{
formatting::{flush_stdout, pretty_format_multi_value, print_color, print_label, print_style},
table_builder::ReadonlyTableBuilder,
};
-pub async fn new(lua: &Lua) -> Result
{
+pub async fn create(lua: Lua) -> Result {
let print = |args: &MultiValue, throw: bool| -> Result<()> {
let s = pretty_format_multi_value(args)?;
if throw {
@@ -16,26 +16,30 @@ pub async fn new(lua: &Lua) -> Result {
flush_stdout()?;
Ok(())
};
- ReadonlyTableBuilder::new(lua)?
- .with_function("resetColor", |_, _: ()| print_color("reset"))?
- .with_function("setColor", |_, color: String| print_color(color))?
- .with_function("resetStyle", |_, _: ()| print_style("reset"))?
- .with_function("setStyle", |_, style: String| print_style(style))?
- .with_function("format", |_, args: MultiValue| {
- pretty_format_multi_value(&args)
- })?
- .with_function("log", move |_, args: MultiValue| print(&args, false))?
- .with_function("info", move |_, args: MultiValue| {
- print_label("info")?;
- print(&args, false)
- })?
- .with_function("warn", move |_, args: MultiValue| {
- print_label("warn")?;
- print(&args, false)
- })?
- .with_function("error", move |_, args: MultiValue| {
- print_label("error")?;
- print(&args, true)
- })?
- .build()
+ lua.globals().raw_set(
+ "console",
+ ReadonlyTableBuilder::new(&lua)?
+ .with_function("resetColor", |_, _: ()| print_color("reset"))?
+ .with_function("setColor", |_, color: String| print_color(color))?
+ .with_function("resetStyle", |_, _: ()| print_style("reset"))?
+ .with_function("setStyle", |_, style: String| print_style(style))?
+ .with_function("format", |_, args: MultiValue| {
+ pretty_format_multi_value(&args)
+ })?
+ .with_function("log", move |_, args: MultiValue| print(&args, false))?
+ .with_function("info", move |_, args: MultiValue| {
+ print_label("info")?;
+ print(&args, false)
+ })?
+ .with_function("warn", move |_, args: MultiValue| {
+ print_label("warn")?;
+ print(&args, false)
+ })?
+ .with_function("error", move |_, args: MultiValue| {
+ print_label("error")?;
+ print(&args, true)
+ })?
+ .build()?,
+ )?;
+ Ok(lua)
}
diff --git a/src/lib/globals/fs.rs b/src/lib/globals/fs.rs
index 9afd785..ab3c1b1 100644
--- a/src/lib/globals/fs.rs
+++ b/src/lib/globals/fs.rs
@@ -1,21 +1,25 @@
use std::path::{PathBuf, MAIN_SEPARATOR};
-use mlua::{Lua, Result, Table};
+use mlua::{Lua, Result};
use tokio::fs;
use crate::utils::table_builder::ReadonlyTableBuilder;
-pub async fn new(lua: &Lua) -> Result {
- ReadonlyTableBuilder::new(lua)?
- .with_async_function("readFile", fs_read_file)?
- .with_async_function("readDir", fs_read_dir)?
- .with_async_function("writeFile", fs_write_file)?
- .with_async_function("writeDir", fs_write_dir)?
- .with_async_function("removeFile", fs_remove_file)?
- .with_async_function("removeDir", fs_remove_dir)?
- .with_async_function("isFile", fs_is_file)?
- .with_async_function("isDir", fs_is_dir)?
- .build()
+pub async fn create(lua: Lua) -> Result {
+ lua.globals().raw_set(
+ "fs",
+ ReadonlyTableBuilder::new(&lua)?
+ .with_async_function("readFile", fs_read_file)?
+ .with_async_function("readDir", fs_read_dir)?
+ .with_async_function("writeFile", fs_write_file)?
+ .with_async_function("writeDir", fs_write_dir)?
+ .with_async_function("removeFile", fs_remove_file)?
+ .with_async_function("removeDir", fs_remove_dir)?
+ .with_async_function("isFile", fs_is_file)?
+ .with_async_function("isDir", fs_is_dir)?
+ .build()?,
+ )?;
+ Ok(lua)
}
async fn fs_read_file(_: &Lua, path: String) -> Result {
diff --git a/src/lib/globals/mod.rs b/src/lib/globals/mod.rs
index 4ecc1c5..28ba9f0 100644
--- a/src/lib/globals/mod.rs
+++ b/src/lib/globals/mod.rs
@@ -4,8 +4,8 @@ mod net;
mod process;
mod task;
-pub use console::new as new_console;
-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 console::create as create_console;
+pub use fs::create as create_fs;
+pub use net::create as create_net;
+pub use process::create as create_process;
+pub use task::create as create_task;
diff --git a/src/lib/globals/net.rs b/src/lib/globals/net.rs
index 2fea6e3..a1c5724 100644
--- a/src/lib/globals/net.rs
+++ b/src/lib/globals/net.rs
@@ -1,6 +1,6 @@
use std::{collections::HashMap, str::FromStr};
-use mlua::{Error, Lua, LuaSerdeExt, Result, Table, Value};
+use mlua::{Error, Lua, LuaSerdeExt, Result, Value};
use reqwest::{
header::{HeaderMap, HeaderName, HeaderValue},
Method,
@@ -8,12 +8,16 @@ use reqwest::{
use crate::utils::{net::get_request_user_agent_header, table_builder::ReadonlyTableBuilder};
-pub async fn new(lua: &Lua) -> Result {
- ReadonlyTableBuilder::new(lua)?
- .with_function("jsonEncode", net_json_encode)?
- .with_function("jsonDecode", net_json_decode)?
- .with_async_function("request", net_request)?
- .build()
+pub async fn create(lua: Lua) -> Result {
+ lua.globals().raw_set(
+ "net",
+ ReadonlyTableBuilder::new(&lua)?
+ .with_function("jsonEncode", net_json_encode)?
+ .with_function("jsonDecode", net_json_decode)?
+ .with_async_function("request", net_request)?
+ .build()?,
+ )?;
+ Ok(lua)
}
fn net_json_encode(_: &Lua, (val, pretty): (Value, Option)) -> Result {
diff --git a/src/lib/globals/process.rs b/src/lib/globals/process.rs
index 314c190..b5e472a 100644
--- a/src/lib/globals/process.rs
+++ b/src/lib/globals/process.rs
@@ -9,7 +9,7 @@ use tokio::process::Command;
use crate::utils::table_builder::ReadonlyTableBuilder;
-pub async fn new(lua: &Lua, args_vec: Vec) -> Result {
+pub async fn create(lua: Lua, args_vec: Vec) -> Result {
// Create readonly args array
let inner_args = lua.create_table()?;
for arg in &args_vec {
@@ -36,12 +36,16 @@ pub async fn new(lua: &Lua, args_vec: Vec) -> Result {
inner_env.set_metatable(Some(inner_env_meta));
inner_env.set_readonly(true);
// Create the full process table
- ReadonlyTableBuilder::new(lua)?
- .with_table("args", inner_args)?
- .with_table("env", inner_env)?
- .with_function("exit", process_exit)?
- .with_async_function("spawn", process_spawn)?
- .build()
+ lua.globals().raw_set(
+ "process",
+ ReadonlyTableBuilder::new(&lua)?
+ .with_table("args", inner_args)?
+ .with_table("env", inner_env)?
+ .with_function("exit", process_exit)?
+ .with_async_function("spawn", process_spawn)?
+ .build()?,
+ )?;
+ Ok(lua)
}
fn process_env_get<'lua>(lua: &'lua Lua, (_, key): (Value<'lua>, String)) -> Result> {
diff --git a/src/lib/globals/task.rs b/src/lib/globals/task.rs
index 52ebf45..1a74252 100644
--- a/src/lib/globals/task.rs
+++ b/src/lib/globals/task.rs
@@ -1,40 +1,26 @@
use std::time::Duration;
-use mlua::{Function, Lua, Result, Table, Value};
+use mlua::{Lua, Result};
use tokio::time;
use crate::utils::table_builder::ReadonlyTableBuilder;
const DEFAULT_SLEEP_DURATION: f32 = 1.0 / 60.0;
-const TASK_LIB_LUAU: &str = include_str!("../luau/task.luau");
-
-pub async fn new(lua: &Lua) -> Result {
- let task_lib: Table = lua
- .load(TASK_LIB_LUAU)
- .set_name("task")?
- .eval_async()
- .await?;
- // FUTURE: Properly implementing the task library in async rust is
- // very complicated but should be done at some point, for now we will
- // fall back to implementing only task.wait and doing the rest in lua
- let task_cancel: Function = task_lib.raw_get("cancel")?;
- let task_defer: Function = task_lib.raw_get("defer")?;
- let task_delay: Function = task_lib.raw_get("delay")?;
- let task_spawn: Function = task_lib.raw_get("spawn")?;
- ReadonlyTableBuilder::new(lua)?
- .with_value("cancel", Value::Function(task_cancel))?
- .with_value("defer", Value::Function(task_defer))?
- .with_value("delay", Value::Function(task_delay))?
- .with_value("spawn", Value::Function(task_spawn))?
- .with_async_function("wait", wait)?
- .build()
+pub async fn create(lua: Lua) -> Result {
+ lua.globals().raw_set(
+ "task",
+ ReadonlyTableBuilder::new(&lua)?
+ .with_async_function("wait", task_wait)?
+ .build()?,
+ )?;
+ Ok(lua)
}
// FIXME: It does seem possible to properly make an async wait
// function with mlua right now, something breaks when using
// async wait functions inside of coroutines
-async fn wait(_: &Lua, duration: Option) -> Result {
+async fn task_wait(_: &Lua, duration: Option) -> Result {
let secs = duration.unwrap_or(DEFAULT_SLEEP_DURATION);
time::sleep(Duration::from_secs_f32(secs)).await;
Ok(secs)
diff --git a/src/lib/lib.rs b/src/lib/lib.rs
index 4318a40..f45f2de 100644
--- a/src/lib/lib.rs
+++ b/src/lib/lib.rs
@@ -1,3 +1,5 @@
+use std::collections::HashSet;
+
use anyhow::{bail, Result};
use mlua::Lua;
@@ -5,38 +7,85 @@ pub mod globals;
pub mod utils;
use crate::{
- globals::{new_console, new_fs, new_net, new_process, new_task},
+ globals::{create_console, create_fs, create_net, create_process, create_task},
utils::formatting::{format_label, pretty_format_luau_error},
};
-pub async fn run_lune(name: &str, chunk: &str, args: Vec) -> Result<()> {
- let lua = Lua::new();
- lua.sandbox(true)?;
- // Add in all globals
- {
- let globals = lua.globals();
- globals.raw_set("console", new_console(&lua).await?)?;
- globals.raw_set("fs", new_fs(&lua).await?)?;
- globals.raw_set("net", new_net(&lua).await?)?;
- globals.raw_set("process", new_process(&lua, args.clone()).await?)?;
- globals.raw_set("task", new_task(&lua).await?)?;
- globals.set_readonly(true);
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub enum LuneGlobal {
+ Console,
+ Fs,
+ Net,
+ Process,
+ Task,
+}
+
+impl LuneGlobal {
+ pub fn get_all() -> Vec {
+ vec![
+ Self::Console,
+ Self::Fs,
+ Self::Net,
+ Self::Process,
+ Self::Task,
+ ]
}
- // 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)
- ),
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct Lune {
+ globals: HashSet,
+ args: Vec,
+}
+
+impl Lune {
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ pub fn with_args(mut self, args: Vec) -> Self {
+ self.args = args;
+ self
+ }
+
+ pub fn with_global(mut self, global: LuneGlobal) -> Self {
+ self.globals.insert(global);
+ self
+ }
+
+ pub fn with_all_globals(mut self) -> Self {
+ for global in LuneGlobal::get_all() {
+ self.globals.insert(global);
+ }
+ self
+ }
+
+ pub async fn run(&self, name: &str, chunk: &str) -> Result<()> {
+ let mut lua = Lua::new();
+ for global in &self.globals {
+ lua = match &global {
+ LuneGlobal::Console => create_console(lua).await?,
+ LuneGlobal::Fs => create_fs(lua).await?,
+ LuneGlobal::Net => create_net(lua).await?,
+ LuneGlobal::Process => create_process(lua, self.args.clone()).await?,
+ LuneGlobal::Task => create_task(lua).await?,
+ }
+ }
+ 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;
+ use crate::Lune;
macro_rules! run_tests {
($($name:ident: $value:expr,)*) => {
@@ -53,7 +102,10 @@ mod tests {
let script = tokio::fs::read_to_string(&path)
.await
.unwrap();
- if let Err(e) = run_lune($value, &script, args).await {
+ let lune = Lune::new()
+ .with_args(args)
+ .with_all_globals();
+ if let Err(e) = lune.run($value, &script).await {
panic!("\nTest '{}' failed!\n{}\n", $value, e.to_string())
}
}