use std::ffi::c_void;
use std::marker::PhantomData;
use std::ptr::NonNull;
use super::errors::GCMockingError;
use super::gateway_instance_ctx::GCGatewayMockInstanceCtx;
use super::gateway_mock_builder::GCGatewayMockBuilder;
use super::gateway_mock_extern::{
audit_log, get_ethernet_interface, get_last_datapoint_value, get_serial_interface, publish_datapoint_value, release_datapoint_value, system_log,
};
use crate::{GCDatapoint, GCDatapointValue, GCPluginInfo};
use crate::{GCPluginInstance, GCPluginInterface};
use gc_abi::raw;
use gc_json_bridge::GCJsonBridge;
use serde_json::Value;
pub struct GCGatewayMock<'a, T: GCPluginInstance<'a>> {
plugin_interface: Box<raw::sGCPluginInterface>,
_own_datapoints: Vec<GCDatapoint>,
_subscribed_datapoints: Vec<GCDatapoint>,
_json_bridge: Box<GCJsonBridge>,
json_data: &'static Value,
plugin_ctx: Option<NonNull<T>>, mark: PhantomData<&'a T>,
}
impl<'a, T: GCPluginInstance<'a>> GCGatewayMock<'a, T> {
pub fn new(mock_builder: GCGatewayMockBuilder) -> Result<Self, GCMockingError> {
let leaked_json = Box::leak(mock_builder.config);
let own_datapoints = mock_builder.own_datapoints;
let subscribed_datapoints = mock_builder.subscribed_datapoints;
let gateway_ctx = mock_builder.core_ctx;
let bridge = Box::new(GCJsonBridge::new(leaked_json));
let callbacks = std::ptr::from_ref(bridge.gc_json_bridge_callbacks());
let config = raw::GCPluginConfig {
pluginJsonConfig: callbacks,
ownDatapoints: own_datapoints.as_ptr() as *const raw::GCDatapoint,
ownDatapointsCount: own_datapoints.len() as u64,
subscribedDatapoints: subscribed_datapoints.as_ptr() as *const raw::GCDatapoint,
subscribedDatapointsCount: subscribed_datapoints.len() as u64,
};
let interface: Box<raw::sGCPluginInterface> = Box::new(raw::sGCPluginInterface {
ctx: Box::into_raw(gateway_ctx) as raw::GCCoreCtx,
logLevel: mock_builder.log_level.into_c(),
config,
publishDatapointValueCallback: Some(publish_datapoint_value),
logSystemCallback: Some(system_log),
logAuditCallback: Some(audit_log),
releaseDatapointValueCallback: Some(release_datapoint_value),
getLastDatapointValueCallback: Some(get_last_datapoint_value),
getEthernetInterfaceCallback: Some(get_ethernet_interface),
getSerialInterfaceCallback: Some(get_serial_interface),
storeDatapointValueCallback: None,
});
let ctx = Self {
plugin_interface: interface,
_own_datapoints: own_datapoints,
_subscribed_datapoints: subscribed_datapoints,
_json_bridge: bridge,
json_data: leaked_json,
plugin_ctx: None,
mark: PhantomData,
};
Ok(ctx)
}
pub(crate) fn start(&mut self) -> Result<*const T, GCMockingError> {
if self.plugin_ctx.is_some() {
return Err(GCMockingError::ErrorPluginAlreadyLoaded);
}
let interface: raw::GCPluginInterface = &*self.plugin_interface;
let result = unsafe { T::gc_plugin_init(interface) };
if result.is_null() {
return Err(GCMockingError::ErrorPluginInitFailed);
}
self.plugin_ctx = Some(NonNull::new(result as *mut T)).ok_or(GCMockingError::ErrorPluginInitFailed)?;
Ok(result as *const T)
}
pub fn start_from<F>(&mut self, f: F) -> Result<(), GCMockingError>
where
F: FnOnce(&'a GCPluginInterface) -> Box<T> + 'a,
{
if self.plugin_ctx.is_some() {
return Err(GCMockingError::ErrorPluginAlreadyLoaded);
}
let release_callback = self.plugin_interface.releaseDatapointValueCallback;
crate::set_datapoint_release_fn(release_callback);
let result = f(self.get_gateway_interface());
self.plugin_ctx = Some(NonNull::new(Box::into_raw(result))).ok_or(GCMockingError::ErrorPluginInitFailed)?;
Ok(())
}
pub fn send_subscribed_datapoint(&self, datapoint_value: &GCDatapointValue) -> Result<(), GCMockingError> {
if let Some(plugin_ctx) = self.plugin_ctx {
let dp_copy = Box::new(datapoint_value.clone());
let dp_copy_raw = Box::into_raw(dp_copy);
unsafe { T::gc_plugin_receive_datapoint(plugin_ctx.as_ptr() as *mut c_void, dp_copy_raw as raw::GCDatapointValue) };
Ok(())
} else {
Err(GCMockingError::ErrorPluginNotLoaded)
}
}
pub fn get_gateway_interface(&self) -> &'a GCPluginInterface {
let ptr = self.plugin_interface.as_ref() as raw::GCPluginInterface;
ptr.into()
}
pub fn get_plugin_info() -> &'static GCPluginInfo {
unsafe {
let result = T::gc_plugin_get_info() as *const GCPluginInfo;
(&*result) as _
}
}
pub fn get_plugin_instance(&self) -> Result<&T, GCMockingError> {
if let Some(plugin_ctx) = self.plugin_ctx {
Ok(unsafe { &*(plugin_ctx.as_ptr()) })
} else {
Err(GCMockingError::ErrorPluginNotLoaded)
}
}
}
impl<'a, T: GCPluginInstance<'a>> Drop for GCGatewayMock<'a, T> {
fn drop(&mut self) {
if let Some(plugin_ctx) = self.plugin_ctx {
unsafe { T::gc_plugin_shutdown(plugin_ctx.as_ptr() as *mut c_void) };
unsafe {
drop(Box::from_raw(self.json_data as *const Value as *mut Value));
drop(Box::from_raw(self.plugin_interface.ctx as *mut GCGatewayMockInstanceCtx));
}
}
}
}
unsafe impl<'a, T: GCPluginInstance<'a>> Send for GCGatewayMock<'a, T> {}
unsafe impl<'a, T: GCPluginInstance<'a>> Sync for GCGatewayMock<'a, T> {}