use std::collections::HashMap;
use mlua::{Function, Lua, Table, Value};
use crate::error::{Error, Result};
pub struct PermsRegistry {
lua: Lua,
keys: HashMap<String, ()>,
}
const PERMS_REGISTRY_KEY: &str = "__mlua_eris_perms";
const UPERMS_REGISTRY_KEY: &str = "__mlua_eris_uperms";
impl PermsRegistry {
pub fn empty(lua: &Lua) -> Result<Self> {
let registry = Self {
lua: lua.clone(),
keys: HashMap::new(),
};
registry.write_perms_table(lua.create_table()?)?;
registry.write_uperms_table(lua.create_table()?)?;
Ok(registry)
}
pub fn with_default_stdlib(lua: &Lua) -> Result<Self> {
let mut reg = Self::empty(lua)?;
reg.register_default_stdlib()?;
Ok(reg)
}
fn register_default_stdlib(&mut self) -> Result<()> {
const BASE_FUNCS: &[&str] = &[
"assert", "collectgarbage", "dofile", "error", "getmetatable",
"ipairs", "load", "loadfile", "next", "pairs", "pcall", "print",
"rawequal", "rawget", "rawlen", "rawset", "require", "select",
"setmetatable", "tonumber", "tostring", "type", "xpcall",
];
const LIBS: &[&str] = &[
"coroutine", "debug", "io", "math", "os", "package",
"string", "table", "utf8", "eris",
];
let globals = self.lua.globals();
for name in BASE_FUNCS {
let value: Value = globals.get(*name)?;
if !matches!(value, Value::Nil) {
self.register_value(name, value)?;
}
}
for lib_name in LIBS {
let lib: Value = globals.get(*lib_name)?;
match lib {
Value::Table(ref tbl) => {
self.register_value(lib_name, lib.clone())?;
let mut keys: Vec<String> = Vec::new();
for pair in tbl.clone().pairs::<Value, Value>() {
let (k, _v) = pair?;
if let Value::String(s) = k {
if let Ok(s_str) = s.to_str() {
keys.push(s_str.to_string());
}
}
}
keys.sort();
for k in keys {
let v: Value = tbl.get(k.as_str())?;
let perm_key = format!("{}.{}", lib_name, k);
match v {
Value::Function(_) | Value::Table(_) => {
let _ = self.register_value(&perm_key, v);
}
_ => {}
}
}
}
Value::Nil => {} other => {
self.register_value(lib_name, other)?;
}
}
}
let g = Value::Table(self.lua.globals());
self.register_value("_G", g)?;
Ok(())
}
pub fn register_function(&mut self, key: &str, func: Function) -> Result<()> {
self.register_value(key, Value::Function(func))
}
pub fn register_value(&mut self, key: &str, value: Value) -> Result<()> {
if self.keys.contains_key(key) {
return Err(Error::DuplicatePermKey(key.to_string()));
}
let perms = self.read_perms_table()?;
let uperms = self.read_uperms_table()?;
perms.set(value.clone(), key)?;
uperms.set(key, value)?;
self.write_perms_table(perms)?;
self.write_uperms_table(uperms)?;
self.keys.insert(key.to_string(), ());
Ok(())
}
pub fn len(&self) -> usize {
self.keys.len()
}
pub fn is_empty(&self) -> bool {
self.keys.is_empty()
}
pub(crate) fn perms_table(&self) -> Result<Table> {
self.read_perms_table()
}
pub(crate) fn uperms_table(&self) -> Result<Table> {
self.read_uperms_table()
}
fn read_perms_table(&self) -> Result<Table> {
Ok(self.lua.named_registry_value(PERMS_REGISTRY_KEY)?)
}
fn write_perms_table(&self, t: Table) -> Result<()> {
self.lua.set_named_registry_value(PERMS_REGISTRY_KEY, t)?;
Ok(())
}
fn read_uperms_table(&self) -> Result<Table> {
Ok(self.lua.named_registry_value(UPERMS_REGISTRY_KEY)?)
}
fn write_uperms_table(&self, t: Table) -> Result<()> {
self.lua.set_named_registry_value(UPERMS_REGISTRY_KEY, t)?;
Ok(())
}
}