mqi/common/
result.rs

1use crate::core::values;
2use crate::sys;
3use crate::HasMqNames;
4use std::fmt::{Debug, Display};
5
6/// MQ API reason code (`MQRC_*`)
7pub type ReasonCode = values::MQRC;
8
9/// MQ API completion code (`MQCC_*`)
10pub type CompletionCode = values::MQCC;
11
12impl Default for ReasonCode {
13    fn default() -> Self {
14        Self(sys::MQRC_NONE)
15    }
16}
17
18impl Default for CompletionCode {
19    fn default() -> Self {
20        Self(sys::MQCC_UNKNOWN)
21    }
22}
23
24impl ReasonCode {
25    #[must_use]
26    pub fn ibm_reference_url(&self, language: &str, version: Option<&str>) -> Option<String> {
27        let name = self.mq_primary_name()?.to_lowercase().replace('_', "-");
28        let version = version.unwrap_or("latest");
29        let code = self.value();
30        Some(format!(
31            "https://www.ibm.com/docs/{language}/ibm-mq/{version}?topic=codes-{code}-{code:04x}-rc{code}-{name}"
32        ))
33    }
34}
35/// A value returned from an MQ API call, optionally with a warning `ReasonCode`
36#[derive(Debug, Clone, derive_more::Deref, derive_more::DerefMut, derive_more::AsRef, derive_more::AsMut)]
37#[must_use]
38pub struct Completion<T>(
39    #[deref]
40    #[deref_mut]
41    #[as_ref]
42    #[as_mut]
43    pub T,
44    pub Option<(ReasonCode, &'static str)>,
45);
46
47impl<T> Completion<T> {
48    pub const fn new(value: T) -> Self {
49        Self(value, None)
50    }
51
52    pub const fn new_warning(value: T, warning: (ReasonCode, &'static str)) -> Self {
53        Self(value, Some(warning))
54    }
55}
56
57impl<T: std::process::Termination> std::process::Termination for Completion<T> {
58    fn report(self) -> std::process::ExitCode {
59        self.discard_warning().report()
60    }
61}
62
63impl<T> Completion<T> {
64    pub fn map<U, F: FnOnce(T) -> U>(self, op: F) -> Completion<U> {
65        let Self(value, warning) = self;
66        Completion(op(value), warning)
67    }
68
69    /// Discards the `MQCC_WARNING` in the Completion
70    pub fn discard_warning(self) -> T {
71        let Self(value, ..) = self;
72        value
73    }
74
75    /// Returns the reason code associated with the warning. Returns `None` when no warning is issued.
76    #[must_use]
77    pub const fn warning(&self) -> Option<(ReasonCode, &'static str)> {
78        let Self(_, warning) = self;
79        *warning
80    }
81}
82
83impl<I: Iterator> Iterator for Completion<I> {
84    type Item = I::Item;
85
86    fn next(&mut self) -> Option<Self::Item> {
87        let iter = &mut **self;
88        iter.next()
89    }
90
91    fn size_hint(&self) -> (usize, Option<usize>) {
92        let iter = &**self;
93        iter.size_hint()
94    }
95}
96
97impl<T: Display> Display for Completion<T> {
98    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99        match self {
100            Self(value, Some((warning, verb))) => write!(f, "MQCC_WARNING: {verb} {warning} {value}"),
101            Self(value, None) => write!(f, "MQCC_OK: {value}"),
102        }
103    }
104}
105
106/// MQ failure with `CompCode` != `MQCC_OK`. Has the associated verb and `ReasonCode`.
107#[derive(Debug, derive_more::Error, derive_more::Display)]
108#[display("{_0}: {_1} - {_2}")]
109pub struct Error(pub CompletionCode, pub &'static str, pub ReasonCode);
110
111/// Result of an MQI API call wrapped in a `Completion` for warnings
112pub type ResultCompErr<T, E> = Result<Completion<T>, E>;
113/// Result of an MQI API call wrapped in a `Completion` for warnings and with an MQ `Error` for errors
114pub type ResultComp<T> = Result<Completion<T>, Error>;
115/// Result of an MQI API call with an MQ `Error`
116pub type ResultErr<T> = Result<T, Error>;
117
118/// Extends a `ResultComp` with additional methods to handle warnings.
119pub trait ResultCompExt<T, E> {
120    /// Converts the MQ warning in the `Ok(Completion(..))` into an `Err`.
121    fn warn_as_error(self) -> Result<T, E>;
122}
123
124pub trait WithMQError {
125    fn mqi_error(&self) -> Option<&Error>;
126}
127
128impl WithMQError for Error {
129    fn mqi_error(&self) -> Option<&Error> {
130        Some(self)
131    }
132}
133
134/// Extends a `ResultCompErr` with additional methods to handle warnings.
135pub trait ResultCompErrExt<T, E> {
136    /// Maps the the value of the MQI API Result, maintaining the `Completion` wrapper with any associated warning.
137    fn map_completion<U, F: FnOnce(T) -> U>(self, op: F) -> ResultCompErr<U, E>;
138
139    /// Discards the completion
140    fn discard_warning(self) -> Result<T, E>;
141
142    /// Returns the contained `Ok(Completion(..))` value, discarding any warning and consumes the `self` value.
143    ///
144    /// This function can panic, so use it with caution.
145    ///
146    /// # Panic
147    /// Panics if the value is an `Err`, with a panic message provided by the `Err`'s value.
148    fn unwrap_completion(self) -> T;
149}
150
151impl<T, E> ResultCompErrExt<T, E> for ResultCompErr<T, E>
152where
153    E: std::fmt::Debug, // for unwrap_completion
154{
155    fn map_completion<U, F: FnOnce(T) -> U>(self, op: F) -> ResultCompErr<U, E> {
156        self.map(|mq| mq.map(op))
157    }
158
159    #[expect(clippy::unwrap_used)]
160    fn unwrap_completion(self) -> T {
161        self.unwrap().discard_warning()
162    }
163
164    fn discard_warning(self) -> Result<T, E> {
165        self.map(Completion::discard_warning)
166    }
167}
168
169impl<T, E: From<Error>> ResultCompExt<T, E> for ResultCompErr<T, E> {
170    fn warn_as_error(self) -> Result<T, E> {
171        match self {
172            Ok(Completion(_, Some((warn_cc, verb)))) => {
173                Err(E::from(Error(CompletionCode::from(sys::MQCC_WARNING), verb, warn_cc)))
174            }
175            other => other.map(|Completion(value, ..)| value),
176        }
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use crate::sys;
183    use crate::ReasonCode;
184
185    #[test]
186    fn mqmd_new() {
187        let d = sys::MQMD2::default();
188        assert_eq!(d.Version, 2);
189    }
190
191    #[test]
192    fn reason_code_display() {
193        assert_eq!(ReasonCode::from(sys::MQRC_Q_MGR_ACTIVE).to_string(), "MQRC_Q_MGR_ACTIVE");
194        assert_eq!(ReasonCode::from(sys::MQRC_NONE).to_string(), "MQRC_NONE");
195        assert_eq!(ReasonCode::from(-1).to_string(), "-1");
196    }
197
198    #[test]
199    fn ibm_reference_url() {
200        assert_eq!(
201            ReasonCode::from(sys::MQRC_Q_ALREADY_EXISTS).ibm_reference_url("en", None),
202            Some("https://www.ibm.com/docs/en/ibm-mq/latest?topic=codes-2290-08f2-rc2290-mqrc-q-already-exists".to_owned())
203        );
204    }
205}