mirror of
https://github.com/lune-org/lune.git
synced 2024-12-12 13:00:37 +00:00
Pass ownership to global constructors to avoid lifetime issues
This commit is contained in:
parent
6d432171e5
commit
6b14bc3dc0
8 changed files with 160 additions and 105 deletions
|
@ -3,7 +3,7 @@ use std::fs::read_to_string;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::{CommandFactory, Parser};
|
use clap::{CommandFactory, Parser};
|
||||||
|
|
||||||
use lune::run_lune;
|
use lune::Lune;
|
||||||
|
|
||||||
use crate::utils::{files::find_parse_file_path, github::Client as GithubClient};
|
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
|
// Display the file path relative to cwd with no extensions in stack traces
|
||||||
let file_display_name = file_path.with_extension("").display().to_string();
|
let file_display_name = file_path.with_extension("").display().to_string();
|
||||||
// Create a new lune object with all globals & run the script
|
// 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}");
|
eprintln!("{e}");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use mlua::{Lua, MultiValue, Result, Table};
|
use mlua::{Lua, MultiValue, Result};
|
||||||
|
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
formatting::{flush_stdout, pretty_format_multi_value, print_color, print_label, print_style},
|
formatting::{flush_stdout, pretty_format_multi_value, print_color, print_label, print_style},
|
||||||
table_builder::ReadonlyTableBuilder,
|
table_builder::ReadonlyTableBuilder,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub async fn new(lua: &Lua) -> Result<Table> {
|
pub async fn create(lua: Lua) -> Result<Lua> {
|
||||||
let print = |args: &MultiValue, throw: bool| -> Result<()> {
|
let print = |args: &MultiValue, throw: bool| -> Result<()> {
|
||||||
let s = pretty_format_multi_value(args)?;
|
let s = pretty_format_multi_value(args)?;
|
||||||
if throw {
|
if throw {
|
||||||
|
@ -16,7 +16,9 @@ pub async fn new(lua: &Lua) -> Result<Table> {
|
||||||
flush_stdout()?;
|
flush_stdout()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
ReadonlyTableBuilder::new(lua)?
|
lua.globals().raw_set(
|
||||||
|
"console",
|
||||||
|
ReadonlyTableBuilder::new(&lua)?
|
||||||
.with_function("resetColor", |_, _: ()| print_color("reset"))?
|
.with_function("resetColor", |_, _: ()| print_color("reset"))?
|
||||||
.with_function("setColor", |_, color: String| print_color(color))?
|
.with_function("setColor", |_, color: String| print_color(color))?
|
||||||
.with_function("resetStyle", |_, _: ()| print_style("reset"))?
|
.with_function("resetStyle", |_, _: ()| print_style("reset"))?
|
||||||
|
@ -37,5 +39,7 @@ pub async fn new(lua: &Lua) -> Result<Table> {
|
||||||
print_label("error")?;
|
print_label("error")?;
|
||||||
print(&args, true)
|
print(&args, true)
|
||||||
})?
|
})?
|
||||||
.build()
|
.build()?,
|
||||||
|
)?;
|
||||||
|
Ok(lua)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
use std::path::{PathBuf, MAIN_SEPARATOR};
|
use std::path::{PathBuf, MAIN_SEPARATOR};
|
||||||
|
|
||||||
use mlua::{Lua, Result, Table};
|
use mlua::{Lua, Result};
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
|
|
||||||
use crate::utils::table_builder::ReadonlyTableBuilder;
|
use crate::utils::table_builder::ReadonlyTableBuilder;
|
||||||
|
|
||||||
pub async fn new(lua: &Lua) -> Result<Table> {
|
pub async fn create(lua: Lua) -> Result<Lua> {
|
||||||
ReadonlyTableBuilder::new(lua)?
|
lua.globals().raw_set(
|
||||||
|
"fs",
|
||||||
|
ReadonlyTableBuilder::new(&lua)?
|
||||||
.with_async_function("readFile", fs_read_file)?
|
.with_async_function("readFile", fs_read_file)?
|
||||||
.with_async_function("readDir", fs_read_dir)?
|
.with_async_function("readDir", fs_read_dir)?
|
||||||
.with_async_function("writeFile", fs_write_file)?
|
.with_async_function("writeFile", fs_write_file)?
|
||||||
|
@ -15,7 +17,9 @@ pub async fn new(lua: &Lua) -> Result<Table> {
|
||||||
.with_async_function("removeDir", fs_remove_dir)?
|
.with_async_function("removeDir", fs_remove_dir)?
|
||||||
.with_async_function("isFile", fs_is_file)?
|
.with_async_function("isFile", fs_is_file)?
|
||||||
.with_async_function("isDir", fs_is_dir)?
|
.with_async_function("isDir", fs_is_dir)?
|
||||||
.build()
|
.build()?,
|
||||||
|
)?;
|
||||||
|
Ok(lua)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fs_read_file(_: &Lua, path: String) -> Result<String> {
|
async fn fs_read_file(_: &Lua, path: String) -> Result<String> {
|
||||||
|
|
|
@ -4,8 +4,8 @@ mod net;
|
||||||
mod process;
|
mod process;
|
||||||
mod task;
|
mod task;
|
||||||
|
|
||||||
pub use console::new as new_console;
|
pub use console::create as create_console;
|
||||||
pub use fs::new as new_fs;
|
pub use fs::create as create_fs;
|
||||||
pub use net::new as new_net;
|
pub use net::create as create_net;
|
||||||
pub use process::new as new_process;
|
pub use process::create as create_process;
|
||||||
pub use task::new as new_task;
|
pub use task::create as create_task;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{collections::HashMap, str::FromStr};
|
use std::{collections::HashMap, str::FromStr};
|
||||||
|
|
||||||
use mlua::{Error, Lua, LuaSerdeExt, Result, Table, Value};
|
use mlua::{Error, Lua, LuaSerdeExt, Result, Value};
|
||||||
use reqwest::{
|
use reqwest::{
|
||||||
header::{HeaderMap, HeaderName, HeaderValue},
|
header::{HeaderMap, HeaderName, HeaderValue},
|
||||||
Method,
|
Method,
|
||||||
|
@ -8,12 +8,16 @@ use reqwest::{
|
||||||
|
|
||||||
use crate::utils::{net::get_request_user_agent_header, table_builder::ReadonlyTableBuilder};
|
use crate::utils::{net::get_request_user_agent_header, table_builder::ReadonlyTableBuilder};
|
||||||
|
|
||||||
pub async fn new(lua: &Lua) -> Result<Table> {
|
pub async fn create(lua: Lua) -> Result<Lua> {
|
||||||
ReadonlyTableBuilder::new(lua)?
|
lua.globals().raw_set(
|
||||||
|
"net",
|
||||||
|
ReadonlyTableBuilder::new(&lua)?
|
||||||
.with_function("jsonEncode", net_json_encode)?
|
.with_function("jsonEncode", net_json_encode)?
|
||||||
.with_function("jsonDecode", net_json_decode)?
|
.with_function("jsonDecode", net_json_decode)?
|
||||||
.with_async_function("request", net_request)?
|
.with_async_function("request", net_request)?
|
||||||
.build()
|
.build()?,
|
||||||
|
)?;
|
||||||
|
Ok(lua)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn net_json_encode(_: &Lua, (val, pretty): (Value, Option<bool>)) -> Result<String> {
|
fn net_json_encode(_: &Lua, (val, pretty): (Value, Option<bool>)) -> Result<String> {
|
||||||
|
|
|
@ -9,7 +9,7 @@ use tokio::process::Command;
|
||||||
|
|
||||||
use crate::utils::table_builder::ReadonlyTableBuilder;
|
use crate::utils::table_builder::ReadonlyTableBuilder;
|
||||||
|
|
||||||
pub async fn new(lua: &Lua, args_vec: Vec<String>) -> Result<Table> {
|
pub async fn create(lua: Lua, args_vec: Vec<String>) -> Result<Lua> {
|
||||||
// Create readonly args array
|
// Create readonly args array
|
||||||
let inner_args = lua.create_table()?;
|
let inner_args = lua.create_table()?;
|
||||||
for arg in &args_vec {
|
for arg in &args_vec {
|
||||||
|
@ -36,12 +36,16 @@ pub async fn new(lua: &Lua, args_vec: Vec<String>) -> Result<Table> {
|
||||||
inner_env.set_metatable(Some(inner_env_meta));
|
inner_env.set_metatable(Some(inner_env_meta));
|
||||||
inner_env.set_readonly(true);
|
inner_env.set_readonly(true);
|
||||||
// Create the full process table
|
// Create the full process table
|
||||||
ReadonlyTableBuilder::new(lua)?
|
lua.globals().raw_set(
|
||||||
|
"process",
|
||||||
|
ReadonlyTableBuilder::new(&lua)?
|
||||||
.with_table("args", inner_args)?
|
.with_table("args", inner_args)?
|
||||||
.with_table("env", inner_env)?
|
.with_table("env", inner_env)?
|
||||||
.with_function("exit", process_exit)?
|
.with_function("exit", process_exit)?
|
||||||
.with_async_function("spawn", process_spawn)?
|
.with_async_function("spawn", process_spawn)?
|
||||||
.build()
|
.build()?,
|
||||||
|
)?;
|
||||||
|
Ok(lua)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_env_get<'lua>(lua: &'lua Lua, (_, key): (Value<'lua>, String)) -> Result<Value<'lua>> {
|
fn process_env_get<'lua>(lua: &'lua Lua, (_, key): (Value<'lua>, String)) -> Result<Value<'lua>> {
|
||||||
|
|
|
@ -1,40 +1,26 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use mlua::{Function, Lua, Result, Table, Value};
|
use mlua::{Lua, Result};
|
||||||
use tokio::time;
|
use tokio::time;
|
||||||
|
|
||||||
use crate::utils::table_builder::ReadonlyTableBuilder;
|
use crate::utils::table_builder::ReadonlyTableBuilder;
|
||||||
|
|
||||||
const DEFAULT_SLEEP_DURATION: f32 = 1.0 / 60.0;
|
const DEFAULT_SLEEP_DURATION: f32 = 1.0 / 60.0;
|
||||||
|
|
||||||
const TASK_LIB_LUAU: &str = include_str!("../luau/task.luau");
|
pub async fn create(lua: Lua) -> Result<Lua> {
|
||||||
|
lua.globals().raw_set(
|
||||||
pub async fn new(lua: &Lua) -> Result<Table> {
|
"task",
|
||||||
let task_lib: Table = lua
|
ReadonlyTableBuilder::new(&lua)?
|
||||||
.load(TASK_LIB_LUAU)
|
.with_async_function("wait", task_wait)?
|
||||||
.set_name("task")?
|
.build()?,
|
||||||
.eval_async()
|
)?;
|
||||||
.await?;
|
Ok(lua)
|
||||||
// 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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: It does seem possible to properly make an async wait
|
// FIXME: It does seem possible to properly make an async wait
|
||||||
// function with mlua right now, something breaks when using
|
// function with mlua right now, something breaks when using
|
||||||
// async wait functions inside of coroutines
|
// async wait functions inside of coroutines
|
||||||
async fn wait(_: &Lua, duration: Option<f32>) -> Result<f32> {
|
async fn task_wait(_: &Lua, duration: Option<f32>) -> Result<f32> {
|
||||||
let secs = duration.unwrap_or(DEFAULT_SLEEP_DURATION);
|
let secs = duration.unwrap_or(DEFAULT_SLEEP_DURATION);
|
||||||
time::sleep(Duration::from_secs_f32(secs)).await;
|
time::sleep(Duration::from_secs_f32(secs)).await;
|
||||||
Ok(secs)
|
Ok(secs)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use mlua::Lua;
|
use mlua::Lua;
|
||||||
|
|
||||||
|
@ -5,24 +7,70 @@ pub mod globals;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
use crate::{
|
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},
|
utils::formatting::{format_label, pretty_format_luau_error},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub async fn run_lune(name: &str, chunk: &str, args: Vec<String>) -> Result<()> {
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
let lua = Lua::new();
|
pub enum LuneGlobal {
|
||||||
lua.sandbox(true)?;
|
Console,
|
||||||
// Add in all globals
|
Fs,
|
||||||
{
|
Net,
|
||||||
let globals = lua.globals();
|
Process,
|
||||||
globals.raw_set("console", new_console(&lua).await?)?;
|
Task,
|
||||||
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?)?;
|
impl LuneGlobal {
|
||||||
globals.raw_set("task", new_task(&lua).await?)?;
|
pub fn get_all() -> Vec<Self> {
|
||||||
globals.set_readonly(true);
|
vec![
|
||||||
|
Self::Console,
|
||||||
|
Self::Fs,
|
||||||
|
Self::Net,
|
||||||
|
Self::Process,
|
||||||
|
Self::Task,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct Lune {
|
||||||
|
globals: HashSet<LuneGlobal>,
|
||||||
|
args: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Lune {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_args(mut self, args: Vec<String>) -> 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?,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Run the requested chunk asynchronously
|
|
||||||
let result = lua.load(chunk).set_name(name)?.exec_async().await;
|
let result = lua.load(chunk).set_name(name)?.exec_async().await;
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
|
@ -32,11 +80,12 @@ pub async fn run_lune(name: &str, chunk: &str, args: Vec<String>) -> Result<()>
|
||||||
pretty_format_luau_error(&e)
|
pretty_format_luau_error(&e)
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::run_lune;
|
use crate::Lune;
|
||||||
|
|
||||||
macro_rules! run_tests {
|
macro_rules! run_tests {
|
||||||
($($name:ident: $value:expr,)*) => {
|
($($name:ident: $value:expr,)*) => {
|
||||||
|
@ -53,7 +102,10 @@ mod tests {
|
||||||
let script = tokio::fs::read_to_string(&path)
|
let script = tokio::fs::read_to_string(&path)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.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())
|
panic!("\nTest '{}' failed!\n{}\n", $value, e.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue