diff --git a/src/lib/globals/console.rs b/src/lib/globals/console.rs index 783ad7e..279ab4b 100644 --- a/src/lib/globals/console.rs +++ b/src/lib/globals/console.rs @@ -2,7 +2,7 @@ use mlua::{Lua, MultiValue, Result}; use crate::utils::{ formatting::{flush_stdout, pretty_format_multi_value, print_color, print_label, print_style}, - table_builder::ReadonlyTableBuilder, + table_builder::TableBuilder, }; pub async fn create(lua: &Lua) -> Result<()> { @@ -18,7 +18,7 @@ pub async fn create(lua: &Lua) -> Result<()> { }; lua.globals().raw_set( "console", - ReadonlyTableBuilder::new(lua)? + TableBuilder::new(lua)? .with_function("resetColor", |_, _: ()| print_color("reset"))? .with_function("setColor", |_, color: String| print_color(color))? .with_function("resetStyle", |_, _: ()| print_style("reset"))? @@ -39,6 +39,6 @@ pub async fn create(lua: &Lua) -> Result<()> { print_label("error")?; print(&args, true) })? - .build()?, + .build_readonly()?, ) } diff --git a/src/lib/globals/fs.rs b/src/lib/globals/fs.rs index 4c3f3a5..00bc8bc 100644 --- a/src/lib/globals/fs.rs +++ b/src/lib/globals/fs.rs @@ -3,12 +3,12 @@ use std::path::{PathBuf, MAIN_SEPARATOR}; use mlua::{Lua, Result}; use tokio::fs; -use crate::utils::table_builder::ReadonlyTableBuilder; +use crate::utils::table_builder::TableBuilder; pub async fn create(lua: &Lua) -> Result<()> { lua.globals().raw_set( "fs", - ReadonlyTableBuilder::new(lua)? + TableBuilder::new(lua)? .with_async_function("readFile", fs_read_file)? .with_async_function("readDir", fs_read_dir)? .with_async_function("writeFile", fs_write_file)? @@ -17,7 +17,7 @@ pub async fn create(lua: &Lua) -> Result<()> { .with_async_function("removeDir", fs_remove_dir)? .with_async_function("isFile", fs_is_file)? .with_async_function("isDir", fs_is_dir)? - .build()?, + .build_readonly()?, ) } diff --git a/src/lib/globals/net.rs b/src/lib/globals/net.rs index 6ee0c3a..d1cb278 100644 --- a/src/lib/globals/net.rs +++ b/src/lib/globals/net.rs @@ -1,21 +1,21 @@ use std::{collections::HashMap, str::FromStr}; -use mlua::{Error, Lua, LuaSerdeExt, Result, Value}; +use mlua::{Error, Lua, LuaSerdeExt, Result, Table, Value}; use reqwest::{ header::{HeaderMap, HeaderName, HeaderValue}, Method, }; -use crate::utils::{net::get_request_user_agent_header, table_builder::ReadonlyTableBuilder}; +use crate::utils::{net::get_request_user_agent_header, table_builder::TableBuilder}; pub async fn create(lua: &Lua) -> Result<()> { lua.globals().raw_set( "net", - ReadonlyTableBuilder::new(lua)? + TableBuilder::new(lua)? .with_function("jsonEncode", net_json_encode)? .with_function("jsonDecode", net_json_decode)? .with_async_function("request", net_request)? - .build()?, + .build_readonly()?, ) } @@ -32,7 +32,7 @@ fn net_json_decode(lua: &Lua, json: String) -> Result { lua.to_value(&json) } -async fn net_request<'lua>(lua: &'lua Lua, config: Value<'lua>) -> Result> { +async fn net_request<'lua>(lua: &'lua Lua, config: Value<'lua>) -> Result> { // Extract stuff from config and make sure its all valid let (url, method, headers, body) = match config { Value::String(s) => { @@ -44,7 +44,11 @@ async fn net_request<'lua>(lua: &'lua Lua, config: Value<'lua>) -> Result("url") { Ok(config_url) => config_url.to_string_lossy().to_string(), - Err(_) => return Err(Error::RuntimeError("Missing 'url' in config".to_string())), + Err(_) => { + return Err(Error::RuntimeError( + "Missing 'url' in request config".to_string(), + )) + } }; // Extract method let method = match tab.raw_get::<&str, mlua::String>("method") { @@ -73,14 +77,19 @@ async fn net_request<'lua>(lua: &'lua Lua, config: Value<'lua>) -> Result return Err(Error::RuntimeError("Invalid config value".to_string())), + value => { + return Err(Error::RuntimeError(format!( + "Invalid request config - expected string or table, got {}", + value.type_name() + ))) + } }; // Convert method string into proper enum let method = match Method::from_str(&method) { Ok(meth) => meth, Err(_) => { return Err(Error::RuntimeError(format!( - "Invalid config method '{}'", + "Invalid request config method '{}'", &method ))) } @@ -111,22 +120,23 @@ async fn net_request<'lua>(lua: &'lua Lua, config: Value<'lua>) -> Result>(), - )?; - tab.raw_set("body", lua.create_string(&res_bytes)?)?; - tab.set_readonly(true); - Ok(Value::Table(tab)) + TableBuilder::new(lua)? + .with_value("ok", res_status.is_success())? + .with_value("statusCode", res_status.as_u16())? + .with_value( + "statusMessage", + res_status.canonical_reason().unwrap_or("?"), + )? + .with_value( + "headers", + res_headers + .iter() + .filter_map(|(key, value)| match value.to_str() { + Ok(value) => Some((key.as_str(), value)), + Err(_) => None, + }) + .collect::>(), + )? + .with_value("body", lua.create_string(&res_bytes)?)? + .build_readonly() } diff --git a/src/lib/globals/process.rs b/src/lib/globals/process.rs index ae18a5b..0ce23d9 100644 --- a/src/lib/globals/process.rs +++ b/src/lib/globals/process.rs @@ -7,43 +7,32 @@ use mlua::{Error, Function, Lua, MetaMethod, Result, Table, Value}; use os_str_bytes::RawOsString; use tokio::process::Command; -use crate::utils::table_builder::ReadonlyTableBuilder; +use crate::utils::table_builder::TableBuilder; 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 { - inner_args.push(arg.clone())?; - } - inner_args.set_readonly(true); - // Create proxied env metatable that gets & sets real env vars - let inner_env_meta = lua.create_table()?; - inner_env_meta.raw_set( - MetaMethod::Index.name(), - lua.create_function(process_env_get)?, - )?; - inner_env_meta.raw_set( - MetaMethod::NewIndex.name(), - lua.create_function(process_env_set)?, - )?; - inner_env_meta.raw_set( - MetaMethod::Iter.name(), - lua.create_function(process_env_iter)?, - )?; - inner_env_meta.set_readonly(true); - // Create blank table for env with the metatable - let inner_env = lua.create_table()?; - inner_env.set_metatable(Some(inner_env_meta)); - inner_env.set_readonly(true); + let args_tab = TableBuilder::new(lua)? + .with_sequential_values(args_vec)? + .build_readonly()?; + // Create proxied table for env that gets & sets real env vars + let env_tab = TableBuilder::new(lua)? + .with_metatable( + TableBuilder::new(lua)? + .with_function(MetaMethod::Index.name(), process_env_get)? + .with_function(MetaMethod::NewIndex.name(), process_env_set)? + .with_function(MetaMethod::Iter.name(), process_env_iter)? + .build_readonly()?, + )? + .build_readonly()?; // Create the full process table lua.globals().raw_set( "process", - ReadonlyTableBuilder::new(lua)? - .with_table("args", inner_args)? - .with_table("env", inner_env)? + TableBuilder::new(lua)? + .with_value("args", args_tab)? + .with_value("env", env_tab)? .with_function("exit", process_exit)? .with_async_function("spawn", process_spawn)? - .build()?, + .build_readonly()?, ) } @@ -138,11 +127,10 @@ async fn process_spawn(lua: &Lua, (program, args): (String, Option>) false => 1, }); // Construct and return a readonly lua table with results - let table = lua.create_table()?; - table.raw_set("ok", code == 0)?; - table.raw_set("code", code)?; - table.raw_set("stdout", lua.create_string(&output.stdout)?)?; - table.raw_set("stderr", lua.create_string(&output.stderr)?)?; - table.set_readonly(true); - Ok(table) + TableBuilder::new(lua)? + .with_value("ok", code == 0)? + .with_value("code", code)? + .with_value("stdout", lua.create_string(&output.stdout)?)? + .with_value("stderr", lua.create_string(&output.stderr)?)? + .build_readonly() } diff --git a/src/lib/globals/task.rs b/src/lib/globals/task.rs index 567182b..680e056 100644 --- a/src/lib/globals/task.rs +++ b/src/lib/globals/task.rs @@ -3,20 +3,20 @@ use std::time::Duration; use mlua::{Error, Function, Lua, Result, Table, Thread, Value, Variadic}; use tokio::time::{self, Instant}; -use crate::utils::table_builder::ReadonlyTableBuilder; +use crate::utils::table_builder::TableBuilder; type Vararg<'lua> = Variadic>; pub async fn create(lua: &Lua) -> Result<()> { lua.globals().raw_set( "task", - ReadonlyTableBuilder::new(lua)? + TableBuilder::new(lua)? .with_async_function("cancel", task_cancel)? .with_async_function("defer", task_defer)? .with_async_function("delay", task_delay)? .with_async_function("spawn", task_spawn)? .with_async_function("wait", task_wait)? - .build()?, + .build_readonly()?, ) } diff --git a/src/lib/utils/table_builder.rs b/src/lib/utils/table_builder.rs index 9bbfb8d..cdeaf34 100644 --- a/src/lib/utils/table_builder.rs +++ b/src/lib/utils/table_builder.rs @@ -1,29 +1,64 @@ use std::future::Future; -use mlua::{FromLuaMulti, Lua, Result, Table, ToLuaMulti, Value}; +use mlua::{FromLuaMulti, Lua, Result, Table, ToLua, ToLuaMulti, Value}; -pub struct ReadonlyTableBuilder<'lua> { +pub struct TableBuilder<'lua> { lua: &'lua Lua, tab: Table<'lua>, } -impl<'lua> ReadonlyTableBuilder<'lua> { +impl<'lua> TableBuilder<'lua> { pub fn new(lua: &'lua Lua) -> Result { let tab = lua.create_table()?; Ok(Self { lua, tab }) } - pub fn with_value(self, key: &'static str, value: Value) -> Result { + pub fn with_value(self, key: K, value: V) -> Result + where + K: ToLua<'lua>, + V: ToLua<'lua>, + { self.tab.raw_set(key, value)?; Ok(self) } - pub fn with_table(self, key: &'static str, table: Table) -> Result { - self.with_value(key, Value::Table(table)) + pub fn with_values(self, values: Vec<(K, V)>) -> Result + where + K: ToLua<'lua>, + V: ToLua<'lua>, + { + for (key, value) in values { + self.tab.raw_set(key, value)?; + } + Ok(self) } - pub fn with_function(self, key: &'static str, func: F) -> Result + pub fn with_sequential_value(self, value: V) -> Result where + V: ToLua<'lua>, + { + self.tab.raw_push(value)?; + Ok(self) + } + + pub fn with_sequential_values(self, values: Vec) -> Result + where + V: ToLua<'lua>, + { + for value in values { + self.tab.raw_push(value)?; + } + Ok(self) + } + + pub fn with_metatable(self, table: Table) -> Result { + self.tab.set_metatable(Some(table)); + Ok(self) + } + + pub fn with_function(self, key: K, func: F) -> Result + where + K: ToLua<'lua>, A: FromLuaMulti<'lua>, R: ToLuaMulti<'lua>, F: 'static + Fn(&'lua Lua, A) -> Result, @@ -32,8 +67,9 @@ impl<'lua> ReadonlyTableBuilder<'lua> { self.with_value(key, Value::Function(f)) } - pub fn with_async_function(self, key: &'static str, func: F) -> Result + pub fn with_async_function(self, key: K, func: F) -> Result where + K: ToLua<'lua>, A: FromLuaMulti<'lua>, R: ToLuaMulti<'lua>, F: 'static + Fn(&'lua Lua, A) -> FR, @@ -43,8 +79,12 @@ impl<'lua> ReadonlyTableBuilder<'lua> { self.with_value(key, Value::Function(f)) } - pub fn build(self) -> Result> { + pub fn build_readonly(self) -> Result> { self.tab.set_readonly(true); Ok(self.tab) } + + pub fn build(self) -> Result> { + Ok(self.tab) + } }