1#![warn(missing_docs)]
2
3use anyhow::{bail, Context};
7use hook_call::HookHandle;
8use hooks::{Hook, OnLoad};
9use mcvm_core::Paths;
10use mcvm_shared::output::MCVMOutput;
11use plugin::Plugin;
12
13#[cfg(feature = "api")]
15pub mod api;
16pub mod hook_call;
18pub mod hooks;
20pub mod output;
22pub mod plugin;
24
25pub use mcvm_shared as shared;
26
27pub static PLUGIN_DEBUG_ENV: &str = "MCVM_PLUGIN_DEBUG";
29
30pub fn plugin_debug_enabled() -> bool {
32 std::env::var(PLUGIN_DEBUG_ENV).unwrap_or_default() == "1"
33}
34
35#[derive(Debug)]
38pub struct CorePluginManager {
39 plugins: Vec<Plugin>,
40 plugin_list: Vec<String>,
41 mcvm_version: Option<&'static str>,
42}
43
44impl Default for CorePluginManager {
45 fn default() -> Self {
46 Self::new()
47 }
48}
49
50impl CorePluginManager {
51 pub fn new() -> Self {
53 Self {
54 plugins: Vec::new(),
55 plugin_list: Vec::new(),
56 mcvm_version: None,
57 }
58 }
59
60 pub fn set_mcvm_version(&mut self, version: &'static str) {
62 self.mcvm_version = Some(version);
63 }
64
65 pub fn add_plugin(
67 &mut self,
68 plugin: Plugin,
69 paths: &Paths,
70 o: &mut impl MCVMOutput,
71 ) -> anyhow::Result<()> {
72 self.plugin_list.push(plugin.get_id().clone());
74
75 let result = plugin
77 .call_hook(&OnLoad, &(), paths, self.mcvm_version, &self.plugin_list, o)
78 .context("Failed to call on_load hook of plugin")?;
79 if let Some(result) = result {
80 result.result(o)?;
81 }
82
83 self.plugins.push(plugin);
84
85 Ok(())
86 }
87
88 pub fn call_hook<H: Hook>(
90 &self,
91 hook: H,
92 arg: &H::Arg,
93 paths: &Paths,
94 o: &mut impl MCVMOutput,
95 ) -> anyhow::Result<Vec<HookHandle<H>>> {
96 let mut out = Vec::new();
97 for plugin in &self.plugins {
98 let result = plugin
99 .call_hook(&hook, arg, paths, self.mcvm_version, &self.plugin_list, o)
100 .with_context(|| format!("Hook failed for plugin {}", plugin.get_id()))?;
101 out.extend(result);
102 }
103
104 Ok(out)
105 }
106
107 pub fn call_hook_on_plugin<H: Hook>(
109 &self,
110 hook: H,
111 plugin_id: &str,
112 arg: &H::Arg,
113 paths: &Paths,
114 o: &mut impl MCVMOutput,
115 ) -> anyhow::Result<Option<HookHandle<H>>> {
116 for plugin in &self.plugins {
117 if plugin.get_id() == plugin_id {
118 let result = plugin
119 .call_hook(&hook, arg, paths, self.mcvm_version, &self.plugin_list, o)
120 .context("Plugin hook failed")?;
121 return Ok(result);
122 }
123 }
124
125 bail!("No plugin found that matched the given ID")
126 }
127
128 pub fn iter_plugins(&self) -> impl Iterator<Item = &Plugin> {
130 self.plugins.iter()
131 }
132
133 pub fn has_plugin(&self, plugin_id: &str) -> bool {
135 self.plugin_list.iter().any(|x| x == plugin_id)
136 }
137}