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::{
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()?,
)
}

View file

@ -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()?,
)
}

View file

@ -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<Value> {
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
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<Value<
// Extract url
let url = match tab.raw_get::<&str, mlua::String>("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<Value<
};
(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
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<Value<
let res_headers = response.headers().clone();
let res_bytes = response.bytes().await.map_err(Error::external)?;
// Construct and return a readonly lua table with results
let tab = lua.create_table()?;
tab.raw_set("ok", res_status.is_success())?;
tab.raw_set("statusCode", res_status.as_u16())?;
tab.raw_set(
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("?"),
)?;
tab.raw_set(
)?
.with_value(
"headers",
res_headers
.iter()
.filter(|(_, value)| value.to_str().is_ok())
.map(|(key, value)| (key.as_str(), value.to_str().unwrap()))
.filter_map(|(key, value)| match value.to_str() {
Ok(value) => Some((key.as_str(), value)),
Err(_) => None,
})
.collect::<HashMap<_, _>>(),
)?;
tab.raw_set("body", lua.create_string(&res_bytes)?)?;
tab.set_readonly(true);
Ok(Value::Table(tab))
)?
.with_value("body", lua.create_string(&res_bytes)?)?
.build_readonly()
}

View file

@ -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<String>) -> 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<Vec<String>>)
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()
}

View file

@ -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<Value<'lua>>;
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()?,
)
}

View file

@ -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<Self> {
let tab = lua.create_table()?;
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)?;
Ok(self)
}
pub fn with_table(self, key: &'static str, table: Table) -> Result<Self> {
self.with_value(key, Value::Table(table))
pub fn with_values<K, V>(self, values: Vec<(K, V)>) -> Result<Self>
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
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>,
R: ToLuaMulti<'lua>,
F: 'static + Fn(&'lua Lua, A) -> Result<R>,
@ -32,8 +67,9 @@ impl<'lua> ReadonlyTableBuilder<'lua> {
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
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<Table<'lua>> {
pub fn build_readonly(self) -> Result<Table<'lua>> {
self.tab.set_readonly(true);
Ok(self.tab)
}
pub fn build(self) -> Result<Table<'lua>> {
Ok(self.tab)
}
}