From 0fe8e4bcbddaf29637d6d92d66038d81f6ffd94a Mon Sep 17 00:00:00 2001 From: YuKun Liu Date: Wed, 12 Oct 2022 11:47:05 -0700 Subject: [PATCH] fix: plugin init problem --- src/plugin/interface/fs.rs | 14 ++- src/plugin/interface/mod.rs | 46 +++++++++ src/plugin/mod.rs | 179 ++++++++++++++++++++++++------------ src/plugin/types.rs | 66 +++++++------ 4 files changed, 218 insertions(+), 87 deletions(-) diff --git a/src/plugin/interface/fs.rs b/src/plugin/interface/fs.rs index cf90d171c..d72e41375 100644 --- a/src/plugin/interface/fs.rs +++ b/src/plugin/interface/fs.rs @@ -42,9 +42,17 @@ impl UserData for PluginFileSystem { let path = args.0; let content = args.1; let path = PathBuf::from(path); - let mut file = std::fs::File::create(path)?; - file.write_all(content.as_bytes())?; - Ok(()) + + let file = std::fs::File::create(path); + if file.is_err() { + return Ok(false); + } + + if file.unwrap().write_all(content.as_bytes()).is_err() { + return Ok(false) + } + + Ok(true) }); methods.add_function("unzip_file", |_, args: (String, String)| { let file = PathBuf::from(args.0); diff --git a/src/plugin/interface/mod.rs b/src/plugin/interface/mod.rs index 2ef1920fc..5ed2ed827 100644 --- a/src/plugin/interface/mod.rs +++ b/src/plugin/interface/mod.rs @@ -15,6 +15,8 @@ pub struct PluginInfo<'lua> { pub author: String, pub version: String, + pub inner: PluginInner, + pub on_init: Option>, pub build: PluginBuildInfo<'lua>, pub serve: PluginServeInfo<'lua>, @@ -28,6 +30,8 @@ impl<'lua> FromLua<'lua> for PluginInfo<'lua> { author: String::default(), version: String::from("0.1.0"), + inner: Default::default(), + on_init: None, build: Default::default(), serve: Default::default(), @@ -46,6 +50,10 @@ impl<'lua> FromLua<'lua> for PluginInfo<'lua> { res.version = v; } + if let Ok(v) = tab.get::<_, PluginInner>("inner") { + res.inner = v; + } + if let Ok(v) = tab.get::<_, Function>("on_init") { res.on_init = Some(v); } @@ -72,6 +80,8 @@ impl<'lua> ToLua<'lua> for PluginInfo<'lua> { res.set("author", self.author.to_string())?; res.set("version", self.version.to_string())?; + res.set("inner", self.inner)?; + if let Some(e) = self.on_init { res.set("on_init", e)?; } @@ -82,6 +92,42 @@ impl<'lua> ToLua<'lua> for PluginInfo<'lua> { } } +#[derive(Debug, Clone, Default)] +pub struct PluginInner { + pub plugin_dir: String, + pub from_loader: bool, +} + +impl<'lua> FromLua<'lua> for PluginInner { + fn from_lua(lua_value: mlua::Value<'lua>, _lua: &'lua mlua::Lua) -> mlua::Result { + let mut res = Self { + plugin_dir: String::new(), + from_loader: false, + }; + + if let mlua::Value::Table(t) = lua_value { + if let Ok(v) = t.get::<_, String>("plugin_dir") { + res.plugin_dir = v; + } + if let Ok(v) = t.get::<_, bool>("from_loader") { + res.from_loader = v; + } + } + Ok(res) + } +} + +impl<'lua> ToLua<'lua> for PluginInner { + fn to_lua(self, lua: &'lua mlua::Lua) -> mlua::Result> { + let res = lua.create_table()?; + + res.set("plugin_dir", self.plugin_dir)?; + res.set("from_loader", self.from_loader)?; + + Ok(mlua::Value::Table(res)) + } +} + #[derive(Debug, Clone, Default)] pub struct PluginBuildInfo<'lua> { pub on_start: Option>, diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs index 673f2ad5b..c3d7f43ab 100644 --- a/src/plugin/mod.rs +++ b/src/plugin/mod.rs @@ -4,7 +4,7 @@ use std::{ sync::Mutex, }; -use mlua::{AsChunk, Lua, Table}; +use mlua::{Lua, Table}; use serde_json::json; use crate::{ @@ -32,9 +32,17 @@ pub struct PluginManager; impl PluginManager { pub fn init(config: toml::Value) -> anyhow::Result<()> { + let config = PluginConfig::from_toml_value(config); + + if !config.available { + return Ok(()); + } + let lua = LUA.lock().unwrap(); let manager = lua.create_table().unwrap(); + let name_index = lua.create_table().unwrap(); + let plugin_dir = Self::init_plugin_dir(); let api = lua.create_table().unwrap(); @@ -51,18 +59,27 @@ impl PluginManager { lua.globals() .set("library_dir", plugin_dir.to_str().unwrap()) .unwrap(); - let config = PluginConfig::from_toml_value(config); - lua.globals().set("config_info", config)?; + lua.globals().set("config_info", config.clone())?; let mut index: u32 = 1; - let mut init_list: Vec<(u32, PathBuf, PluginInfo)> = Vec::new(); let dirs = std::fs::read_dir(&plugin_dir)?; - for entry in dirs { - if entry.is_err() { - continue; + + let mut path_list = dirs + .filter(|v| v.is_ok()) + .map(|v| (v.unwrap().path(), false)) + .collect::>(); + for i in &config.loader { + let path = PathBuf::from(i); + if !path.is_dir() { + // for loader dir, we need check first, because we need give a error log. + log::error!("Plugin loader: {:?} path is not a exists directory.", path); } - let entry = entry.unwrap(); - let plugin_dir = entry.path().to_path_buf(); + path_list.push((path, true)); + } + + for entry in path_list { + let plugin_dir = entry.0.to_path_buf(); + if plugin_dir.is_dir() { let init_file = plugin_dir.join("init.lua"); if init_file.is_file() { @@ -70,21 +87,78 @@ impl PluginManager { let mut buffer = String::new(); file.read_to_string(&mut buffer).unwrap(); + let current_plugin_dir = plugin_dir.to_str().unwrap().to_string(); + let from_loader = entry.1; + + lua.globals() + .set("_temp_plugin_dir", current_plugin_dir.clone())?; + lua.globals().set("_temp_from_loader", from_loader)?; + let info = lua.load(&buffer).eval::(); match info { - Ok(info) => { - let _ = manager.set(index, info.clone()); - - let dir_name_str = plugin_dir.name().unwrap().to_string(); - lua.globals().set("current_dir_name", dir_name_str).unwrap(); + Ok(mut info) => { + if name_index.contains_key(info.name.clone()).unwrap_or(false) + && !from_loader + { + // found same name plugin, intercept load + log::warn!( + "Plugin {} has been intercepted. [mulit-load]", + info.name + ); + continue; + } + info.inner.plugin_dir = current_plugin_dir; + info.inner.from_loader = from_loader; // call `on_init` if file "dcp.json" not exists let dcp_file = plugin_dir.join("dcp.json"); if !dcp_file.is_file() { - init_list.push((index, dcp_file, info)); - } + if let Some(func) = info.clone().on_init { + let result = func.call::<_, bool>(()); + match result { + Ok(true) => { + // plugin init success, create `dcp.json` file. + let mut file = std::fs::File::create(dcp_file).unwrap(); + let value = json!({ + "name": info.name, + "author": info.author, + "repository": info.repository, + "version": info.version, + "generate_time": chrono::Local::now().timestamp(), + }); + let buffer = + serde_json::to_string_pretty(&value).unwrap(); + let buffer = buffer.as_bytes(); + file.write_all(buffer).unwrap(); - index += 1; + // insert plugin-info into plugin-manager + if let Ok(index) = + name_index.get::<_, u32>(info.name.clone()) + { + let _ = manager.set(index, info.clone()); + } else { + let _ = manager.set(index, info.clone()); + index += 1; + let _ = name_index.set(info.name, index); + } + } + Ok(false) => { + log::warn!("Plugin init function result is `false`, init failed."); + } + Err(e) => { + log::warn!("Plugin init failed: {e}"); + } + } + } + } else { + if let Ok(index) = name_index.get::<_, u32>(info.name.clone()) { + let _ = manager.set(index, info.clone()); + } else { + let _ = manager.set(index, info.clone()); + index += 1; + let _ = name_index.set(info.name, index); + } + } } Err(_e) => { let dir_name = plugin_dir.file_name().unwrap().to_str().unwrap(); @@ -97,46 +171,6 @@ impl PluginManager { lua.globals().set("manager", manager).unwrap(); - for (idx, path, info) in init_list { - let res = lua - .load(mlua::chunk! { - manager[$idx].on_init() - }) - .eval::(); - match res { - Ok(true) => { - // plugin init success, create `dcp.json` file. - let mut file = std::fs::File::create(path).unwrap(); - let value = json!({ - "name": info.name, - "author": info.author, - "repository": info.repository, - "version": info.version, - "generate_time": chrono::Local::now().timestamp(), - }); - let buffer = serde_json::to_string_pretty(&value).unwrap(); - let buffer = buffer.as_bytes(); - file.write_all(buffer).unwrap(); - } - Ok(false) => { - log::warn!("Plugin init function result is `false`, init failed."); - let _ = lua - .load(mlua::chunk! { - table.remove(manager, $idx) - }) - .exec(); - } - Err(e) => { - // plugin init failed - log::warn!("Plugin init failed: {e}"); - let _ = lua - .load(mlua::chunk! { - table.remove(manager, $idx) - }) - .exec(); - } - } - } return Ok(()); } @@ -264,7 +298,7 @@ impl PluginManager { plugin_path } - pub fn plugin_list() -> Vec { + pub fn plugin_list_from_dir() -> Vec { let mut res = vec![]; let app_path = app_path(); @@ -280,6 +314,35 @@ impl PluginManager { } } } + res + } + + pub fn plugin_list() -> Vec { + let mut res = vec![]; + + if let Ok(lua) = LUA.lock() { + let list = lua + .load(mlua::chunk!( + local list = {} + for key, value in ipairs(manager) do + table.insert(list, {name = value.name, loader = value.inner.from_loader}) + end + return list + )) + .eval::>() + .unwrap_or_default(); + for i in list { + let name = i.get::<_, String>("name").unwrap(); + let loader = i.get::<_, bool>("loader").unwrap(); + + let text = if loader { + format!("{name} [:loader]") + } else { + name + }; + res.push(text); + } + } res } diff --git a/src/plugin/types.rs b/src/plugin/types.rs index a73a09bf0..d22c1423c 100644 --- a/src/plugin/types.rs +++ b/src/plugin/types.rs @@ -4,8 +4,9 @@ use mlua::ToLua; #[derive(Debug, Clone)] pub struct PluginConfig { - available: bool, - config_info: HashMap>, + pub available: bool, + pub loader: Vec, + pub config_info: HashMap>, } impl<'lua> ToLua<'lua> for PluginConfig { @@ -13,6 +14,7 @@ impl<'lua> ToLua<'lua> for PluginConfig { let table = lua.create_table()?; table.set("available", self.available)?; + table.set("loader", self.loader)?; let config_info = lua.create_table()?; @@ -34,9 +36,21 @@ impl PluginConfig { .unwrap_or(&toml::Value::Boolean(true)); let available = available.as_bool().unwrap_or(true); + let mut loader = vec![]; + if let Some(origin) = tab.get("loader") { + if origin.is_array() { + for i in origin.as_array().unwrap() { + loader.push(i.as_str().unwrap_or_default().to_string()); + } + } + } + let mut config_info = HashMap::new(); for (name, value) in tab { + if name == "available" || name == "loader" { + continue; + } if let toml::Value::Table(value) = value { let mut map = HashMap::new(); for (item, info) in value { @@ -48,11 +62,13 @@ impl PluginConfig { Self { available, + loader, config_info, } } else { Self { available: false, + loader: vec![], config_info: HashMap::new(), } } @@ -84,41 +100,39 @@ impl Value { v.push(Value::from_toml(i)); } Value::Array(v) - }, + } cargo_toml::Value::Table(t) => { let mut h = HashMap::new(); for (n, v) in t { h.insert(n, Value::from_toml(v)); } Value::Table(h) - }, + } } } } impl<'lua> ToLua<'lua> for Value { fn to_lua(self, lua: &'lua mlua::Lua) -> mlua::Result> { - Ok( - match self { - Value::String(s) => mlua::Value::String(lua.create_string(&s)?), - Value::Integer(i) => mlua::Value::Integer(i), - Value::Float(f) => mlua::Value::Number(f), - Value::Boolean(b) => mlua::Value::Boolean(b), - Value::Array(a) => { - let table = lua.create_table()?; - for (i, v) in a.iter().enumerate() { - table.set(i, v.clone())?; - } - mlua::Value::Table(table) - }, - Value::Table(t) => { - let table = lua.create_table()?; - for (i, v) in t.iter() { - table.set(i.clone(), v.clone())?; - } - mlua::Value::Table(table) - }, + Ok(match self { + Value::String(s) => mlua::Value::String(lua.create_string(&s)?), + Value::Integer(i) => mlua::Value::Integer(i), + Value::Float(f) => mlua::Value::Number(f), + Value::Boolean(b) => mlua::Value::Boolean(b), + Value::Array(a) => { + let table = lua.create_table()?; + for (i, v) in a.iter().enumerate() { + table.set(i, v.clone())?; + } + mlua::Value::Table(table) } - ) + Value::Table(t) => { + let table = lua.create_table()?; + for (i, v) in t.iter() { + table.set(i.clone(), v.clone())?; + } + mlua::Value::Table(table) + } + }) } -} \ No newline at end of file +}