Use table builder wherever possible instead of manual table creation

This commit is contained in:
Filip Tibell 2023-01-22 20:18:09 -05:00
parent 4ed69994a2
commit e5e96dfd54
No known key found for this signature in database
6 changed files with 118 additions and 80 deletions

View file

@ -2,7 +2,7 @@ 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::TableBuilder,
}; };
pub async fn create(lua: &Lua) -> Result<()> { pub async fn create(lua: &Lua) -> Result<()> {
@ -18,7 +18,7 @@ pub async fn create(lua: &Lua) -> Result<()> {
}; };
lua.globals().raw_set( lua.globals().raw_set(
"console", "console",
ReadonlyTableBuilder::new(lua)? TableBuilder::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"))?
@ -39,6 +39,6 @@ pub async fn create(lua: &Lua) -> Result<()> {
print_label("error")?; print_label("error")?;
print(&args, true) print(&args, true)
})? })?
.build()?, .build_readonly()?,
) )
} }

View file

@ -3,12 +3,12 @@ use std::path::{PathBuf, MAIN_SEPARATOR};
use mlua::{Lua, Result}; use mlua::{Lua, Result};
use tokio::fs; use tokio::fs;
use crate::utils::table_builder::ReadonlyTableBuilder; use crate::utils::table_builder::TableBuilder;
pub async fn create(lua: &Lua) -> Result<()> { pub async fn create(lua: &Lua) -> Result<()> {
lua.globals().raw_set( lua.globals().raw_set(
"fs", "fs",
ReadonlyTableBuilder::new(lua)? TableBuilder::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)?
@ -17,7 +17,7 @@ pub async fn create(lua: &Lua) -> Result<()> {
.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_readonly()?,
) )
} }

View file

@ -1,21 +1,21 @@
use std::{collections::HashMap, str::FromStr}; use std::{collections::HashMap, str::FromStr};
use mlua::{Error, Lua, LuaSerdeExt, Result, Value}; use mlua::{Error, Lua, LuaSerdeExt, Result, Table, Value};
use reqwest::{ use reqwest::{
header::{HeaderMap, HeaderName, HeaderValue}, header::{HeaderMap, HeaderName, HeaderValue},
Method, 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<()> { pub async fn create(lua: &Lua) -> Result<()> {
lua.globals().raw_set( lua.globals().raw_set(
"net", "net",
ReadonlyTableBuilder::new(lua)? TableBuilder::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_readonly()?,
) )
} }
@ -32,7 +32,7 @@ fn net_json_decode(lua: &Lua, json: String) -> Result<Value> {
lua.to_value(&json) lua.to_value(&json)
} }
async fn net_request<'lua>(lua: &'lua Lua, config: Value<'lua>) -> Result<Value<'lua>> { async fn net_request<'lua>(lua: &'lua Lua, config: Value<'lua>) -> Result<Table<'lua>> {
// Extract stuff from config and make sure its all valid // Extract stuff from config and make sure its all valid
let (url, method, headers, body) = match config { let (url, method, headers, body) = match config {
Value::String(s) => { Value::String(s) => {
@ -44,7 +44,11 @@ async fn net_request<'lua>(lua: &'lua Lua, config: Value<'lua>) -> Result<Value<
// Extract url // Extract url
let url = match tab.raw_get::<&str, mlua::String>("url") { let url = match tab.raw_get::<&str, mlua::String>("url") {
Ok(config_url) => config_url.to_string_lossy().to_string(), 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 // Extract method
let method = match tab.raw_get::<&str, mlua::String>("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<Value<
}; };
(url, method, headers, body) (url, method, headers, body)
} }
_ => 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 // Convert method string into proper enum
let method = match Method::from_str(&method) { let method = match Method::from_str(&method) {
Ok(meth) => meth, Ok(meth) => meth,
Err(_) => { Err(_) => {
return Err(Error::RuntimeError(format!( return Err(Error::RuntimeError(format!(
"Invalid config method '{}'", "Invalid request config method '{}'",
&method &method
))) )))
} }
@ -111,22 +120,23 @@ async fn net_request<'lua>(lua: &'lua Lua, config: Value<'lua>) -> Result<Value<
let res_headers = response.headers().clone(); let res_headers = response.headers().clone();
let res_bytes = response.bytes().await.map_err(Error::external)?; let res_bytes = response.bytes().await.map_err(Error::external)?;
// Construct and return a readonly lua table with results // Construct and return a readonly lua table with results
let tab = lua.create_table()?; TableBuilder::new(lua)?
tab.raw_set("ok", res_status.is_success())?; .with_value("ok", res_status.is_success())?
tab.raw_set("statusCode", res_status.as_u16())?; .with_value("statusCode", res_status.as_u16())?
tab.raw_set( .with_value(
"statusMessage", "statusMessage",
res_status.canonical_reason().unwrap_or("?"), res_status.canonical_reason().unwrap_or("?"),
)?; )?
tab.raw_set( .with_value(
"headers", "headers",
res_headers res_headers
.iter() .iter()
.filter(|(_, value)| value.to_str().is_ok()) .filter_map(|(key, value)| match value.to_str() {
.map(|(key, value)| (key.as_str(), value.to_str().unwrap())) Ok(value) => Some((key.as_str(), value)),
Err(_) => None,
})
.collect::<HashMap<_, _>>(), .collect::<HashMap<_, _>>(),
)?; )?
tab.raw_set("body", lua.create_string(&res_bytes)?)?; .with_value("body", lua.create_string(&res_bytes)?)?
tab.set_readonly(true); .build_readonly()
Ok(Value::Table(tab))
} }

View file

@ -7,43 +7,32 @@ use mlua::{Error, Function, Lua, MetaMethod, Result, Table, Value};
use os_str_bytes::RawOsString; use os_str_bytes::RawOsString;
use tokio::process::Command; 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<String>) -> Result<()> { pub async fn create(lua: &Lua, args_vec: Vec<String>) -> Result<()> {
// Create readonly args array // Create readonly args array
let inner_args = lua.create_table()?; let args_tab = TableBuilder::new(lua)?
for arg in &args_vec { .with_sequential_values(args_vec)?
inner_args.push(arg.clone())?; .build_readonly()?;
} // Create proxied table for env that gets & sets real env vars
inner_args.set_readonly(true); let env_tab = TableBuilder::new(lua)?
// Create proxied env metatable that gets & sets real env vars .with_metatable(
let inner_env_meta = lua.create_table()?; TableBuilder::new(lua)?
inner_env_meta.raw_set( .with_function(MetaMethod::Index.name(), process_env_get)?
MetaMethod::Index.name(), .with_function(MetaMethod::NewIndex.name(), process_env_set)?
lua.create_function(process_env_get)?, .with_function(MetaMethod::Iter.name(), process_env_iter)?
)?; .build_readonly()?,
inner_env_meta.raw_set( )?
MetaMethod::NewIndex.name(), .build_readonly()?;
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);
// Create the full process table // Create the full process table
lua.globals().raw_set( lua.globals().raw_set(
"process", "process",
ReadonlyTableBuilder::new(lua)? TableBuilder::new(lua)?
.with_table("args", inner_args)? .with_value("args", args_tab)?
.with_table("env", inner_env)? .with_value("env", env_tab)?
.with_function("exit", process_exit)? .with_function("exit", process_exit)?
.with_async_function("spawn", process_spawn)? .with_async_function("spawn", process_spawn)?
.build()?, .build_readonly()?,
) )
} }
@ -138,11 +127,10 @@ async fn process_spawn(lua: &Lua, (program, args): (String, Option<Vec<String>>)
false => 1, false => 1,
}); });
// Construct and return a readonly lua table with results // Construct and return a readonly lua table with results
let table = lua.create_table()?; TableBuilder::new(lua)?
table.raw_set("ok", code == 0)?; .with_value("ok", code == 0)?
table.raw_set("code", code)?; .with_value("code", code)?
table.raw_set("stdout", lua.create_string(&output.stdout)?)?; .with_value("stdout", lua.create_string(&output.stdout)?)?
table.raw_set("stderr", lua.create_string(&output.stderr)?)?; .with_value("stderr", lua.create_string(&output.stderr)?)?
table.set_readonly(true); .build_readonly()
Ok(table)
} }

View file

@ -3,20 +3,20 @@ use std::time::Duration;
use mlua::{Error, Function, Lua, Result, Table, Thread, Value, Variadic}; use mlua::{Error, Function, Lua, Result, Table, Thread, Value, Variadic};
use tokio::time::{self, Instant}; use tokio::time::{self, Instant};
use crate::utils::table_builder::ReadonlyTableBuilder; use crate::utils::table_builder::TableBuilder;
type Vararg<'lua> = Variadic<Value<'lua>>; type Vararg<'lua> = Variadic<Value<'lua>>;
pub async fn create(lua: &Lua) -> Result<()> { pub async fn create(lua: &Lua) -> Result<()> {
lua.globals().raw_set( lua.globals().raw_set(
"task", "task",
ReadonlyTableBuilder::new(lua)? TableBuilder::new(lua)?
.with_async_function("cancel", task_cancel)? .with_async_function("cancel", task_cancel)?
.with_async_function("defer", task_defer)? .with_async_function("defer", task_defer)?
.with_async_function("delay", task_delay)? .with_async_function("delay", task_delay)?
.with_async_function("spawn", task_spawn)? .with_async_function("spawn", task_spawn)?
.with_async_function("wait", task_wait)? .with_async_function("wait", task_wait)?
.build()?, .build_readonly()?,
) )
} }

View file

@ -1,29 +1,64 @@
use std::future::Future; 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, lua: &'lua Lua,
tab: Table<'lua>, tab: Table<'lua>,
} }
impl<'lua> ReadonlyTableBuilder<'lua> { impl<'lua> TableBuilder<'lua> {
pub fn new(lua: &'lua Lua) -> Result<Self> { pub fn new(lua: &'lua Lua) -> Result<Self> {
let tab = lua.create_table()?; let tab = lua.create_table()?;
Ok(Self { lua, tab }) Ok(Self { lua, tab })
} }
pub fn with_value(self, key: &'static str, value: Value) -> Result<Self> { pub fn with_value<K, V>(self, key: K, value: V) -> Result<Self>
where
K: ToLua<'lua>,
V: ToLua<'lua>,
{
self.tab.raw_set(key, value)?; self.tab.raw_set(key, value)?;
Ok(self) Ok(self)
} }
pub fn with_table(self, key: &'static str, table: Table) -> Result<Self> { pub fn with_values<K, V>(self, values: Vec<(K, V)>) -> Result<Self>
self.with_value(key, Value::Table(table)) where
K: ToLua<'lua>,
V: ToLua<'lua>,
{
for (key, value) in values {
self.tab.raw_set(key, value)?;
}
Ok(self)
} }
pub fn with_function<A, R, F>(self, key: &'static str, func: F) -> Result<Self> pub fn with_sequential_value<V>(self, value: V) -> Result<Self>
where where
V: ToLua<'lua>,
{
self.tab.raw_push(value)?;
Ok(self)
}
pub fn with_sequential_values<V>(self, values: Vec<V>) -> Result<Self>
where
V: ToLua<'lua>,
{
for value in values {
self.tab.raw_push(value)?;
}
Ok(self)
}
pub fn with_metatable(self, table: Table) -> Result<Self> {
self.tab.set_metatable(Some(table));
Ok(self)
}
pub fn with_function<K, A, R, F>(self, key: K, func: F) -> Result<Self>
where
K: ToLua<'lua>,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
F: 'static + Fn(&'lua Lua, A) -> Result<R>, F: 'static + Fn(&'lua Lua, A) -> Result<R>,
@ -32,8 +67,9 @@ impl<'lua> ReadonlyTableBuilder<'lua> {
self.with_value(key, Value::Function(f)) self.with_value(key, Value::Function(f))
} }
pub fn with_async_function<A, R, F, FR>(self, key: &'static str, func: F) -> Result<Self> pub fn with_async_function<K, A, R, F, FR>(self, key: K, func: F) -> Result<Self>
where where
K: ToLua<'lua>,
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
F: 'static + Fn(&'lua Lua, A) -> FR, F: 'static + Fn(&'lua Lua, A) -> FR,
@ -43,8 +79,12 @@ impl<'lua> ReadonlyTableBuilder<'lua> {
self.with_value(key, Value::Function(f)) self.with_value(key, Value::Function(f))
} }
pub fn build(self) -> Result<Table<'lua>> { pub fn build_readonly(self) -> Result<Table<'lua>> {
self.tab.set_readonly(true); self.tab.set_readonly(true);
Ok(self.tab) Ok(self.tab)
} }
pub fn build(self) -> Result<Table<'lua>> {
Ok(self.tab)
}
} }