mqi 0.3.0

Idiomatic IBM® MQ Interface (MQI) and MQ Administration Interface (MQAI) APIs
Documentation
use libmqm_sys as mq;

use super::option;
use crate::{
    MqStr, Object, Properties, connection::AsConnection, constants, macros::all_multi_tuples, prelude::*, result::ResultComp,
    structs, types,
};

structs::impl_min_version!(['a], structs::MQPMO<'a>);

#[derive(Debug, Clone, Copy)]
pub struct Context<T>(pub T);

macro_rules! impl_putoption_tuple {
    ([$($rest:ident),*]) => {
        #[expect(non_snake_case)]
        #[diagnostic::do_not_recommend]
        unsafe impl <'po, $($rest),*> option::PutOption<'po> for ($($rest),*)
        where
            $($rest: option::PutOption<'po> ),*
        {
            #[inline]
            fn apply_param(&self, param: &mut option::PutParam<'po>) {
                let $crate::macros::reverse_ident!($($rest),*) = self;
                $($rest.apply_param(param);)*
            }
        }
    };
}

unsafe impl option::PutOption<'_> for () {
    fn apply_param(&self, _: &mut option::PutParam<'_>) {}
}

all_multi_tuples!(impl_putoption_tuple);

#[derive(Debug)]
pub enum PropertyAction<'handle, C: AsConnection, C2: AsConnection> {
    Reply(&'handle Properties<C>, &'handle mut Properties<C2>),
    Forward(&'handle Properties<C>, &'handle mut Properties<C2>),
    Report(&'handle Properties<C>, &'handle mut Properties<C2>),
}

unsafe impl<'po, C: AsConnection> option::PutOption<'po> for Context<&Object<C>> {
    fn apply_param(&self, (.., pmo): &mut option::PutParam<'po>) {
        pmo.Context = self.0.handle.raw_handle();
    }
}

unsafe impl<'po, C: AsConnection> option::PutOption<'po> for &mut Properties<C> {
    fn apply_param(&self, (.., pmo): &mut option::PutParam<'po>) {
        pmo.set_min_version(mq::MQPMO_VERSION_3);
        *pmo.Action.as_mut() = constants::MQACTP_NEW;
        pmo.OriginalMsgHandle = self.handle().raw_handle();
    }
}

macro_rules! impl_putoption_deref {
    ($field:tt, $ty:ty) => {
        unsafe impl option::PutOption<'_> for $ty {
            fn apply_param(&self, (mqmd, _): &mut option::PutParam<'_>) {
                let mut_field: &mut Self = mqmd.$field.as_mut();
                *mut_field = *self;
            }
        }
    };
}

macro_rules! impl_putoption_mqchar {
    ($field:tt, $ty:ty) => {
        unsafe impl option::PutOption<'_> for $ty {
            fn apply_param(&self, (mqmd, _): &mut option::PutParam<'_>) {
                mqmd.$field = *self.as_mqchar();
            }
        }
    };
}

unsafe impl option::PutOption<'_> for types::MQPMO {
    fn apply_param(&self, (.., pmo): &mut option::PutParam<'_>) {
        let pmo_options: &mut Self = pmo.Options.as_mut();
        pmo_options.insert(*self);
    }
}

unsafe impl option::PutOption<'_> for structs::MQMD {
    fn apply_param(&self, param: &mut option::PutParam<'_>) {
        self.clone_into(&mut param.0);
    }
}

unsafe impl option::PutOption<'_> for types::MQRO {
    fn apply_param(&self, (mqmd, _): &mut option::PutParam<'_>) {
        let mut_report: &mut Self = mqmd.Report.as_mut();
        mut_report.insert(*self);
    }
}

impl_putoption_deref!(MsgType, types::MQMT);
impl_putoption_deref!(Expiry, types::MQEI);
impl_putoption_deref!(Feedback, types::MQFB);
impl_putoption_deref!(Priority, types::MQPRI);
impl_putoption_deref!(Persistence, types::MQPER);
impl_putoption_deref!(PutApplType, types::MQAT);
impl_putoption_deref!(MsgFlags, types::MQMF);

unsafe impl option::PutOption<'_> for types::MessageId {
    fn apply_param(&self, (mqmd, _): &mut option::PutParam<'_>) {
        mqmd.MsgId = self.0;
    }
}

unsafe impl option::PutOption<'_> for types::CorrelationId {
    fn apply_param(&self, (mqmd, _): &mut option::PutParam<'_>) {
        mqmd.CorrelId = self.0;
    }
}

unsafe impl option::PutOption<'_> for types::GroupId {
    fn apply_param(&self, (mqmd, _): &mut option::PutParam<'_>) {
        mqmd.GroupId = self.0;
    }
}

unsafe impl option::PutOption<'_> for types::AccountingToken {
    fn apply_param(&self, (mqmd, _): &mut option::PutParam<'_>) {
        mqmd.AccountingToken = self.0;
    }
}

impl_putoption_mqchar!(PutApplName, types::ApplName);
impl_putoption_mqchar!(ReplyToQ, types::ReplyToQueueName);
impl_putoption_mqchar!(ReplyToQMgr, types::ReplyToQueueManagerName);
impl_putoption_mqchar!(PutDate, types::PutDate);
impl_putoption_mqchar!(PutTime, types::PutTime);
impl_putoption_mqchar!(UserIdentifier, types::UserIdentifier);
impl_putoption_mqchar!(ApplIdentityData, types::ApplIdentityData);
impl_putoption_mqchar!(ApplOriginData, types::ApplOriginData);

unsafe impl<'po, C: AsConnection, C2: AsConnection> option::PutOption<'po> for PropertyAction<'po, C, C2> {
    fn apply_param(&self, (.., pmo): &mut option::PutParam<'po>) {
        let (action, original, new) = match self {
            PropertyAction::Reply(original, new) => (constants::MQACTP_REPLY, original, new),
            PropertyAction::Forward(original, new) => (constants::MQACTP_FORWARD, original, new),
            PropertyAction::Report(original, new) => (constants::MQACTP_REPORT, original, new),
        };
        pmo.set_min_version(mq::MQPMO_VERSION_3);
        *pmo.Action.as_mut() = action;
        pmo.OriginalMsgHandle = original.handle().raw_handle();
        pmo.NewMsgHandle = new.handle().raw_handle();
    }
}

unsafe impl option::PutAttr for structs::MQMD {
    #[inline]
    fn put_extract<'b, F>(param: &mut option::PutParam<'b>, put: F) -> ResultComp<Self>
    where
        F: FnOnce(&mut option::PutParam<'b>) -> ResultComp<()>,
    {
        put(param).map_completion(|()| {
            let (md, ..) = param;
            md.clone()
        })
    }
}

macro_rules! impl_putattr_mqmd_mqstr {
    ($field:tt, $ty:ty) => {
        unsafe impl option::PutAttr for $ty {
            #[inline]
            fn put_extract<'b, F>(param: &mut option::PutParam<'b>, put: F) -> ResultComp<Self>
            where
                F: FnOnce(&mut option::PutParam<'b>) -> ResultComp<()>,
            {
                put(param).map_completion(|()| {
                    let (md, ..) = param;
                    MqStr::from(md.$field).into()
                })
            }
        }
    };
}

macro_rules! impl_putattr_mqmd {
    ($field:tt, $ty:ty) => {
        unsafe impl option::PutAttr for $ty {
            #[inline]
            fn put_extract<'b, F>(param: &mut option::PutParam<'b>, put: F) -> ResultComp<Self>
            where
                F: FnOnce(&mut option::PutParam<'b>) -> ResultComp<()>,
            {
                put(param).map_completion(|()| {
                    let (md, ..) = param;
                    md.$field.into()
                })
            }
        }
    };
}

impl_putattr_mqmd_mqstr!(PutDate, types::PutDate);
impl_putattr_mqmd_mqstr!(PutTime, types::PutTime);
impl_putattr_mqmd!(MsgId, types::MessageId);
impl_putattr_mqmd_mqstr!(UserIdentifier, types::UserIdentifier);
impl_putattr_mqmd!(AccountingToken, types::AccountingToken);
impl_putattr_mqmd_mqstr!(ApplIdentityData, types::ApplIdentityData);
impl_putattr_mqmd!(CorrelId, types::CorrelationId);
impl_putattr_mqmd!(GroupId, types::GroupId);
impl_putattr_mqmd_mqstr!(ApplOriginData, types::ApplOriginData);

#[expect(unused_parens)]
mod impl_put {
    use super::option;
    use crate::{macros::all_multi_tuples, prelude::*, result::ResultComp};

    macro_rules! impl_putattr_tuple {
        ([$first:ident, $($ty:ident),*]) => {
            #[diagnostic::do_not_recommend]
            unsafe impl<$first, $($ty),*> option::PutAttr for ($first, $($ty),*)
            where
                $first: option::PutAttr,
                $($ty: option::PutAttr),*
            {
                #[expect(non_snake_case)]
                #[inline]
                fn put_extract<'p, F>(param: &mut option::PutParam<'p>, mqi: F) -> ResultComp<Self>
                where
                    F: FnOnce(&mut option::PutParam<'p>) -> ResultComp<()>
                {
                    let mut rest_outer = None;
                    $first::put_extract(param, |param| {
                        <($($ty),*) as option::PutAttr>::put_extract(param, mqi).map_completion(|rest| {
                            rest_outer = Some(rest);
                        })
                    })
                    .map_completion(|a| {
                        let ($($ty),*) = rest_outer.expect("rest_outer should be set by extract closure");
                        (a, $($ty),*)
                    })
                }
            }
        }
    }

    unsafe impl option::PutAttr for () {
        #[inline]
        fn put_extract<'p, F>(param: &mut option::PutParam<'p>, mqi: F) -> ResultComp<Self>
        where
            F: FnOnce(&mut option::PutParam<'p>) -> ResultComp<()>,
            Self: Sized,
        {
            mqi(param)
        }
    }

    all_multi_tuples!(impl_putattr_tuple);
}

#[cfg(test)]
#[cfg(feature = "mock")]
#[cfg_attr(coverage_nightly, coverage(off))]
mod test {
    use std::error::Error;

    use libmqm_default as default;

    use super::*;
    use crate::{Properties, put::PutOption as _, test::mock, types::MQCMHO};

    #[test]
    fn property_action() -> Result<(), Box<dyn Error>> {
        let qm = mock::connect_ok(|mock_library| {
            let mut seq = mockall::Sequence::new();
            mock::properties_ok(mock_library, 0xf0f0, 1, &mut seq);
            mock::properties_ok(mock_library, 0x0e0e, 1, &mut seq);
        });

        let mut put_param = (
            structs::MQMD::new(default::MQMD_DEFAULT),
            structs::MQPMO::new(default::MQPMO_DEFAULT),
        );

        let source = Properties::new(&qm, MQCMHO::default())?;
        let mut outcome = Properties::new(&qm, MQCMHO::default())?;
        let action = PropertyAction::Reply(&source, &mut outcome);
        action.apply_param(&mut put_param);

        dbg!(put_param);

        Ok(())
    }
}