dpdu-wrapper 0.2.0

High-level Rust wrapper for D-PDU implementations.
use crate::api::{ApiResult, PduApi};
use crate::types::pdu_com_param::table::PduComParamTable;
use crate::types::pdu_com_param::{CpVariant, PduComParam};
use crate::types::{PduCllHandle, PduModuleHandle};
use crate::vendor_specific::wrap_pdu_call;
use dpdu_api_types::{
    ParamByteFieldData, ParamLongFieldData, ParamStructAccessTiming, ParamStructFieldData,
    ParamStructSessionTiming, PduCpst, PduError, PduIt, PduPt,
};
use std::ptr::NonNull;
use std::{ptr, slice};
use tracing::{error, trace};

impl PduApi {
    pub fn pdu_get_unique_resp_id_table(
        &self,
        h_mod: PduModuleHandle,
        h_cll: PduCllHandle,
    ) -> ApiResult<PduComParamTable> {
        impl_defer_clear_suppress_options!(self, get_unique_resp_id_table);

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

        let mut table_item_ptr = ptr::null_mut();

        trace!(
            func = FUNC,
            h_mod,
            h_cll,
            item_ptr = format!("{:#x}", &table_item_ptr as *const _ as usize),
            "D-PDU API Call Return"
        );

        let get_timestamp_fn = self.symbols.get_unique_resp_id_table;
        let result = wrap_pdu_call(FUNC, || get_timestamp_fn(h_mod, h_cll, &mut table_item_ptr));

        if !result.is_success() {
            self.log_api_call_fail(
                FUNC,
                result,
                None,
                resolve_level_of_log_api_call_fail!(self, result, get_unique_resp_id_table),
            );
            return Err(result)?;
        }

        trace!(
            func = FUNC,
            item_ptr = format!("0x{:x}", table_item_ptr as usize),
            item_type = ?NonNull::new(table_item_ptr).map(|wptr| unsafe { (&*wptr.as_ptr()).item_type }),
            "D-PDU API Call Return"
        );

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

        let table_item = unsafe { &*table_item_ptr };

        if !matches!(table_item.item_type, PduIt::RscConflict) {
            error!(
                func = FUNC,
                "Invalid item type received: PduIt::{}. Emulation of PduError::FctFailed...",
                table_item.item_type.as_str(),
            );

            self.pdu_destroy_item(table_item_ptr as _)?;
            return Err(PduError::FctFailed)?;
        }

        let table_ptr = table_item.p_unique_data;
        let table_len = table_item.num_entries as usize;

        let table = if table_ptr.is_null() || table_len == 0 {
            &[]
        } else {
            unsafe { slice::from_raw_parts(table_ptr, table_len) }
        };

        let mut map = PduComParamTable::with_capacity(table.len());

        trace!(
            func = FUNC,
            table_len = table.len(),
            "D-PDU API Call Return"
        );

        for row in table {
            let unique_id = row.unique_resp_identifier;

            let com_params_ptr = row.p_params;
            let com_params_len = row.num_param_items as usize;

            let com_params = if com_params_ptr.is_null() || com_params_len == 0 {
                &[]
            } else {
                unsafe { slice::from_raw_parts(com_params_ptr, com_params_len) }
            };

            trace!(
                func = FUNC,
                table_item_unique_id = unique_id,
                table_item_cp_len = com_params.len(),
                "D-PDU API Call Return"
            );

            for cp in com_params {
                if !matches!(cp.item_type, PduIt::Param) {
                    error!(
                        func = FUNC,
                        "Invalid ComParam type received: PduIt::{}. Emulation of PduError::FctFailed...",
                        cp.item_type.as_str(),
                    );

                    self.pdu_destroy_item(table_item_ptr as _)?;
                    return Err(PduError::FctFailed)?;
                }

                trace!(
                    func = FUNC,
                    table_item_cp_id = cp.com_param_id,
                    table_item_cp_type = cp.com_param_data_type.as_str(),
                    table_item_cp_class = cp.com_param_class.as_str(),
                    "D-PDU API Call Return"
                );

                let variant: CpVariant = unsafe {
                    use ptr::read;

                    match cp.com_param_data_type {
                        PduPt::Unum8 => read::<u8>(cp.p_com_param_data as _).into(),
                        PduPt::Snum8 => read::<i8>(cp.p_com_param_data as _).into(),
                        PduPt::Unum16 => read::<u16>(cp.p_com_param_data as _).into(),
                        PduPt::Snum16 => read::<i16>(cp.p_com_param_data as _).into(),
                        PduPt::Unum32 => read::<u32>(cp.p_com_param_data as _).into(),
                        PduPt::Snum32 => read::<i32>(cp.p_com_param_data as _).into(),
                        PduPt::ByteField => {
                            let data = read::<ParamByteFieldData>(cp.p_com_param_data as _);
                            let ptr = data.p_data_array;
                            let len = data.param_act_len as usize;
                            let bytes = if ptr.is_null() || len == 0 {
                                &[]
                            } else {
                                slice::from_raw_parts(ptr, len)
                            };
                            (bytes.to_vec(), data.param_max_len as usize).into()
                        }
                        PduPt::LongField => {
                            let data = read::<ParamLongFieldData>(cp.p_com_param_data as _);
                            let ptr = data.p_data_array;
                            let len = data.param_act_len as usize;
                            let nums = if ptr.is_null() || len == 0 {
                                &[]
                            } else {
                                slice::from_raw_parts(ptr, len)
                            };
                            (nums.to_vec(), data.param_max_len as usize).into()
                        }
                        PduPt::StructField => {
                            let data = read::<ParamStructFieldData>(cp.p_com_param_data as _);
                            let ptr = data.p_struct_array;
                            let len = data.param_act_entries as usize;
                            match data.com_param_struct_type {
                                PduCpst::AccessTiming => {
                                    let structs: &[ParamStructAccessTiming] =
                                        if ptr.is_null() || len == 0 {
                                            &[]
                                        } else {
                                            slice::from_raw_parts(ptr as _, len)
                                        };
                                    (structs.to_vec(), data.param_max_entries as usize).into()
                                }
                                PduCpst::SessionTiming => {
                                    let structs: &[ParamStructSessionTiming] =
                                        if ptr.is_null() || len == 0 {
                                            &[]
                                        } else {
                                            slice::from_raw_parts(ptr as _, len)
                                        };
                                    (structs.to_vec(), data.param_max_entries as usize).into()
                                }
                            }
                        }
                    }
                };

                let com_param = PduComParam::from_id(cp.com_param_id, cp.com_param_class, variant);

                com_param.try_init_short_name(self);

                map.add(unique_id, com_param);
            }
        }

        self.pdu_destroy_item(table_item_ptr as _)?;

        Ok(map)
    }
}