use gc_abi::{GCAbiConversion, raw};
use serde_json::Value;
use std::{
ffi::{CString, c_char},
sync::OnceLock,
};
use crate::{GCBorrowedDatapointValue, GCPluginInfo, GCPluginInterface, log};
static PLUGIN_INFO: OnceLock<GCPluginInfo> = OnceLock::new();
pub trait GCPluginInstance<'a>: Send + Sync + Sized {
fn init(plugin_interface: &'a GCPluginInterface) -> Box<Self>;
fn get_plugin_info() -> GCPluginInfo;
fn receive_datapoint(&self, data_value: GCBorrowedDatapointValue) -> bool;
fn get_config_schema() -> Option<Value> {
None
}
#[doc(hidden)]
#[inline]
unsafe fn gc_plugin_init(init_ctx: raw::GCPluginInterface) -> raw::GCInstanceHandle {
unsafe {
let release_callback = (*init_ctx).releaseDatapointValueCallback;
let result = std::panic::catch_unwind(|| {
crate::set_datapoint_release_fn(release_callback);
Self::init(init_ctx.into())
});
match result {
Ok(state) => std::boxed::Box::into_raw(state) as raw::GCInstanceHandle,
Err(e) => {
let init_ctx: &GCPluginInterface = init_ctx.into();
let msg = e
.downcast_ref::<&str>()
.map(|s| s.to_string())
.or_else(|| e.downcast_ref::<String>().cloned())
.unwrap_or_else(|| "Unknown panic".to_string());
log!(init_ctx, crate::raw::eGCLogLevel_ERROR, "Plugin panicked during initialization: {}", msg);
std::ptr::null_mut()
}
}
}
}
#[doc(hidden)]
#[inline]
unsafe fn gc_plugin_shutdown(instance: raw::GCInstanceHandle) {
unsafe {
drop(std::boxed::Box::from_raw(instance as *mut Self));
}
}
#[doc(hidden)]
#[inline]
unsafe fn gc_plugin_get_info() -> raw::GCPluginInfo {
unsafe { PLUGIN_INFO.get_or_init(|| Self::get_plugin_info()).as_ptr() }
}
#[doc(hidden)]
#[inline]
unsafe fn gc_plugin_receive_datapoint(instance: raw::GCInstanceHandle, data_value: raw::GCDatapointValue) -> bool {
unsafe {
let ctx = &*(instance as *const Self);
ctx.receive_datapoint(data_value.into())
}
}
#[doc(hidden)]
#[inline]
unsafe fn gc_plugin_config_schema() -> *mut c_char {
let schema = match Self::get_config_schema() {
Some(mut schema) => {
let version = Self::get_plugin_info().version();
let obj = schema.as_object_mut().unwrap();
obj.insert("version".to_string(), Value::String(version));
serde_json::to_string_pretty(&schema).unwrap()
}
None => "".to_string(),
};
let c_str_schema = CString::new(schema).unwrap();
c_str_schema.into_raw()
}
}