Browse Source

fix: plugin init problem

YuKun Liu 2 years ago
parent
commit
0fe8e4bcbd
4 changed files with 218 additions and 87 deletions
  1. 11 3
      src/plugin/interface/fs.rs
  2. 46 0
      src/plugin/interface/mod.rs
  3. 121 58
      src/plugin/mod.rs
  4. 40 26
      src/plugin/types.rs

+ 11 - 3
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);

+ 46 - 0
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<Function<'lua>>,
     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<Self> {
+        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<mlua::Value<'lua>> {
+        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<Function<'lua>>,

+ 121 - 58
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::<Vec<(PathBuf, bool)>>();
+        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::<PluginInfo>();
                     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();
+
+                                            // 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);
+                                }
                             }
-
-                            index += 1;
                         }
                         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::<bool>();
-            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<String> {
+    pub fn plugin_list_from_dir() -> Vec<String> {
         let mut res = vec![];
 
         let app_path = app_path();
@@ -280,6 +314,35 @@ impl PluginManager {
                 }
             }
         }
+        res
+    }
+
+    pub fn plugin_list() -> Vec<String> {
+        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::<Vec<Table>>()
+                .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
     }

+ 40 - 26
src/plugin/types.rs

@@ -4,8 +4,9 @@ use mlua::ToLua;
 
 #[derive(Debug, Clone)]
 pub struct PluginConfig {
-    available: bool,
-    config_info: HashMap<String, HashMap<String, Value>>,
+    pub available: bool,
+    pub loader: Vec<String>,
+    pub config_info: HashMap<String, HashMap<String, Value>>,
 }
 
 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<mlua::Value<'lua>> {
-        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)
+            }
+        })
     }
-}
+}