mqi 0.3.0

Idiomatic IBM® MQ Interface (MQI) and MQ Administration Interface (MQAI) APIs
Documentation
use std::{
    ffi::c_void,
    ptr,
    sync::{Arc, Mutex},
};

use libmqm_sys::{self as mq, mock::MockMq};

use crate::{constants, types};

const MQCBC_DEFAULT: libmqm_sys::MQCBC = mq::MQCBC {
    StrucId: [67, 66, 67, 32],
    Version: mq::MQCBC_VERSION_2,
    CallType: 0,
    Hobj: mq::MQHO_NONE,
    CallbackArea: ptr::null_mut(),
    ConnectionArea: ptr::null_mut(),
    CompCode: mq::MQCC_OK,
    Reason: mq::MQRC_NONE,
    State: mq::MQCS_NONE,
    DataLength: 0,
    BufferLength: 0,
    Flags: mq::MQCBCF_NONE,
    ReconnectDelay: mq::MQRD_NO_DELAY,
};

pub fn event_cb(mock_library: &mut MockMq) {
    type MqCbFn = unsafe extern "C" fn(_: mq::MQHCONN, _: mq::PMQVOID, _: mq::PMQVOID, _: mq::PMQVOID, _: *const mq::MQCBC);
    #[derive(Clone)]
    struct MqCallback(*mut c_void, *mut c_void);
    unsafe impl Send for MqCallback {}

    let cb: Arc<Mutex<Option<MqCallback>>> = Arc::default();
    let cb_init = cb.clone();
    mock_library
        .expect_MQCB()
        .withf(|_, op, cbd, _, _, _, _, _| {
            let cbd = cbd.expect("MQCBD should be non-null");
            let op = types::MQOP(*op);
            let callback_type = types::MQCBT(cbd.CallbackType);
            callback_type == constants::MQCBT_EVENT_HANDLER
                && (op.contains(constants::MQOP_REGISTER) || op.contains(constants::MQOP_DEREGISTER))
        })
        .returning(move |hconn, op, cbd, _, _, _, cc, rc| {
            let cbd = cbd.expect("MQCBD should be non-null");
            let op = types::MQOP(op);
            let mut cb_init_lock = cb_init.lock().expect("mutex retrieval should succeed");
            let existing = match op {
                constants::MQOP_REGISTER => {
                    let cb_new = Some(MqCallback(cbd.CallbackArea, cbd.CallbackFunction));
                    cb_init_lock.clone_from(&cb_new);
                    cb_new
                }
                constants::MQOP_DEREGISTER => cb_init_lock.take(),
                _ => None,
            };
            drop(cb_init_lock);
            if let Some(MqCallback(area, function)) = existing {
                let mut cb_context = libmqm_sys::MQCBC {
                    CallType: match op {
                        constants::MQOP_REGISTER => libmqm_sys::MQCBCT_REGISTER_CALL,
                        constants::MQOP_DEREGISTER => libmqm_sys::MQCBCT_DEREGISTER_CALL,
                        _ => unreachable!(),
                    },
                    CallbackArea: area,
                    ..MQCBC_DEFAULT
                };
                unsafe {
                    let fn_cb = std::mem::transmute::<*const c_void, MqCbFn>(function);
                    fn_cb(hconn, ptr::null_mut(), ptr::null_mut(), ptr::null_mut(), &raw mut cb_context);
                }
            }

            super::mqi_outcome_ok(cc, rc);
        });
    mock_library.expect_MQDISC().returning(move |hconn, cc, rc| {
        let callback = cb.lock().expect("mutex retrieval should succeed").take();
        if let Some(MqCallback(area, function)) = callback {
            let mut cbc = libmqm_sys::MQCBC {
                CallbackArea: area,
                CallType: libmqm_sys::MQCBCT_DEREGISTER_CALL,
                ..MQCBC_DEFAULT
            };
            unsafe {
                let fn_cb = std::mem::transmute::<*const c_void, MqCbFn>(function);
                fn_cb(*hconn, ptr::null_mut(), ptr::null_mut(), ptr::null_mut(), &raw mut cbc);
            }
        }
        super::mqi_outcome_ok(cc, rc);
    });
}