use std::fmt::{Debug, Display};
use crate::{
Connection, Library,
connection::ConnectionEither,
constants,
types::{MQCC, MQRC},
};
#[derive(Debug, Clone, derive_more::Deref, derive_more::DerefMut, derive_more::AsRef, derive_more::AsMut)]
#[must_use]
pub struct Completion<T>(
#[deref]
#[deref_mut]
#[as_ref]
#[as_mut]
pub T,
pub Option<(MQRC, &'static str)>,
);
impl<T> Completion<T> {
pub const fn new(value: T) -> Self {
Self(value, None)
}
pub const fn new_warning(value: T, warning: (MQRC, &'static str)) -> Self {
Self(value, Some(warning))
}
}
impl<T: std::process::Termination> std::process::Termination for Completion<T> {
fn report(self) -> std::process::ExitCode {
self.discard_warning().report()
}
}
impl<T> Completion<T> {
pub fn map<U, F: FnOnce(T) -> U>(self, op: F) -> Completion<U> {
let Self(value, warning) = self;
Completion(op(value), warning)
}
pub fn discard_warning(self) -> T {
let Self(value, ..) = self;
value
}
#[must_use]
pub const fn warning(&self) -> Option<(MQRC, &'static str)> {
let Self(_, warning) = self;
*warning
}
}
impl<I: Iterator> Iterator for Completion<I> {
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
let iter = &mut **self;
iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
let iter = &**self;
iter.size_hint()
}
}
impl<T: Display> Display for Completion<T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self(value, Some((warning, verb))) => write!(f, "MQCC_WARNING: {verb} {warning} {value}"),
Self(value, None) => write!(f, "MQCC_OK: {value}"),
}
}
}
#[derive(Debug, derive_more::Error, derive_more::Display)]
#[display("{_0}: {_1} - {_2}")]
pub struct Error(pub MQCC, pub &'static str, pub MQRC);
pub type ResultCompErr<T, E> = Result<Completion<T>, E>;
pub type ResultComp<T> = Result<Completion<T>, Error>;
pub type ResultErr<T> = Result<T, Error>;
pub trait ResultCompExt<T, E> {
fn warn_as_error(self) -> Result<T, E>;
}
pub trait WithMqError {
fn mqi_error(&self) -> Option<&Error>;
}
impl WithMqError for Error {
fn mqi_error(&self) -> Option<&Error> {
Some(self)
}
}
pub trait ResultCompErrExt<T, E> {
fn map_completion<U, F: FnOnce(T) -> U>(self, op: F) -> ResultCompErr<U, E>;
fn discard_warning(self) -> Result<T, E>;
fn unwrap_completion(self) -> T
where
E: std::fmt::Debug;
fn already_connected_ref<'a, L: Library<MQ: libmqm_sys::Mqi>, H>(self) -> ResultCompErr<ConnectionEither<'a, L, H>, E>
where
T: Into<Connection<L, H>>;
}
impl<T, E> ResultCompErrExt<T, E> for ResultCompErr<T, E> {
fn map_completion<U, F: FnOnce(T) -> U>(self, op: F) -> ResultCompErr<U, E> {
self.map(|mq| mq.map(op))
}
#[expect(clippy::unwrap_used)]
fn unwrap_completion(self) -> T
where
E: std::fmt::Debug,
{
self.unwrap().discard_warning()
}
fn discard_warning(self) -> Result<T, E> {
self.map(Completion::discard_warning)
}
fn already_connected_ref<'a, L: Library<MQ: libmqm_sys::Mqi>, H>(self) -> ResultCompErr<ConnectionEither<'a, L, H>, E>
where
T: Into<Connection<L, H>>,
{
self.map(|comp| match comp {
Completion(connection, warn @ Some((constants::MQRC_ALREADY_CONNECTED, _))) => {
Completion(ConnectionEither::Ref(connection.into().leak()), warn)
}
other => other.map(|c| ConnectionEither::Owned(c.into())),
})
}
}
impl<T, E: From<Error>> ResultCompExt<T, E> for ResultCompErr<T, E> {
fn warn_as_error(self) -> Result<T, E> {
match self {
Ok(Completion(_, Some((warn_cc, verb)))) => Err(E::from(Error(constants::MQCC_WARNING, verb, warn_cc))),
other => other.map(|Completion(value, ..)| value),
}
}
}
#[cfg(test)]
mod test {
#[cfg(feature = "mock")]
#[test]
fn leak_already_connected() {
use super::*;
use crate::test::mock;
let connection = mock::connect_ok(|_| {});
let subject_warn: ResultComp<_> = Ok(Completion(connection, Some((constants::MQRC_ALREADY_CONNECTED, "verb"))));
let result = subject_warn.already_connected_ref();
assert!(matches!(result, Ok(Completion(ConnectionEither::Ref(_), _))));
let connection = mock::connect_ok(|_| {});
let subject: ResultComp<_> = Ok(Completion(connection, None));
let result = subject.already_connected_ref();
assert!(matches!(result, Ok(Completion(ConnectionEither::Owned(_), _))));
}
}