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}