Skip to main content

mqtt_topic_engine/
qos.rs

1//! Quality of Service (QoS) levels for MQTT
2//!
3//! Defines the three standard MQTT QoS levels independent of any specific
4//! MQTT client implementation.
5//!
6//! This module provides conversions to/from popular MQTT client libraries:
7//! - `rumqttc` - Enable with feature `rumqttc`
8//! - `paho-mqtt` - Enable with feature `paho-mqtt`
9//! - `ntex-mqtt` - Enable with feature `ntex-mqtt`
10//!
11//! These features only add **type conversions** between [`QoS`] and the client's
12//! own QoS type — they pull in the client crate purely for its types and do not
13//! drive any connection. How that client itself is built stays under your
14//! control: this crate depends on each with `default-features = false`, so it
15//! never forces a native toolchain on you. In particular `paho-mqtt` links a
16//! C library — its default `bundled` feature builds it from source (needs CMake)
17//! while otherwise it expects a system-installed Paho C library. Since you would
18//! only enable the `paho-mqtt` feature when you already use `paho-mqtt` as your
19//! client, Cargo's (additive) feature unification applies your own build choice
20//! there, and everything links as expected.
21
22use std::fmt;
23
24/// MQTT Quality of Service levels
25///
26/// Defines delivery guarantees for MQTT messages:
27/// - `AtMostOnce` (0): Best effort delivery, no guarantees
28/// - `AtLeastOnce` (1): Message delivered at least once, duplicates possible
29/// - `ExactlyOnce` (2): Message delivered exactly once, highest guarantee
30#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
31#[repr(u8)]
32pub enum QoS {
33	/// QoS 0: At most once delivery (fire and forget)
34	AtMostOnce = 0,
35	/// QoS 1: At least once delivery (acknowledged delivery)
36	AtLeastOnce = 1,
37	/// QoS 2: Exactly once delivery (assured delivery)
38	ExactlyOnce = 2,
39}
40
41impl QoS {
42	/// Convert to rumqttc QoS type
43	///
44	/// # Example
45	/// ```ignore
46	/// let qos = QoS::AtLeastOnce;
47	/// let rumqttc_qos = qos.to_rumqttc();
48	/// ```
49	#[cfg(feature = "rumqttc")]
50	pub fn to_rumqttc(self) -> rumqttc::QoS {
51		match self {
52			| QoS::AtMostOnce => rumqttc::QoS::AtMostOnce,
53			| QoS::AtLeastOnce => rumqttc::QoS::AtLeastOnce,
54			| QoS::ExactlyOnce => rumqttc::QoS::ExactlyOnce,
55		}
56	}
57
58	/// Convert to rumqttc MQTT v5 QoS type
59	///
60	/// Upstream `rumqttc` ships a distinct `QoS` enum under `v5::mqttbytes`, so
61	/// this cannot reuse [`to_rumqttc`](Self::to_rumqttc).
62	#[cfg(feature = "rumqttc")]
63	pub fn to_rumqttc_v5(self) -> rumqttc::v5::mqttbytes::QoS {
64		use rumqttc::v5::mqttbytes::QoS as V5;
65		match self {
66			| QoS::AtMostOnce => V5::AtMostOnce,
67			| QoS::AtLeastOnce => V5::AtLeastOnce,
68			| QoS::ExactlyOnce => V5::ExactlyOnce,
69		}
70	}
71
72	/// Convert to paho-mqtt QoS type
73	///
74	/// # Example
75	/// ```ignore
76	/// let qos = QoS::AtLeastOnce;
77	/// let paho_qos = qos.to_paho_mqtt();
78	/// ```
79	#[cfg(feature = "paho-mqtt")]
80	pub fn to_paho_mqtt(self) -> paho_mqtt::QoS {
81		match self {
82			| QoS::AtMostOnce => paho_mqtt::QoS::AtMostOnce,
83			| QoS::AtLeastOnce => paho_mqtt::QoS::AtLeastOnce,
84			| QoS::ExactlyOnce => paho_mqtt::QoS::ExactlyOnce,
85		}
86	}
87
88	/// Convert to ntex-mqtt QoS type
89	///
90	/// # Example
91	/// ```ignore
92	/// let qos = QoS::AtLeastOnce;
93	/// let ntex_qos = qos.to_ntex_mqtt();
94	/// ```
95	#[cfg(feature = "ntex-mqtt")]
96	pub fn to_ntex_mqtt(self) -> ntex_mqtt::QoS {
97		match self {
98			| QoS::AtMostOnce => ntex_mqtt::QoS::AtMostOnce,
99			| QoS::AtLeastOnce => ntex_mqtt::QoS::AtLeastOnce,
100			| QoS::ExactlyOnce => ntex_mqtt::QoS::ExactlyOnce,
101		}
102	}
103}
104
105#[cfg(feature = "rumqttc")]
106impl From<rumqttc::QoS> for QoS {
107	fn from(qos: rumqttc::QoS) -> Self {
108		match qos {
109			| rumqttc::QoS::AtMostOnce => QoS::AtMostOnce,
110			| rumqttc::QoS::AtLeastOnce => QoS::AtLeastOnce,
111			| rumqttc::QoS::ExactlyOnce => QoS::ExactlyOnce,
112		}
113	}
114}
115
116#[cfg(feature = "rumqttc")]
117impl From<rumqttc::v5::mqttbytes::QoS> for QoS {
118	fn from(qos: rumqttc::v5::mqttbytes::QoS) -> Self {
119		use rumqttc::v5::mqttbytes::QoS as V5;
120		match qos {
121			| V5::AtMostOnce => QoS::AtMostOnce,
122			| V5::AtLeastOnce => QoS::AtLeastOnce,
123			| V5::ExactlyOnce => QoS::ExactlyOnce,
124		}
125	}
126}
127
128#[cfg(feature = "paho-mqtt")]
129impl From<paho_mqtt::QoS> for QoS {
130	fn from(qos: paho_mqtt::QoS) -> Self {
131		match qos {
132			| paho_mqtt::QoS::AtMostOnce => QoS::AtMostOnce,
133			| paho_mqtt::QoS::AtLeastOnce => QoS::AtLeastOnce,
134			| paho_mqtt::QoS::ExactlyOnce => QoS::ExactlyOnce,
135		}
136	}
137}
138
139#[cfg(feature = "ntex-mqtt")]
140impl From<ntex_mqtt::QoS> for QoS {
141	fn from(qos: ntex_mqtt::QoS) -> Self {
142		match qos {
143			| ntex_mqtt::QoS::AtMostOnce => QoS::AtMostOnce,
144			| ntex_mqtt::QoS::AtLeastOnce => QoS::AtLeastOnce,
145			| ntex_mqtt::QoS::ExactlyOnce => QoS::ExactlyOnce,
146		}
147	}
148}
149
150impl fmt::Display for QoS {
151	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152		match self {
153			| QoS::AtMostOnce => write!(f, "QoS0"),
154			| QoS::AtLeastOnce => write!(f, "QoS1"),
155			| QoS::ExactlyOnce => write!(f, "QoS2"),
156		}
157	}
158}
159
160#[cfg(all(test, feature = "rumqttc"))]
161mod rumqttc_v5_tests {
162	use super::QoS;
163
164	#[test]
165	fn v5_round_trip_all_levels() {
166		for qos in [QoS::AtMostOnce, QoS::AtLeastOnce, QoS::ExactlyOnce] {
167			assert_eq!(QoS::from(qos.to_rumqttc_v5()), qos);
168		}
169	}
170
171	#[test]
172	fn v5_is_a_distinct_enum_from_v4() {
173		use rumqttc::v5::mqttbytes::QoS as V5;
174		assert_eq!(QoS::AtLeastOnce.to_rumqttc_v5(), V5::AtLeastOnce);
175		assert_eq!(QoS::from(V5::ExactlyOnce), QoS::ExactlyOnce);
176	}
177}