Prepare CLI for move into mainline
This commit is contained in:
1 parent
737910f8ae
commit
a9d7b4d32a
83 files changed
No files matched your search
@@ -0,0 +1,64 @@
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
use mlua::{FromLua, UserData};
|
||||
|
||||
enum StdioFromString {
|
||||
Inherit,
|
||||
Piped,
|
||||
Null,
|
||||
}
|
||||
impl<'lua> FromLua<'lua> for StdioFromString {
|
||||
fn from_lua(lua_value: mlua::Value<'lua>, _lua: &'lua mlua::Lua) -> mlua::Result<Self> {
|
||||
if let mlua::Value::String(v) = lua_value {
|
||||
let v = v.to_str().unwrap();
|
||||
return Ok(match v.to_lowercase().as_str() {
|
||||
"inherit" => Self::Inherit,
|
||||
"piped" => Self::Piped,
|
||||
"null" => Self::Null,
|
||||
_ => Self::Inherit,
|
||||
});
|
||||
}
|
||||
Ok(Self::Inherit)
|
||||
}
|
||||
}
|
||||
impl StdioFromString {
|
||||
pub fn to_stdio(self) -> Stdio {
|
||||
match self {
|
||||
StdioFromString::Inherit => Stdio::inherit(),
|
||||
StdioFromString::Piped => Stdio::piped(),
|
||||
StdioFromString::Null => Stdio::null(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PluginCommander;
|
||||
impl UserData for PluginCommander {
|
||||
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_function(
|
||||
"exec",
|
||||
|_, args: (Vec<String>, StdioFromString, StdioFromString)| {
|
||||
let cmd = args.0;
|
||||
let stdout = args.1;
|
||||
let stderr = args.2;
|
||||
|
||||
if cmd.len() == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
let cmd_name = cmd.get(0).unwrap();
|
||||
let mut command = Command::new(cmd_name);
|
||||
let t = cmd
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(i, _)| *i > 0)
|
||||
.map(|v| v.1.clone())
|
||||
.collect::<Vec<String>>();
|
||||
command.args(t);
|
||||
command.stdout(stdout.to_stdio()).stderr(stderr.to_stdio());
|
||||
command.output()?;
|
||||
Ok(())
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(_fields: &mut F) {}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
use mlua::UserData;
|
||||
|
||||
use crate::tools::app_path;
|
||||
|
||||
pub struct PluginDirs;
|
||||
impl UserData for PluginDirs {
|
||||
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_function("plugins_dir", |_, ()| {
|
||||
let path = app_path().join("plugins");
|
||||
Ok(path.to_str().unwrap().to_string())
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
use std::{
|
||||
fs::{create_dir, create_dir_all, remove_dir_all, File},
|
||||
io::{Read, Write},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use crate::tools::extract_zip;
|
||||
use flate2::read::GzDecoder;
|
||||
use mlua::UserData;
|
||||
use tar::Archive;
|
||||
|
||||
pub struct PluginFileSystem;
|
||||
impl UserData for PluginFileSystem {
|
||||
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_function("create_dir", |_, args: (String, bool)| {
|
||||
let path = args.0;
|
||||
let recursive = args.1;
|
||||
let path = PathBuf::from(path);
|
||||
if !path.exists() {
|
||||
let v = if recursive {
|
||||
create_dir_all(path)
|
||||
} else {
|
||||
create_dir(path)
|
||||
};
|
||||
return Ok(v.is_ok());
|
||||
}
|
||||
Ok(true)
|
||||
});
|
||||
methods.add_function("remove_dir", |_, path: String| {
|
||||
let path = PathBuf::from(path);
|
||||
let r = remove_dir_all(path);
|
||||
Ok(r.is_ok())
|
||||
});
|
||||
methods.add_function("file_get_content", |_, path: String| {
|
||||
let path = PathBuf::from(path);
|
||||
let mut file = std::fs::File::open(path)?;
|
||||
let mut buffer = String::new();
|
||||
file.read_to_string(&mut buffer)?;
|
||||
Ok(buffer)
|
||||
});
|
||||
methods.add_function("file_set_content", |_, args: (String, String)| {
|
||||
let path = args.0;
|
||||
let content = args.1;
|
||||
let path = PathBuf::from(path);
|
||||
|
||||
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);
|
||||
let target = PathBuf::from(args.1);
|
||||
let res = extract_zip(&file, &target);
|
||||
if let Err(_) = res {
|
||||
return Ok(false);
|
||||
}
|
||||
Ok(true)
|
||||
});
|
||||
methods.add_function("untar_gz_file", |_, args: (String, String)| {
|
||||
let file = PathBuf::from(args.0);
|
||||
let target = PathBuf::from(args.1);
|
||||
|
||||
let tar_gz = if let Ok(v) = File::open(file) {
|
||||
v
|
||||
} else {
|
||||
return Ok(false);
|
||||
};
|
||||
|
||||
let tar = GzDecoder::new(tar_gz);
|
||||
let mut archive = Archive::new(tar);
|
||||
if archive.unpack(&target).is_err() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
use log;
|
||||
use mlua::UserData;
|
||||
|
||||
pub struct PluginLogger;
|
||||
impl UserData for PluginLogger {
|
||||
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_function("trace", |_, info: String| {
|
||||
log::trace!("{}", info);
|
||||
Ok(())
|
||||
});
|
||||
methods.add_function("info", |_, info: String| {
|
||||
log::info!("{}", info);
|
||||
Ok(())
|
||||
});
|
||||
methods.add_function("debug", |_, info: String| {
|
||||
log::debug!("{}", info);
|
||||
Ok(())
|
||||
});
|
||||
methods.add_function("warn", |_, info: String| {
|
||||
log::warn!("{}", info);
|
||||
Ok(())
|
||||
});
|
||||
methods.add_function("error", |_, info: String| {
|
||||
log::error!("{}", info);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
use mlua::{FromLua, Function, ToLua};
|
||||
|
||||
pub mod command;
|
||||
pub mod dirs;
|
||||
pub mod fs;
|
||||
pub mod log;
|
||||
pub mod network;
|
||||
pub mod os;
|
||||
pub mod path;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PluginInfo<'lua> {
|
||||
pub name: String,
|
||||
pub repository: String,
|
||||
pub author: String,
|
||||
pub version: String,
|
||||
|
||||
pub inner: PluginInner,
|
||||
|
||||
pub on_init: Option<Function<'lua>>,
|
||||
pub build: PluginBuildInfo<'lua>,
|
||||
pub serve: PluginServeInfo<'lua>,
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for PluginInfo<'lua> {
|
||||
fn from_lua(lua_value: mlua::Value<'lua>, _lua: &'lua mlua::Lua) -> mlua::Result<Self> {
|
||||
let mut res = Self {
|
||||
name: String::default(),
|
||||
repository: String::default(),
|
||||
author: String::default(),
|
||||
version: String::from("0.1.0"),
|
||||
|
||||
inner: Default::default(),
|
||||
|
||||
on_init: None,
|
||||
build: Default::default(),
|
||||
serve: Default::default(),
|
||||
};
|
||||
if let mlua::Value::Table(tab) = lua_value {
|
||||
if let Ok(v) = tab.get::<_, String>("name") {
|
||||
res.name = v;
|
||||
}
|
||||
if let Ok(v) = tab.get::<_, String>("repository") {
|
||||
res.repository = v;
|
||||
}
|
||||
if let Ok(v) = tab.get::<_, String>("author") {
|
||||
res.author = v;
|
||||
}
|
||||
if let Ok(v) = tab.get::<_, String>("version") {
|
||||
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);
|
||||
}
|
||||
|
||||
if let Ok(v) = tab.get::<_, PluginBuildInfo>("build") {
|
||||
res.build = v;
|
||||
}
|
||||
|
||||
if let Ok(v) = tab.get::<_, PluginServeInfo>("serve") {
|
||||
res.serve = v;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for PluginInfo<'lua> {
|
||||
fn to_lua(self, lua: &'lua mlua::Lua) -> mlua::Result<mlua::Value<'lua>> {
|
||||
let res = lua.create_table()?;
|
||||
|
||||
res.set("name", self.name.to_string())?;
|
||||
res.set("repository", self.repository.to_string())?;
|
||||
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)?;
|
||||
}
|
||||
res.set("build", self.build)?;
|
||||
res.set("serve", self.serve)?;
|
||||
|
||||
Ok(mlua::Value::Table(res))
|
||||
}
|
||||
}
|
||||
|
||||
#[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>>,
|
||||
pub on_finish: Option<Function<'lua>>,
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for PluginBuildInfo<'lua> {
|
||||
fn from_lua(lua_value: mlua::Value<'lua>, _lua: &'lua mlua::Lua) -> mlua::Result<Self> {
|
||||
let mut res = Self {
|
||||
on_start: None,
|
||||
on_finish: None,
|
||||
};
|
||||
|
||||
if let mlua::Value::Table(t) = lua_value {
|
||||
if let Ok(v) = t.get::<_, Function>("on_start") {
|
||||
res.on_start = Some(v);
|
||||
}
|
||||
if let Ok(v) = t.get::<_, Function>("on_finish") {
|
||||
res.on_finish = Some(v);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for PluginBuildInfo<'lua> {
|
||||
fn to_lua(self, lua: &'lua mlua::Lua) -> mlua::Result<mlua::Value<'lua>> {
|
||||
let res = lua.create_table()?;
|
||||
|
||||
if let Some(v) = self.on_start {
|
||||
res.set("on_start", v)?;
|
||||
}
|
||||
|
||||
if let Some(v) = self.on_finish {
|
||||
res.set("on_finish", v)?;
|
||||
}
|
||||
|
||||
Ok(mlua::Value::Table(res))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct PluginServeInfo<'lua> {
|
||||
pub interval: i32,
|
||||
|
||||
pub on_start: Option<Function<'lua>>,
|
||||
pub on_interval: Option<Function<'lua>>,
|
||||
pub on_rebuild: Option<Function<'lua>>,
|
||||
pub on_shutdown: Option<Function<'lua>>,
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for PluginServeInfo<'lua> {
|
||||
fn from_lua(lua_value: mlua::Value<'lua>, _lua: &'lua mlua::Lua) -> mlua::Result<Self> {
|
||||
let mut res = Self::default();
|
||||
|
||||
if let mlua::Value::Table(tab) = lua_value {
|
||||
if let Ok(v) = tab.get::<_, i32>("interval") {
|
||||
res.interval = v;
|
||||
}
|
||||
if let Ok(v) = tab.get::<_, Function>("on_start") {
|
||||
res.on_start = Some(v);
|
||||
}
|
||||
if let Ok(v) = tab.get::<_, Function>("on_interval") {
|
||||
res.on_interval = Some(v);
|
||||
}
|
||||
if let Ok(v) = tab.get::<_, Function>("on_rebuild") {
|
||||
res.on_rebuild = Some(v);
|
||||
}
|
||||
if let Ok(v) = tab.get::<_, Function>("on_shutdown") {
|
||||
res.on_shutdown = Some(v);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for PluginServeInfo<'lua> {
|
||||
fn to_lua(self, lua: &'lua mlua::Lua) -> mlua::Result<mlua::Value<'lua>> {
|
||||
let res = lua.create_table()?;
|
||||
|
||||
res.set("interval", self.interval)?;
|
||||
|
||||
if let Some(v) = self.on_start {
|
||||
res.set("on_start", v)?;
|
||||
}
|
||||
|
||||
if let Some(v) = self.on_interval {
|
||||
res.set("on_interval", v)?;
|
||||
}
|
||||
|
||||
if let Some(v) = self.on_rebuild {
|
||||
res.set("on_rebuild", v)?;
|
||||
}
|
||||
|
||||
if let Some(v) = self.on_shutdown {
|
||||
res.set("on_shutdown", v)?;
|
||||
}
|
||||
|
||||
Ok(mlua::Value::Table(res))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
use std::{io::Cursor, path::PathBuf};
|
||||
|
||||
use mlua::UserData;
|
||||
|
||||
pub struct PluginNetwork;
|
||||
impl UserData for PluginNetwork {
|
||||
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_function("download_file", |_, args: (String, String)| {
|
||||
let url = args.0;
|
||||
let path = args.1;
|
||||
|
||||
let resp = reqwest::blocking::get(url);
|
||||
if let Ok(resp) = resp {
|
||||
let mut content = Cursor::new(resp.bytes().unwrap());
|
||||
let file = std::fs::File::create(PathBuf::from(path));
|
||||
if file.is_err() {
|
||||
return Ok(false);
|
||||
}
|
||||
let mut file = file.unwrap();
|
||||
let res = std::io::copy(&mut content, &mut file);
|
||||
return Ok(res.is_ok());
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
use mlua::UserData;
|
||||
|
||||
pub struct PluginOS;
|
||||
impl UserData for PluginOS {
|
||||
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_function("current_platform", |_, ()| {
|
||||
if cfg!(target_os = "windows") {
|
||||
Ok("windows")
|
||||
} else if cfg!(target_os = "macos") {
|
||||
Ok("macos")
|
||||
} else if cfg!(target_os = "linux") {
|
||||
Ok("linux")
|
||||
} else {
|
||||
panic!("unsupported platformm");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use mlua::{UserData, Variadic};
|
||||
|
||||
pub struct PluginPath;
|
||||
impl UserData for PluginPath {
|
||||
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
// join function
|
||||
methods.add_function("join", |_, args: Variadic<String>| {
|
||||
let mut path = PathBuf::new();
|
||||
for i in args {
|
||||
path = path.join(i);
|
||||
}
|
||||
Ok(path.to_str().unwrap().to_string())
|
||||
});
|
||||
|
||||
// parent function
|
||||
methods.add_function("parent", |_, path: String| {
|
||||
let current_path = PathBuf::from(&path);
|
||||
let parent = current_path.parent();
|
||||
if parent.is_none() {
|
||||
return Ok(path);
|
||||
} else {
|
||||
return Ok(parent.unwrap().to_str().unwrap().to_string());
|
||||
}
|
||||
});
|
||||
methods.add_function("exists", |_, path: String| {
|
||||
let path = PathBuf::from(path);
|
||||
Ok(path.exists())
|
||||
});
|
||||
methods.add_function("is_dir", |_, path: String| {
|
||||
let path = PathBuf::from(path);
|
||||
Ok(path.is_dir())
|
||||
});
|
||||
methods.add_function("is_file", |_, path: String| {
|
||||
let path = PathBuf::from(path);
|
||||
Ok(path.is_file())
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
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();
|
||||
|
||||
return 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use mlua::ToLua;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PluginConfig {
|
||||
pub available: bool,
|
||||
pub loader: Vec<String>,
|
||||
pub config_info: HashMap<String, HashMap<String, Value>>,
|
||||
}
|
||||
|
||||
impl<'lua> ToLua<'lua> for PluginConfig {
|
||||
fn to_lua(self, lua: &'lua mlua::Lua) -> mlua::Result<mlua::Value<'lua>> {
|
||||
let table = lua.create_table()?;
|
||||
|
||||
table.set("available", self.available)?;
|
||||
table.set("loader", self.loader)?;
|
||||
|
||||
let config_info = lua.create_table()?;
|
||||
|
||||
for (name, data) in self.config_info {
|
||||
config_info.set(name, data)?;
|
||||
}
|
||||
|
||||
table.set("config_info", config_info)?;
|
||||
|
||||
Ok(mlua::Value::Table(table))
|
||||
}
|
||||
}
|
||||
|
||||
impl PluginConfig {
|
||||
pub fn from_toml_value(val: toml::Value) -> Self {
|
||||
if let toml::Value::Table(tab) = val {
|
||||
let available = tab
|
||||
.get::<_>("available")
|
||||
.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 {
|
||||
map.insert(item, Value::from_toml(info));
|
||||
}
|
||||
config_info.insert(name, map);
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
available,
|
||||
loader,
|
||||
config_info,
|
||||
}
|
||||
} else {
|
||||
Self {
|
||||
available: false,
|
||||
loader: vec![],
|
||||
config_info: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Value {
|
||||
String(String),
|
||||
Integer(i64),
|
||||
Float(f64),
|
||||
Boolean(bool),
|
||||
Array(Vec<Value>),
|
||||
Table(HashMap<String, Value>),
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn from_toml(origin: toml::Value) -> Self {
|
||||
match origin {
|
||||
cargo_toml::Value::String(s) => Value::String(s),
|
||||
cargo_toml::Value::Integer(i) => Value::Integer(i),
|
||||
cargo_toml::Value::Float(f) => Value::Float(f),
|
||||
cargo_toml::Value::Boolean(b) => Value::Boolean(b),
|
||||
cargo_toml::Value::Datetime(d) => Value::String(d.to_string()),
|
||||
cargo_toml::Value::Array(a) => {
|
||||
let mut v = vec![];
|
||||
for i in a {
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user