332 lines
12 KiB
Rust
332 lines
12 KiB
Rust
use std::{
|
|
io::{Read, Write},
|
|
path::PathBuf,
|
|
sync::Mutex,
|
|
};
|
|
|
|
use mlua::{Lua, Table};
|
|
use serde_json::json;
|
|
|
|
use crate::{
|
|
tools::{app_path, clone_repo},
|
|
CrateConfig,
|
|
};
|
|
|
|
use self::{
|
|
interface::{
|
|
command::PluginCommander, dirs::PluginDirs, fs::PluginFileSystem, log::PluginLogger,
|
|
network::PluginNetwork, os::PluginOS, path::PluginPath, PluginInfo,
|
|
},
|
|
types::PluginConfig,
|
|
};
|
|
|
|
pub mod interface;
|
|
mod types;
|
|
|
|
lazy_static::lazy_static! {
|
|
static ref LUA: Mutex<Lua> = Mutex::new(Lua::new());
|
|
}
|
|
|
|
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();
|
|
|
|
api.set("log", PluginLogger).unwrap();
|
|
api.set("command", PluginCommander).unwrap();
|
|
api.set("network", PluginNetwork).unwrap();
|
|
api.set("dirs", PluginDirs).unwrap();
|
|
api.set("fs", PluginFileSystem).unwrap();
|
|
api.set("path", PluginPath).unwrap();
|
|
api.set("os", PluginOS).unwrap();
|
|
|
|
lua.globals().set("plugin_lib", api).unwrap();
|
|
lua.globals()
|
|
.set("library_dir", plugin_dir.to_str().unwrap())
|
|
.unwrap();
|
|
lua.globals().set("config_info", config.clone())?;
|
|
|
|
let mut index: u32 = 1;
|
|
let dirs = std::fs::read_dir(&plugin_dir)?;
|
|
|
|
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);
|
|
}
|
|
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() {
|
|
let mut file = std::fs::File::open(init_file).unwrap();
|
|
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(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() {
|
|
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);
|
|
}
|
|
}
|
|
Err(_e) => {
|
|
let dir_name = plugin_dir.file_name().unwrap().to_str().unwrap();
|
|
log::error!("Plugin '{dir_name}' load failed.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
lua.globals().set("manager", manager).unwrap();
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn on_build_start(crate_config: &CrateConfig, platform: &str) -> anyhow::Result<()> {
|
|
let lua = LUA.lock().unwrap();
|
|
|
|
if !lua.globals().contains_key("manager")? {
|
|
return Ok(());
|
|
}
|
|
let manager = lua.globals().get::<_, Table>("manager")?;
|
|
|
|
let args = lua.create_table()?;
|
|
args.set("name", crate_config.dioxus_config.application.name.clone())?;
|
|
args.set("platform", platform)?;
|
|
args.set("out_dir", crate_config.out_dir.to_str().unwrap())?;
|
|
args.set("asset_dir", crate_config.asset_dir.to_str().unwrap())?;
|
|
|
|
for i in 1..(manager.len()? as i32 + 1) {
|
|
let info = manager.get::<i32, PluginInfo>(i)?;
|
|
if let Some(func) = info.build.on_start {
|
|
func.call::<Table, ()>(args.clone())?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn on_build_finish(crate_config: &CrateConfig, platform: &str) -> anyhow::Result<()> {
|
|
let lua = LUA.lock().unwrap();
|
|
|
|
if !lua.globals().contains_key("manager")? {
|
|
return Ok(());
|
|
}
|
|
let manager = lua.globals().get::<_, Table>("manager")?;
|
|
|
|
let args = lua.create_table()?;
|
|
args.set("name", crate_config.dioxus_config.application.name.clone())?;
|
|
args.set("platform", platform)?;
|
|
args.set("out_dir", crate_config.out_dir.to_str().unwrap())?;
|
|
args.set("asset_dir", crate_config.asset_dir.to_str().unwrap())?;
|
|
|
|
for i in 1..(manager.len()? as i32 + 1) {
|
|
let info = manager.get::<i32, PluginInfo>(i)?;
|
|
if let Some(func) = info.build.on_finish {
|
|
func.call::<Table, ()>(args.clone())?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn on_serve_start(crate_config: &CrateConfig) -> anyhow::Result<()> {
|
|
let lua = LUA.lock().unwrap();
|
|
|
|
if !lua.globals().contains_key("manager")? {
|
|
return Ok(());
|
|
}
|
|
let manager = lua.globals().get::<_, Table>("manager")?;
|
|
|
|
let args = lua.create_table()?;
|
|
args.set("name", crate_config.dioxus_config.application.name.clone())?;
|
|
|
|
for i in 1..(manager.len()? as i32 + 1) {
|
|
let info = manager.get::<i32, PluginInfo>(i)?;
|
|
if let Some(func) = info.serve.on_start {
|
|
func.call::<Table, ()>(args.clone())?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn on_serve_rebuild(timestamp: i64, files: Vec<PathBuf>) -> anyhow::Result<()> {
|
|
let lua = LUA.lock().unwrap();
|
|
|
|
let manager = lua.globals().get::<_, Table>("manager")?;
|
|
|
|
let args = lua.create_table()?;
|
|
args.set("timestamp", timestamp)?;
|
|
let files: Vec<String> = files
|
|
.iter()
|
|
.map(|v| v.to_str().unwrap().to_string())
|
|
.collect();
|
|
args.set("changed_files", files)?;
|
|
|
|
for i in 1..(manager.len()? as i32 + 1) {
|
|
let info = manager.get::<i32, PluginInfo>(i)?;
|
|
if let Some(func) = info.serve.on_rebuild {
|
|
func.call::<Table, ()>(args.clone())?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn on_serve_shutdown(crate_config: &CrateConfig) -> anyhow::Result<()> {
|
|
let lua = LUA.lock().unwrap();
|
|
|
|
if !lua.globals().contains_key("manager")? {
|
|
return Ok(());
|
|
}
|
|
let manager = lua.globals().get::<_, Table>("manager")?;
|
|
|
|
let args = lua.create_table()?;
|
|
args.set("name", crate_config.dioxus_config.application.name.clone())?;
|
|
|
|
for i in 1..(manager.len()? as i32 + 1) {
|
|
let info = manager.get::<i32, PluginInfo>(i)?;
|
|
if let Some(func) = info.serve.on_shutdown {
|
|
func.call::<Table, ()>(args.clone())?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn init_plugin_dir() -> PathBuf {
|
|
let app_path = app_path();
|
|
let plugin_path = app_path.join("plugins");
|
|
if !plugin_path.is_dir() {
|
|
log::info!("📖 Start to init plugin library ...");
|
|
let url = "https://github.com/DioxusLabs/cli-plugin-library";
|
|
if let Err(err) = clone_repo(&plugin_path, url) {
|
|
log::error!("Failed to init plugin dir, error caused by {}. ", err);
|
|
}
|
|
}
|
|
plugin_path
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|