gc_plugin_abi 0.5.0

Gridcore Plugin API
Documentation
use gc_json_bridge::GCJsonBridge;
use gc_plugin_abi::*;
use serde_json::Value;
use std::sync::atomic::AtomicBool;

static INIT_CALLED: AtomicBool = AtomicBool::new(false);

///// CORE CODE //////
unsafe extern "C" fn publish_datapoint_value(ctx: raw::GCCoreCtx, _dp_value: raw::GCDatapointValue) -> bool {
    assert_eq!(
        ctx,
        std::ptr::null_mut(),
        "The ctx should be null, as it was set in the function get_interface"
    );
    true
}

unsafe extern "C" fn system_log(ctx: raw::GCCoreCtx, _log_level: raw::GCLogLevel, _log: *const ::std::os::raw::c_char) {
    assert_eq!(
        ctx,
        std::ptr::null_mut(),
        "The ctx should be null, as it was set in the function get_interface"
    );
}

unsafe extern "C" fn audit_log(ctx: raw::GCCoreCtx, _log: *const ::std::os::raw::c_char) {
    assert_eq!(
        ctx,
        std::ptr::null_mut(),
        "The ctx should be null, as it was set in the function get_interface"
    );
}

unsafe extern "C" fn release_datapoint_value(_: raw::GCDatapointValue) -> bool {
    true
}

unsafe extern "C" fn get_last_datapoint_value(ctx: raw::GCCoreCtx, _datapoint_id: raw::GCDatapointIdentifier) -> raw::GCDatapointValue {
    assert_eq!(
        ctx,
        std::ptr::null_mut(),
        "The ctx should be null, as it was set in the function get_interface"
    );
    std::ptr::null_mut()
}

pub fn get_config() -> Value {
    let data = r#"
    {
        "name": "John Doe",
        "age": 43,
        "phones": [
            "+44 1234567",
            "+44 2345678"
        ]
    }"#;
    serde_json::from_str(data).unwrap()
}

pub fn get_interface(bridge: &GCJsonBridge) -> raw::sGCPluginInterface {
    raw::sGCPluginInterface {
        logLevel: raw::eGCLogLevel_INFO,
        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: None,
        getSerialInterfaceCallback: None,
        storeDatapointValueCallback: None,

        config: raw::GCPluginConfig {
            pluginJsonConfig: bridge.gc_json_bridge_callbacks() as *const raw::sGCJsonCallbacks,
            ownDatapoints: std::ptr::null(),
            ownDatapointsCount: 0,
            subscribedDatapoints: std::ptr::null(),
            subscribedDatapointsCount: 0,
        },
        ctx: std::ptr::null_mut(),
    }
}

////// PLUGIN CODE //////
#[gc_plugin]
pub struct RestSomethingPlugin<'a> {
    _plugin_interface: &'a GCPluginInterface,
}
impl<'a> RestSomethingPlugin<'a> {
    pub fn new(plugin_interface: &'a GCPluginInterface) -> Self {
        let config: Value = plugin_interface.get_own_configuration().expect("Failed to get configuration");
        INIT_CALLED.store(true, std::sync::atomic::Ordering::SeqCst);

        assert_eq!(get_config(), config);

        Self {
            _plugin_interface: plugin_interface,
        }
    }
}

impl<'a> GCPluginInstance<'a> for RestSomethingPlugin<'a> {
    fn get_plugin_info() -> GCPluginInfo {
        GCPluginInfo::new("RestSomethingPlugin", "0.1.0", "0", 0)
    }

    fn receive_datapoint(&self, _data_value: GCBorrowedDatapointValue) -> bool {
        // Handle incoming datapoint
        true
    }

    fn init(plugin_interface: &'a GCPluginInterface) -> Box<Self> {
        Box::new(RestSomethingPlugin::new(plugin_interface))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use gc_utils::testing::gc_integration_test;

    #[gc_integration_test]
    fn test_config_loading() {
        let info = allocation_counter::measure(|| {
            let json_config = get_config();
            let json_bridge = GCJsonBridge::new(&json_config);
            let core_ifc = get_interface(&json_bridge);

            unsafe {
                let handle = gc_plugin_init(&core_ifc as raw::GCPluginInterface);
                gc_plugin_shutdown(handle);
            }
        });
        println!("{}", INIT_CALLED.load(std::sync::atomic::Ordering::SeqCst));
        assert!(INIT_CALLED.load(std::sync::atomic::Ordering::SeqCst), "The init function was not called");
        assert_eq!(info.bytes_current, 0, "Memory leak detected, ensure the shutdown function was called");
        assert_eq!(info.count_current, 0, "Memory leak detected, ensure the shutdown function was called");
    }
}