use std::{collections::HashMap, path::PathBuf};
use anyhow::Context;
use tokio::fs::read_to_string;
use crate::{get_home, submodules::build::Step};
use self::{config::PluginToml, executable::ExecutableLua};
pub mod api;
pub mod config;
pub mod executable;
#[derive(Debug)]
pub struct Plugin {
pub name: String,
pub version: String,
pub path: PathBuf,
pub step: Step,
pub priority: i32,
pub dependents: Option<(Vec<PathBuf>, Vec<PathBuf>)>,
pub package_paths: Vec<PathBuf>,
}
impl Plugin {
pub fn new(name: String, version: String, path: PathBuf, step: Step) -> Self {
Plugin {
name,
version,
path,
step,
priority: 0,
dependents: None,
package_paths: vec![],
}
}
pub fn load(&self) -> anyhow::Result<ExecutableLua> {
let mut exe = ExecutableLua::new(self.path.clone(), &self.package_paths);
exe.set_build_step(self.step);
exe.load_api_tables()
.context("Error injecting api tables into lua context")?;
Ok(exe)
}
}
async fn load(root: PathBuf) -> anyhow::Result<PluginToml> {
let mut path = root.clone();
path.push("plugin.toml");
let file_string = read_to_string(&path).await?;
let mut plugin: PluginToml = toml_edit::de::from_str(file_string.as_str())?;
plugin.path = root;
Ok(plugin)
}
pub fn load_plugins_config() -> anyhow::Result<Vec<PluginToml>> {
use anyhow::Ok;
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.context("Error creating a tokio runtime")?;
let plugins = runtime
.block_on(async {
let mut plugins: Vec<PluginToml> = vec![];
let mut path = get_home().context("Error loading labt home directory")?;
path.push("plugins");
let paths = (path
.read_dir()
.context("Error listing plugins directory contents on labt home")?)
.flatten();
let mut handlers = vec![];
for dir in paths {
if dir.file_type()?.is_dir() {
handlers.push((dir.path(), tokio::spawn(load(dir.path()))));
}
}
for (dir, handler) in handlers {
let plugin_result = handler.await?;
let plugin =
plugin_result.context(format!("Error parsing plugin config at {:?}", dir))?;
plugins.push(plugin);
}
Ok(plugins)
})
.context("Plugin config loader worker threads failed")?;
Ok(plugins)
}
pub fn load_plugins_from_paths(paths: Vec<PathBuf>) -> anyhow::Result<Vec<PluginToml>> {
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.context("Error creating a tokio runtime")?;
let plugins = runtime
.block_on(async {
let mut plugins: Vec<PluginToml> = vec![];
let mut handlers = vec![];
for path in &paths {
handlers.push((path, tokio::spawn(load(path.clone()))));
}
for (dir, handler) in handlers {
let plugin_result = handler.await?;
let plugin =
plugin_result.context(format!("Error parsing plugin config at {:?}", dir))?;
plugins.push(plugin);
}
Ok::<Vec<PluginToml>, anyhow::Error>(plugins)
})
.context("Plugin config loader worker threads failed")?;
Ok(plugins)
}
pub fn load_plugins(configs: Vec<PluginToml>) -> anyhow::Result<HashMap<Step, Vec<Plugin>>> {
let mut plugins: HashMap<Step, Vec<Plugin>> = HashMap::new();
for config in configs {
let plugin_steps = config
.get_steps()
.context("Unable to parse build stages from plugins")?;
for plugin in plugin_steps {
if let Some(step_vec) = plugins.get_mut(&plugin.step) {
step_vec.push(plugin);
} else {
plugins.insert(plugin.step, vec![plugin]);
}
}
}
Ok(plugins)
}