dpdu-wrapper 0.2.0

High-level Rust wrapper for D-PDU implementations.
use crate::api::{ApiResult, PduApi};
use crate::types::pdu_event::{
    PduErrorEvent, PduEvent, PduEventData, PduEventTarget, PduInfoEvent, PduResultEvent,
    PduStatusEvent,
};
use crate::vendor_specific::wrap_pdu_call;
use dpdu_api_types::{
    ErrorData, EventItem, InfoData, PDU_HANDLE_UNDEF, PduError, PduIt, PduStatus, ResultData,
};
use std::cell::OnceCell;
use std::num::NonZeroUsize;
use std::ptr::NonNull;
use std::{ptr, slice};
use tracing::{error, trace};

impl PduApi {
    pub fn pdu_get_event_item(&self, target: &PduEventTarget) -> ApiResult<Option<PduEvent>> {
        impl_defer_clear_suppress_options!(self, get_event_item);

        const FUNC: &'static str = "PDUGetEventItem";
        self.log_api_call(FUNC);

        let h_mod = target.get_module_handle().unwrap_or(PDU_HANDLE_UNDEF);
        let h_cll = target.get_cll_handle().unwrap_or(PDU_HANDLE_UNDEF);

        trace!(func = FUNC, h_mod, h_cll, "D-PDU API Call Args");

        let mut item_ptr: *mut EventItem = ptr::null_mut();

        let get_event_item_fn = self.symbols.get_event_item;
        let result = wrap_pdu_call(FUNC, || get_event_item_fn(h_mod, h_cll, &mut item_ptr));
        let debug_ret = || {
            trace!(
                func = FUNC,
                item_ptr = format!("0x{:x}", item_ptr as usize),
                item_type = ?NonNull::new(item_ptr).map(|wptr| unsafe { (&*wptr.as_ptr()).item_type }),
                "D-PDU API Call Return"
            );
        };

        match result {
            PduError::StatusNoError => {}
            PduError::EventQueueEmpty => {
                debug_ret();
                return Ok(None);
            }
            v => {
                self.log_api_call_fail(
                    FUNC,
                    result,
                    None,
                    resolve_level_of_log_api_call_fail!(self, result, get_event_item),
                );
                return Err(v)?;
            }
        }

        debug_ret();

        if item_ptr.is_null() {
            return Ok(None);
        }

        let item = unsafe { &*item_ptr };

        if item.p_data.is_null() {
            error!(
                func = FUNC,
                "Item data pointer is null. Emulation of PduError::FctFailed..."
            );
            return Err(PduError::FctFailed)?;
        }

        let data: PduEventData = match item.item_type {
            PduIt::Status => PduStatusEvent(unsafe { *(item.p_data as *const PduStatus) }).into(),
            PduIt::Result => {
                let data = unsafe { &*(item.p_data as *const ResultData) };

                let mut extra_header = OnceCell::new();
                let mut extra_footer = OnceCell::new();

                if !data.p_extra_info.is_null() {
                    let extra_info = unsafe { &*data.p_extra_info };
                    if !extra_info.p_header_bytes.is_null() {
                        let ptr = extra_info.p_header_bytes;
                        let len = extra_info.num_header_bytes;
                        if !ptr.is_null() && len > 0 {
                            extra_header
                                .set(unsafe { slice::from_raw_parts(ptr, len as _) }.to_vec())
                                .unwrap();
                        }
                    }
                    if !extra_info.p_footer_bytes.is_null() {
                        let ptr = extra_info.p_footer_bytes;
                        let len = extra_info.num_footer_bytes;
                        if !ptr.is_null() && len > 0 {
                            extra_footer
                                .set(unsafe { slice::from_raw_parts(ptr, len as _) }.to_vec())
                                .unwrap();
                        }
                    }
                }

                PduResultEvent {
                    rx_flags: unsafe {
                        let ptr = data.rx_flag.p_flag_data;
                        let len = data.rx_flag.num_flag_bytes as usize;
                        let slice = if ptr.is_null() || len == 0 {
                            &[]
                        } else {
                            slice::from_raw_parts(ptr, len)
                        };
                        slice.to_vec().into()
                    },
                    unique_resp_identifier: data.unique_resp_identifier,
                    acceptance_id: data.acceptance_id,
                    timestamp_flags: unsafe {
                        let ptr = data.timestamp_flags.p_flag_data;
                        let len = data.timestamp_flags.num_flag_bytes as usize;
                        let slice = if ptr.is_null() || len == 0 {
                            &[]
                        } else {
                            slice::from_raw_parts(ptr, len)
                        };
                        slice.to_vec().into()
                    },
                    tx_msg_done_timestamp: data.tx_msg_done_timestamp,
                    start_msg_timestamp: data.start_msg_timestamp,
                    data: unsafe {
                        let ptr = data.p_data_bytes;
                        let len = data.num_data_bytes as usize;
                        let slice = if ptr.is_null() || len == 0 {
                            &[]
                        } else {
                            slice::from_raw_parts(ptr, len)
                        };
                        slice.to_vec().into()
                    },
                    extra_info_header: extra_header.take(),
                    extra_info_footer: extra_footer.take(),
                }
                .into()
            }
            PduIt::Error => {
                let data = unsafe { &*(item.p_data as *const ErrorData) };
                PduErrorEvent {
                    code: data.error_code_id,
                    extra_code: data.extra_error_info_id,
                }
                .into()
            }
            PduIt::Info => {
                let data = unsafe { &*(item.p_data as *const InfoData) };
                PduInfoEvent {
                    code: data.info_code,
                    extra_code: data.extra_info_data,
                }
                .into()
            }
            typ => {
                self.pdu_destroy_item(item_ptr as _)?;
                error!(
                    func = FUNC,
                    "Unexpected PduEventItemType = {}. Emulation of PduError::FctFailed...",
                    typ.as_str()
                );
                return Err(PduError::FctFailed)?;
            }
        };

        let h_cop = (item.h_cop != PDU_HANDLE_UNDEF).then(|| item.h_cop);
        let cop_tag = NonZeroUsize::new(item.p_cop_tag as _);
        let timestamp = item.timestamp;

        self.pdu_destroy_item(item_ptr as _)?;

        Ok(Some(PduEvent {
            target: target.clone(),
            h_cop,
            cop_tag,
            timestamp,
            data,
        }))
    }
}