mod errors;
mod executor;
mod messaging;
use std::{
mem::{discriminant, MaybeUninit},
sync::{Arc, Mutex, RwLock},
};
use libloading::Symbol;
use log;
use kpal_plugin::error_codes::PLUGIN_OK;
use kpal_plugin::{KpalPluginInit, Plugin};
use crate::init::TSLibrary;
use crate::init::Transmitters;
use crate::models::{Library, Model, Peripheral};
use errors::MergeAttributesError;
pub use errors::PluginError;
pub use executor::Executor;
pub use messaging::*;
pub fn init(
peripheral: &mut Peripheral,
lib: TSLibrary,
txs: Arc<RwLock<Transmitters>>,
) -> std::result::Result<(), PluginError> {
let plugin: Plugin = {
let lib = lib.lock()?;
unsafe { kpal_plugin_new(&lib)? }
};
let mut executor = Executor::new(plugin);
merge_attributes(peripheral, lib)?;
peripheral.set_attribute_links();
log::debug!("Synchronizing the plugin with daemon's peripheral data");
executor.sync(peripheral)?;
log::debug!("Running the plugin's initialization routine");
executor.init()?;
log::debug!("Advancing the lifetime phase of the plugin");
executor.advance()?;
let tx = Mutex::new(executor.tx.clone());
txs.write()?.insert(peripheral.id(), tx);
log::debug!("Launching the plugin executor");
executor.run(peripheral.clone());
Ok(())
}
pub unsafe fn kpal_plugin_new(lib: &Library) -> Result<Plugin, PluginError> {
let dll = lib.dll().as_ref().ok_or(PluginError {
body: "Could not obtain reference to the plugin's shared library".to_string(),
http_status_code: 500,
})?;
let kpal_plugin_new: Symbol<KpalPluginInit> = dll.get(b"kpal_plugin_new\0")?;
let mut plugin = MaybeUninit::<Plugin>::uninit();
let result = kpal_plugin_new(plugin.as_mut_ptr());
if result != PLUGIN_OK {
log::error!("Plugin initialization failed: {}", result);
return Err(PluginError {
body: "Could not initialize plugin".to_string(),
http_status_code: 500,
});
}
Ok(plugin.assume_init())
}
fn merge_attributes(periph: &mut Peripheral, lib: TSLibrary) -> Result<(), MergeAttributesError> {
use crate::models::Attribute::*;
let lib = lib.lock()?;
let mut attrs = lib.attributes().clone();
for (id, periph_attr) in periph.attributes() {
let attr = attrs.get_mut(id).ok_or_else(|| {
MergeAttributesError::DoesNotExist(format!("Attribute does not exist: {}", id))
})?;
if discriminant(attr) != discriminant(periph_attr) {
return Err(MergeAttributesError::VariantMismatch(format!(
"Provided variant does not match plugin attribute: {}",
id
)));
};
let err = MergeAttributesError::IsNotPreInit(
"Attribute cannot be set before initialization".to_string(),
);
#[rustfmt::skip]
match (attr, periph_attr) {
(Int { pre_init, value: old_value, .. }, Int { value: new_value, .. }) => {
if !(*pre_init) { return Err(err); }
*old_value = *new_value
}
(Double { pre_init, value: old_value, .. }, Double { value: new_value, .. }) => {
if !(*pre_init) { return Err(err); }
*old_value = *new_value
}
(String { pre_init, value: old_value, .. }, String { value: new_value, .. }) => {
if !(*pre_init) { return Err(err); }
*old_value = new_value.clone()
}
(_, _) => {
return Err(MergeAttributesError::UnknownVariant(
"The daemon does not know how to merge this variant".to_string(),
))
}
};
}
periph.set_attributes(attrs);
Ok(())
}