1use super::{PublishProperties, QoS};
2use crate::prelude::{String, Vec};
3
4#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
5pub struct WillMessage {
6 pub topic: String,
7 pub payload: Vec<u8>,
8 pub qos: QoS,
9 pub retain: bool,
10 pub properties: WillProperties,
11}
12
13impl WillMessage {
14 #[must_use]
15 pub fn new(topic: impl Into<String>, payload: impl Into<Vec<u8>>) -> Self {
16 Self {
17 topic: topic.into(),
18 payload: payload.into(),
19 qos: QoS::AtMostOnce,
20 retain: false,
21 properties: WillProperties::default(),
22 }
23 }
24
25 #[must_use]
26 pub fn with_qos(mut self, qos: QoS) -> Self {
27 self.qos = qos;
28 self
29 }
30
31 #[must_use]
32 pub fn with_retain(mut self, retain: bool) -> Self {
33 self.retain = retain;
34 self
35 }
36}
37
38#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
39pub struct WillProperties {
40 pub will_delay_interval: Option<u32>,
41 pub payload_format_indicator: Option<bool>,
42 pub message_expiry_interval: Option<u32>,
43 pub content_type: Option<String>,
44 pub response_topic: Option<String>,
45 pub correlation_data: Option<Vec<u8>>,
46 pub user_properties: Vec<(String, String)>,
47}
48
49impl WillProperties {
50 pub fn apply_to_publish_properties(
51 &self,
52 props: &mut crate::protocol::v5::properties::Properties,
53 ) {
54 if let Some(format) = self.payload_format_indicator {
55 props.set_payload_format_indicator(format);
56 }
57 if let Some(expiry) = self.message_expiry_interval {
58 props.set_message_expiry_interval(expiry);
59 }
60 if let Some(ref content_type) = self.content_type {
61 props.set_content_type(content_type.clone());
62 }
63 if let Some(ref response_topic) = self.response_topic {
64 props.set_response_topic(response_topic.clone());
65 }
66 if let Some(ref correlation_data) = self.correlation_data {
67 props.set_correlation_data(correlation_data.clone().into());
68 }
69 for (key, value) in &self.user_properties {
70 props.add_user_property(key.clone(), value.clone());
71 }
72 }
73}
74
75impl From<WillProperties> for crate::protocol::v5::properties::Properties {
76 fn from(will_props: WillProperties) -> Self {
77 let mut properties = crate::protocol::v5::properties::Properties::default();
78
79 if let Some(delay) = will_props.will_delay_interval {
80 if properties
81 .add(
82 crate::protocol::v5::properties::PropertyId::WillDelayInterval,
83 crate::protocol::v5::properties::PropertyValue::FourByteInteger(delay),
84 )
85 .is_err()
86 {
87 crate::prelude::warn_log!("Failed to add will delay interval property");
88 }
89 }
90
91 if let Some(format) = will_props.payload_format_indicator {
92 if properties
93 .add(
94 crate::protocol::v5::properties::PropertyId::PayloadFormatIndicator,
95 crate::protocol::v5::properties::PropertyValue::Byte(u8::from(format)),
96 )
97 .is_err()
98 {
99 crate::prelude::warn_log!("Failed to add payload format indicator property");
100 }
101 }
102
103 if let Some(expiry) = will_props.message_expiry_interval {
104 if properties
105 .add(
106 crate::protocol::v5::properties::PropertyId::MessageExpiryInterval,
107 crate::protocol::v5::properties::PropertyValue::FourByteInteger(expiry),
108 )
109 .is_err()
110 {
111 crate::prelude::warn_log!("Failed to add message expiry interval property");
112 }
113 }
114
115 if let Some(content_type) = will_props.content_type {
116 if properties
117 .add(
118 crate::protocol::v5::properties::PropertyId::ContentType,
119 crate::protocol::v5::properties::PropertyValue::Utf8String(content_type),
120 )
121 .is_err()
122 {
123 crate::prelude::warn_log!("Failed to add content type property");
124 }
125 }
126
127 if let Some(response_topic) = will_props.response_topic {
128 if properties
129 .add(
130 crate::protocol::v5::properties::PropertyId::ResponseTopic,
131 crate::protocol::v5::properties::PropertyValue::Utf8String(response_topic),
132 )
133 .is_err()
134 {
135 crate::prelude::warn_log!("Failed to add response topic property");
136 }
137 }
138
139 if let Some(correlation_data) = will_props.correlation_data {
140 if properties
141 .add(
142 crate::protocol::v5::properties::PropertyId::CorrelationData,
143 crate::protocol::v5::properties::PropertyValue::BinaryData(
144 correlation_data.into(),
145 ),
146 )
147 .is_err()
148 {
149 crate::prelude::warn_log!("Failed to add correlation data property");
150 }
151 }
152
153 for (key, value) in will_props.user_properties {
154 if properties
155 .add(
156 crate::protocol::v5::properties::PropertyId::UserProperty,
157 crate::protocol::v5::properties::PropertyValue::Utf8StringPair(key, value),
158 )
159 .is_err()
160 {
161 crate::prelude::warn_log!("Failed to add user property");
162 }
163 }
164
165 properties
166 }
167}
168
169#[derive(Debug, Clone)]
170pub struct Message {
171 pub topic: String,
172 pub payload: Vec<u8>,
173 pub qos: QoS,
174 pub retain: bool,
175 pub properties: MessageProperties,
176}
177
178impl From<crate::packet::publish::PublishPacket> for Message {
179 fn from(packet: crate::packet::publish::PublishPacket) -> Self {
180 Self {
181 topic: packet.topic_name,
182 payload: packet.payload.to_vec(),
183 qos: packet.qos,
184 retain: packet.retain,
185 properties: MessageProperties::from(packet.properties),
186 }
187 }
188}
189
190#[derive(Debug, Clone, Default)]
191pub struct MessageProperties {
192 pub payload_format_indicator: Option<bool>,
193 pub message_expiry_interval: Option<u32>,
194 pub response_topic: Option<String>,
195 pub correlation_data: Option<Vec<u8>>,
196 pub user_properties: Vec<(String, String)>,
197 pub subscription_identifiers: Vec<u32>,
198 pub content_type: Option<String>,
199}
200
201impl From<crate::protocol::v5::properties::Properties> for MessageProperties {
202 fn from(props: crate::protocol::v5::properties::Properties) -> Self {
203 use crate::protocol::v5::properties::{PropertyId, PropertyValue};
204
205 let mut result = Self::default();
206
207 for (id, value) in props.iter() {
208 match (id, value) {
209 (PropertyId::PayloadFormatIndicator, PropertyValue::Byte(v)) => {
210 result.payload_format_indicator = Some(v != &0);
211 }
212 (PropertyId::MessageExpiryInterval, PropertyValue::FourByteInteger(v)) => {
213 result.message_expiry_interval = Some(*v);
214 }
215 (PropertyId::ResponseTopic, PropertyValue::Utf8String(v)) => {
216 result.response_topic = Some(v.clone());
217 }
218 (PropertyId::CorrelationData, PropertyValue::BinaryData(v)) => {
219 result.correlation_data = Some(v.to_vec());
220 }
221 (PropertyId::UserProperty, PropertyValue::Utf8StringPair(k, v)) => {
222 result.user_properties.push((k.clone(), v.clone()));
223 }
224 (PropertyId::SubscriptionIdentifier, PropertyValue::VariableByteInteger(v)) => {
225 result.subscription_identifiers.push(*v);
226 }
227 (PropertyId::ContentType, PropertyValue::Utf8String(v)) => {
228 result.content_type = Some(v.clone());
229 }
230 _ => {}
231 }
232 }
233
234 result
235 }
236}
237
238impl From<MessageProperties> for PublishProperties {
239 fn from(msg_props: MessageProperties) -> Self {
240 Self {
241 payload_format_indicator: msg_props.payload_format_indicator,
242 message_expiry_interval: msg_props.message_expiry_interval,
243 topic_alias: None,
244 response_topic: msg_props.response_topic,
245 correlation_data: msg_props.correlation_data,
246 user_properties: msg_props.user_properties,
247 subscription_identifiers: msg_props.subscription_identifiers,
248 content_type: msg_props.content_type,
249 }
250 }
251}