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