kojacoord_plugin_system/
loader.rs1use crate::api::{Plugin, PluginContext, PluginMetadata};
2use crate::integrity::PluginVerifier;
3use anyhow::{Context, Result};
4use libloading::{Library, Symbol};
5use std::path::Path;
6
7pub struct PluginLoader {
8 libraries: Vec<(String, Library)>,
9 verifier: PluginVerifier,
10}
11
12impl PluginLoader {
13 pub fn new() -> Self {
14 Self {
15 libraries: Vec::new(),
16 verifier: PluginVerifier::new(),
17 }
18 }
19
20 pub fn with_verifier(verifier: PluginVerifier) -> Self {
22 Self {
23 libraries: Vec::new(),
24 verifier,
25 }
26 }
27
28 pub fn verifier_mut(&mut self) -> &mut PluginVerifier {
30 &mut self.verifier
31 }
32
33 pub fn load_plugin<P: AsRef<Path>>(
34 &mut self,
35 path: P,
36 context: &PluginContext,
37 ) -> Result<(Box<dyn Plugin>, PluginMetadata)> {
38 let path = path.as_ref();
39
40 self.verifier
44 .verify(path)
45 .context("plugin integrity verification failed")?;
46
47 unsafe {
55 let library = Library::new(path).context("Failed to load plugin library")?;
56
57 let get_metadata: Symbol<unsafe extern "C" fn() -> PluginMetadata> = library
58 .get(b"get_metadata")
59 .context("Missing get_metadata symbol")?;
60
61 let metadata = get_metadata();
62
63 if !self.check_version_compatibility(&metadata.min_proxy_version) {
64 return Err(anyhow::anyhow!(
65 "Plugin requires proxy version {}, current is {}",
66 metadata.min_proxy_version,
67 env!("CARGO_PKG_VERSION")
68 ));
69 }
70
71 let create_plugin: Symbol<unsafe extern "C" fn() -> *mut dyn Plugin> = library
72 .get(b"create_plugin")
73 .context("Missing create_plugin symbol")?;
74
75 let plugin_ptr = create_plugin();
76 if plugin_ptr.is_null() {
77 return Err(anyhow::anyhow!("create_plugin returned a null pointer"));
78 }
79 let mut plugin: Box<dyn Plugin> = Box::from_raw(plugin_ptr);
82
83 plugin.on_load(context)?;
84
85 self.libraries.push((metadata.name.clone(), library));
86
87 Ok((plugin, metadata))
88 }
89 }
90
91 pub fn unload_all(&mut self) {
92 self.libraries.clear();
93 }
94
95 fn check_version_compatibility(&self, required: &str) -> bool {
96 let current = env!("CARGO_PKG_VERSION");
97 current >= required
98 }
99}
100
101impl Default for PluginLoader {
102 fn default() -> Self {
103 Self::new()
104 }
105}