#![warn(missing_docs)]
use anyhow::{bail, Context};
use hook_call::HookHandle;
use hooks::{Hook, OnLoad};
use mcvm_core::Paths;
use mcvm_shared::output::MCVMOutput;
use plugin::Plugin;
#[cfg(feature = "api")]
pub mod api;
pub mod hook_call;
pub mod hooks;
pub mod output;
pub mod plugin;
pub use mcvm_shared as shared;
pub static PLUGIN_DEBUG_ENV: &str = "MCVM_PLUGIN_DEBUG";
pub fn plugin_debug_enabled() -> bool {
std::env::var(PLUGIN_DEBUG_ENV).unwrap_or_default() == "1"
}
#[derive(Debug)]
pub struct CorePluginManager {
plugins: Vec<Plugin>,
plugin_list: Vec<String>,
mcvm_version: Option<&'static str>,
}
impl Default for CorePluginManager {
fn default() -> Self {
Self::new()
}
}
impl CorePluginManager {
pub fn new() -> Self {
Self {
plugins: Vec::new(),
plugin_list: Vec::new(),
mcvm_version: None,
}
}
pub fn set_mcvm_version(&mut self, version: &'static str) {
self.mcvm_version = Some(version);
}
pub fn add_plugin(
&mut self,
plugin: Plugin,
paths: &Paths,
o: &mut impl MCVMOutput,
) -> anyhow::Result<()> {
self.plugin_list.push(plugin.get_id().clone());
let result = plugin
.call_hook(&OnLoad, &(), paths, self.mcvm_version, &self.plugin_list, o)
.context("Failed to call on_load hook of plugin")?;
if let Some(result) = result {
result.result(o)?;
}
self.plugins.push(plugin);
Ok(())
}
pub fn call_hook<H: Hook>(
&self,
hook: H,
arg: &H::Arg,
paths: &Paths,
o: &mut impl MCVMOutput,
) -> anyhow::Result<Vec<HookHandle<H>>> {
let mut out = Vec::new();
for plugin in &self.plugins {
let result = plugin
.call_hook(&hook, arg, paths, self.mcvm_version, &self.plugin_list, o)
.with_context(|| format!("Hook failed for plugin {}", plugin.get_id()))?;
out.extend(result);
}
Ok(out)
}
pub fn call_hook_on_plugin<H: Hook>(
&self,
hook: H,
plugin_id: &str,
arg: &H::Arg,
paths: &Paths,
o: &mut impl MCVMOutput,
) -> anyhow::Result<Option<HookHandle<H>>> {
for plugin in &self.plugins {
if plugin.get_id() == plugin_id {
let result = plugin
.call_hook(&hook, arg, paths, self.mcvm_version, &self.plugin_list, o)
.context("Plugin hook failed")?;
return Ok(result);
}
}
bail!("No plugin found that matched the given ID")
}
pub fn iter_plugins(&self) -> impl Iterator<Item = &Plugin> {
self.plugins.iter()
}
pub fn has_plugin(&self, plugin_id: &str) -> bool {
self.plugin_list.iter().any(|x| x == plugin_id)
}
}