77 lines
2 KiB
Rust
77 lines
2 KiB
Rust
|
use std::fs::DirEntry;
|
||
|
|
||
|
use log::{debug, Record};
|
||
|
use mlua::{Function, FromLua, Lua, Table, Value};
|
||
|
|
||
|
pub struct Plugin<'lua> {
|
||
|
pub table: Table<'lua>,
|
||
|
pub id: String,
|
||
|
}
|
||
|
|
||
|
impl<'lua> FromLua<'lua> for Plugin<'lua> {
|
||
|
fn from_lua(lua_value: Value<'lua>, lua: &'lua Lua) -> mlua::Result<Self> {
|
||
|
let table = Table::from_lua(lua_value, lua)?;
|
||
|
Ok(Plugin {
|
||
|
id: table.get("id")?,
|
||
|
table,
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn lua_locinfo(lua: &Lua) -> Option<(u32, String)> {
|
||
|
let debug = lua.globals().get::<_, Table>("debug").ok()?;
|
||
|
let getinfo = debug.get::<_, Function>("getinfo").ok()?;
|
||
|
let data = getinfo.call::<_, Table>((2,)).ok()?;
|
||
|
let line = data.get("currentline").ok()?;
|
||
|
let file = data.get("short_src").ok()?;
|
||
|
Some((line, file))
|
||
|
}
|
||
|
|
||
|
fn lua_log(level: log::Level, path: &'static str, msg: String, lua: &Lua) {
|
||
|
let mut record = Record::builder();
|
||
|
record.level(level);
|
||
|
record.target(path);
|
||
|
record.module_path_static(Some(path));
|
||
|
let locinfo = lua_locinfo(lua);
|
||
|
if let Some((line, file)) = &locinfo {
|
||
|
record.line(Some(*line));
|
||
|
record.file(Some(file));
|
||
|
}
|
||
|
log::logger().log(&record.args(format_args!("{}", msg)).build());
|
||
|
|
||
|
}
|
||
|
|
||
|
const LEVELS: [(&str, log::Level); 5] = [
|
||
|
("trace", log::Level::Trace),
|
||
|
("debug", log::Level::Debug),
|
||
|
("info", log::Level::Info),
|
||
|
("warn", log::Level::Warn),
|
||
|
("error", log::Level::Error),
|
||
|
];
|
||
|
|
||
|
impl<'lua> Plugin<'lua> {
|
||
|
pub fn load(entry: &DirEntry, lua: &'lua Lua) -> anyhow::Result<Self> {
|
||
|
let ty = entry.file_type()?;
|
||
|
let mut path = entry.path();
|
||
|
if ty.is_dir() {
|
||
|
path.push("main.lua");
|
||
|
}
|
||
|
let chunk = lua.load(&path);
|
||
|
debug!("evaluating plugin");
|
||
|
let plugin = Self::from_lua(chunk.eval()?, lua)?;
|
||
|
|
||
|
// leak: plugins are only loaded at the beginning of the program
|
||
|
// and the path needs to live forever anyway, so this is fine
|
||
|
let path: &'static str = Box::leak(format!("qcplugin::{}", plugin.id).into_boxed_str());
|
||
|
|
||
|
for (name, level) in LEVELS {
|
||
|
plugin.table.set(name, lua.create_function(move |lua, (msg,): (String,)| {
|
||
|
lua_log(level, path, msg, lua);
|
||
|
Ok(())
|
||
|
})?)?;
|
||
|
}
|
||
|
|
||
|
Ok(plugin)
|
||
|
}
|
||
|
}
|