mqtt_protocol_core/mqtt/packet/retain_handling.rs
1/**
2 * MIT License
3 *
4 * Copyright (c) 2025 Takatoshi Kondo
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24use num_enum::TryFromPrimitive;
25use serde::{Deserialize, Serialize};
26use std::fmt;
27
28/// Retain Handling Option for MQTT Subscriptions
29///
30/// This enum defines how retained messages should be handled when establishing
31/// a new subscription. It corresponds to bits 4-5 in the Subscription Options
32/// byte as specified in the MQTT v5.0 protocol specification.
33///
34/// Retained messages are messages that the broker stores and delivers to new
35/// subscribers immediately upon subscription. The retain handling option
36/// controls this behavior, allowing subscribers to specify whether they want
37/// to receive existing retained messages.
38///
39/// # Protocol Specification
40///
41/// The retain handling option is encoded in bits 4-5 of the Subscription Options:
42/// - Bits 4-5: `00` = Send retained messages at subscribe
43/// - Bits 4-5: `01` = Send retained messages only for new subscriptions
44/// - Bits 4-5: `10` = Do not send retained messages at subscribe
45/// - Bits 4-5: `11` = Reserved (invalid)
46///
47/// # Use Cases
48///
49/// - **SendRetained**: Useful when a subscriber always wants the latest retained value
50/// - **SendRetainedIfNotExists**: Prevents duplicate retained messages on subscription renewal
51/// - **DoNotSendRetained**: For subscribers that only want new messages
52///
53/// # Examples
54///
55/// ```ignore
56/// use mqtt_protocol_core::mqtt;
57///
58/// // Always receive retained messages when subscribing
59/// let always_retained = mqtt::packet::RetainHandling::SendRetained;
60///
61/// // Only receive retained messages for new subscriptions
62/// let new_only = mqtt::packet::RetainHandling::SendRetainedIfNotExists;
63///
64/// // Never receive retained messages
65/// let no_retained = mqtt::packet::RetainHandling::DoNotSendRetained;
66///
67/// // Convert from byte value
68/// let from_byte = mqtt::packet::RetainHandling::try_from(1u8).unwrap();
69/// assert_eq!(from_byte, mqtt::packet::RetainHandling::SendRetainedIfNotExists);
70/// ```
71#[derive(
72 Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, TryFromPrimitive,
73)]
74#[repr(u8)]
75pub enum RetainHandling {
76 /// Send retained messages at the time of the subscribe
77 ///
78 /// When a subscription is established, the broker will immediately send
79 /// any retained messages that match the topic filter to the subscriber.
80 /// This is the default behavior and ensures subscribers always receive
81 /// the most recent retained value for matching topics.
82 ///
83 /// This option is suitable for:
84 /// - Status monitoring applications that need current state
85 /// - Subscribers that always want the latest retained value
86 /// - Applications where missing the current retained value would be problematic
87 SendRetained = 0,
88
89 /// Send retained messages at subscribe only if the subscription does not currently exist
90 ///
91 /// The broker will send retained messages only if this is a completely new
92 /// subscription. If the client already has an active subscription to the
93 /// same topic filter (even with different QoS), retained messages will not
94 /// be sent again. This prevents duplicate delivery of retained messages.
95 ///
96 /// This option is suitable for:
97 /// - Preventing duplicate retained messages during connection recovery
98 /// - Applications that upgrade/downgrade subscription QoS levels
99 /// - Scenarios where processing the same retained message twice is undesirable
100 SendRetainedIfNotExists = 1,
101
102 /// Do not send retained messages at the time of the subscribe
103 ///
104 /// The broker will not send any retained messages when the subscription
105 /// is established, regardless of whether matching retained messages exist.
106 /// The subscriber will only receive newly published messages after the
107 /// subscription is active.
108 ///
109 /// This option is suitable for:
110 /// - Event-driven applications that only care about new events
111 /// - Applications where retained messages represent historical data
112 /// - Scenarios where the current retained value is not relevant to the subscriber
113 DoNotSendRetained = 2,
114}
115
116/// Implementation of `Display` for `RetainHandling`
117///
118/// Formats the retain handling option as a human-readable string representation.
119/// This allows retain handling values to be used with `println!`, `format!`, and
120/// other string formatting operations.
121///
122/// # Output Format
123///
124/// * `RetainHandling::SendRetained` -> "SendRetained"
125/// * `RetainHandling::SendRetainedIfNotExists` -> "SendRetainedIfNotExists"
126/// * `RetainHandling::DoNotSendRetained` -> "DoNotSendRetained"
127impl fmt::Display for RetainHandling {
128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 let s = match self {
130 Self::SendRetained => "SendRetained",
131 Self::SendRetainedIfNotExists => "SendRetainedIfNotExists",
132 Self::DoNotSendRetained => "DoNotSendRetained",
133 };
134 write!(f, "{s}")
135 }
136}
137
138/// Implementation of `Default` for `RetainHandling`
139///
140/// Returns `SendRetained` as the default retain handling behavior.
141/// This matches the MQTT protocol default where retained messages
142/// are sent to new subscribers unless explicitly configured otherwise.
143///
144/// # Returns
145///
146/// `RetainHandling::SendRetained` - The default retain handling behavior
147impl Default for RetainHandling {
148 fn default() -> Self {
149 Self::SendRetained
150 }
151}