use libloading::{Library, Symbol};
use mist_core::{
config::get_plugin_dir,
timer::{
state::{RunUpdate, StateChangeRequest},
Run,
},
};
use mist_pdk::{InitFunc, ShutdownFunc, UpdateFunc, VersionFunc};
use std::{
path::PathBuf,
sync::mpsc::{Receiver, Sender},
};
pub enum PluginMsg {
Update(RunUpdate),
Shutdown,
}
pub struct PluginErr {
pub ty: PluginErrTy,
pub plugin: String,
}
impl PluginErr {
fn missing_sym(plugin: String, sym: String) -> Self {
Self {
ty: PluginErrTy::MissingSym(sym),
plugin,
}
}
fn wrong_version(plugin: String, ver: u16) -> Self {
Self {
ty: PluginErrTy::WrongVersion(ver),
plugin,
}
}
fn filesystem(plugin: String) -> Self {
Self {
ty: PluginErrTy::Filesystem,
plugin,
}
}
}
pub enum PluginErrTy {
Filesystem,
WrongVersion(u16),
MissingSym(String),
}
struct PluginLib {
plugin: String,
lib: Library,
}
pub fn plugin_task(
run: Run,
rx: Receiver<PluginMsg>,
tx: Sender<StateChangeRequest>,
etx: Sender<PluginErr>,
) {
let plugin_path = get_plugin_dir().unwrap_or(PathBuf::from("./plugins/"));
if !plugin_path.exists() {
return;
}
let mut libs = vec![];
let mut updates = vec![];
for file in std::fs::read_dir(plugin_path)
.expect("Could not read plugin directory!")
.map_while(|r| r.ok())
{
if file.file_type().is_ok_and(|t| t.is_file()) {
let file_path = file.path();
let plug_name = file
.path()
.file_stem()
.map(|f| f.to_string_lossy())
.unwrap_or_else(|| file_path.to_string_lossy().clone())
.to_string();
if let Ok(l) = unsafe { Library::new(file_path.clone()) } {
libs.push(PluginLib {
lib: l,
plugin: plug_name,
});
} else {
etx.send(PluginErr::filesystem(plug_name)).unwrap();
}
}
}
for i in 0..libs.len() {
unsafe {
let version = if let Ok(v) = libs[i]
.lib
.get::<Symbol<VersionFunc>>(b"mist_plugin_version")
.or_else(|_| libs[i].lib.get::<Symbol<VersionFunc>>(b"version"))
{
v()
} else {
etx.send(PluginErr {
ty: PluginErrTy::MissingSym("mist_plugin_version".into()),
plugin: libs[i].plugin.clone(),
})
.unwrap();
continue;
};
if version == mist_pdk::pdk_version() {
if let Ok(i) = libs[i].lib.get::<Symbol<InitFunc>>(b"mist_plugin_init") {
i(&run);
} else {
etx.send(PluginErr::missing_sym(
libs[i].plugin.clone(),
"mist_plugin_init".into(),
))
.unwrap();
continue;
}
if let Ok(u) = libs[i].lib.get::<Symbol<UpdateFunc>>(b"mist_plugin_update") {
updates.push(u);
} else {
etx.send(PluginErr::missing_sym(
libs[i].plugin.clone(),
"mist_plugin_update".into(),
))
.unwrap();
continue;
}
} else {
etx.send(PluginErr::wrong_version(libs[i].plugin.clone(), version))
.unwrap();
}
}
}
while let Ok(PluginMsg::Update(upd)) = rx.recv() {
for f in &updates {
let rq = f(&upd);
if rq != StateChangeRequest::None {
let _ = tx.send(rq);
}
}
}
for l in libs {
unsafe {
if let Ok(s) = l.lib.get::<Symbol<ShutdownFunc>>(b"mist_plugin_shutdown") {
s();
}
}
}
}