1use std::collections::HashMap;
16
17use cheetah_string::CheetahString;
18
19use crate::common::message::MessageConst;
20
21#[derive(Clone, Debug, Default)]
23pub struct MessageProperties {
24 inner: HashMap<CheetahString, CheetahString>,
25}
26
27impl MessageProperties {
28 pub fn new() -> Self {
30 Self::default()
31 }
32
33 #[inline]
35 pub fn get(&self, key: MessagePropertyKey) -> Option<&str> {
36 self.inner.get(key.as_str()).map(|s| s.as_str())
37 }
38
39 pub(crate) fn insert(&mut self, key: MessagePropertyKey, value: impl Into<CheetahString>) {
41 self.inner.insert(key.to_cheetah_string(), value.into());
42 }
43
44 pub(crate) fn remove(&mut self, key: MessagePropertyKey) -> Option<CheetahString> {
46 self.inner.remove(key.as_str())
47 }
48
49 #[inline]
51 pub fn as_map(&self) -> &HashMap<CheetahString, CheetahString> {
52 &self.inner
53 }
54
55 #[doc(hidden)]
57 #[inline]
58 pub fn as_map_mut(&mut self) -> &mut HashMap<CheetahString, CheetahString> {
59 &mut self.inner
60 }
61
62 #[inline]
64 pub fn from_map(map: HashMap<CheetahString, CheetahString>) -> Self {
65 Self { inner: map }
66 }
67
68 #[inline]
70 pub fn len(&self) -> usize {
71 self.inner.len()
72 }
73
74 #[inline]
76 pub fn is_empty(&self) -> bool {
77 self.inner.is_empty()
78 }
79
80 #[inline]
84 pub fn tags(&self) -> Option<&str> {
85 self.get(MessagePropertyKey::Tags)
86 }
87
88 pub fn keys(&self) -> Option<Vec<String>> {
90 self.get(MessagePropertyKey::Keys).map(|s| {
91 s.split(MessageConst::KEY_SEPARATOR)
92 .filter(|k| !k.is_empty())
93 .map(|k| k.to_string())
94 .collect()
95 })
96 }
97
98 pub fn delay_level(&self) -> Option<i32> {
100 self.get(MessagePropertyKey::DelayTimeLevel)
101 .and_then(|s| s.parse().ok())
102 }
103
104 pub fn delay_time_sec(&self) -> Option<u64> {
106 self.get(MessagePropertyKey::DelayTimeSec).and_then(|s| s.parse().ok())
107 }
108
109 pub fn delay_time_ms(&self) -> Option<u64> {
111 self.get(MessagePropertyKey::DelayTimeMs).and_then(|s| s.parse().ok())
112 }
113
114 pub fn deliver_time_ms(&self) -> Option<u64> {
116 self.get(MessagePropertyKey::DeliverTimeMs).and_then(|s| s.parse().ok())
117 }
118
119 pub fn buyer_id(&self) -> Option<&str> {
121 self.get(MessagePropertyKey::BuyerId)
122 }
123
124 pub fn wait_store_msg_ok(&self) -> bool {
126 self.get(MessagePropertyKey::WaitStoreMsgOk)
127 .map(|s| s.eq_ignore_ascii_case("true"))
128 .unwrap_or(true)
129 }
130
131 pub fn origin_message_id(&self) -> Option<&str> {
133 self.get(MessagePropertyKey::OriginMessageId)
134 }
135
136 pub fn retry_topic(&self) -> Option<&str> {
138 self.get(MessagePropertyKey::RetryTopic)
139 }
140
141 pub fn real_topic(&self) -> Option<&str> {
143 self.get(MessagePropertyKey::RealTopic)
144 }
145
146 pub fn real_queue_id(&self) -> Option<i32> {
148 self.get(MessagePropertyKey::RealQueueId).and_then(|s| s.parse().ok())
149 }
150
151 pub fn unique_client_msg_id(&self) -> Option<&str> {
153 self.get(MessagePropertyKey::UniqueClientMsgId)
154 }
155
156 pub fn producer_group(&self) -> Option<&str> {
158 self.get(MessagePropertyKey::ProducerGroup)
159 }
160
161 pub fn instance_id(&self) -> Option<&str> {
163 self.get(MessagePropertyKey::InstanceId)
164 }
165
166 pub fn correlation_id(&self) -> Option<&str> {
168 self.get(MessagePropertyKey::CorrelationId)
169 }
170
171 pub fn message_type(&self) -> Option<&str> {
173 self.get(MessagePropertyKey::MessageType)
174 }
175
176 pub fn trace_switch(&self) -> bool {
178 self.get(MessagePropertyKey::TraceSwitch)
179 .map(|s| s == "true")
180 .unwrap_or(false)
181 }
182
183 pub fn priority(&self) -> i32 {
185 self.get(MessagePropertyKey::Priority)
186 .and_then(|s| s.parse().ok())
187 .unwrap_or(-1)
188 }
189}
190
191#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
193pub enum MessagePropertyKey {
194 Tags,
195 Keys,
196 WaitStoreMsgOk,
197 DelayTimeLevel,
198 DelayTimeSec,
199 DelayTimeMs,
200 DeliverTimeMs,
201 RetryTopic,
202 RealTopic,
203 RealQueueId,
204 TransactionPrepared,
205 ProducerGroup,
206 MinOffset,
207 MaxOffset,
208 BuyerId,
209 OriginGroup,
210 OriginMessageId,
211 TransferFlag,
212 CorrectionFlag,
213 MqReplyTopic,
214 MqReplyQueueId,
215 UniqueClientMsgId,
216 ReconsumeTime,
217 MsgRegion,
218 TraceSwitch,
219 UniqueKey,
220 MaxReconsumeTimes,
221 ConsumeStartTimestamp,
222 PopCk,
223 PopCkOffset,
224 FirstPopTime,
225 Priority,
226 TransactionPreparedQueueOffset,
227 DupInfo,
228 ExtendUniqInfo,
229 InstanceId,
230 CorrelationId,
231 MessageReplyToClient,
232 MessageTtl,
233 ReplyMessageArriveTime,
234 PushReplyTime,
235 Cluster,
236 MessageType,
237 InnerMultiQueueOffset,
238 TimerDelayLevel,
239 TimerEnqueueMs,
240 TimerDequeueMs,
241 TimerRollTimes,
242 TimerRollLabel,
243 TimerOutMs,
244 TimerDelUniqkey,
245 BornHost,
246 BornTimestamp,
247 DlqOriginTopic,
248 DlqOriginMessageId,
249 Crc32,
250 Redirect,
251 ForwardQueueId,
252 InnerBase,
253 InnerMultiDispatch,
254 InnerNum,
255 Mq2Flag,
256 TransOffset,
257 ShardingKey,
258 TransactionId,
259 TransactionCheckTimes,
260 TransientGroupConfig,
261 TransientTopicConfig,
262 TraceContext,
263}
264
265impl MessagePropertyKey {
266 pub fn as_str(&self) -> &'static str {
268 match self {
269 Self::Tags => MessageConst::PROPERTY_TAGS,
270 Self::Keys => MessageConst::PROPERTY_KEYS,
271 Self::WaitStoreMsgOk => MessageConst::PROPERTY_WAIT_STORE_MSG_OK,
272 Self::DelayTimeLevel => MessageConst::PROPERTY_DELAY_TIME_LEVEL,
273 Self::DelayTimeSec => MessageConst::PROPERTY_TIMER_DELAY_SEC,
274 Self::DelayTimeMs => MessageConst::PROPERTY_TIMER_DELAY_MS,
275 Self::DeliverTimeMs => MessageConst::PROPERTY_TIMER_DELIVER_MS,
276 Self::RetryTopic => MessageConst::PROPERTY_RETRY_TOPIC,
277 Self::RealTopic => MessageConst::PROPERTY_REAL_TOPIC,
278 Self::RealQueueId => MessageConst::PROPERTY_REAL_QUEUE_ID,
279 Self::TransactionPrepared => MessageConst::PROPERTY_TRANSACTION_PREPARED,
280 Self::ProducerGroup => MessageConst::PROPERTY_PRODUCER_GROUP,
281 Self::MinOffset => MessageConst::PROPERTY_MIN_OFFSET,
282 Self::MaxOffset => MessageConst::PROPERTY_MAX_OFFSET,
283 Self::BuyerId => MessageConst::PROPERTY_BUYER_ID,
284 Self::OriginGroup => MessageConst::PROPERTY_ORIGIN_GROUP,
285 Self::OriginMessageId => MessageConst::PROPERTY_ORIGIN_MESSAGE_ID,
286 Self::TransferFlag => MessageConst::PROPERTY_TRANSFER_FLAG,
287 Self::CorrectionFlag => MessageConst::PROPERTY_CORRECTION_FLAG,
288 Self::MqReplyTopic => MessageConst::PROPERTY_MESSAGE_REPLY_TO_CLIENT,
289 Self::MqReplyQueueId => MessageConst::PROPERTY_MESSAGE_REPLY_TO_CLIENT,
290 Self::UniqueClientMsgId => MessageConst::PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX,
291 Self::ReconsumeTime => MessageConst::PROPERTY_RECONSUME_TIME,
292 Self::MsgRegion => MessageConst::PROPERTY_MSG_REGION,
293 Self::TraceSwitch => MessageConst::PROPERTY_TRACE_SWITCH,
294 Self::UniqueKey => MessageConst::PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX,
295 Self::MaxReconsumeTimes => MessageConst::PROPERTY_MAX_RECONSUME_TIMES,
296 Self::ConsumeStartTimestamp => MessageConst::PROPERTY_CONSUME_START_TIMESTAMP,
297 Self::PopCk => MessageConst::PROPERTY_POP_CK,
298 Self::PopCkOffset => MessageConst::PROPERTY_POP_CK_OFFSET,
299 Self::FirstPopTime => MessageConst::PROPERTY_FIRST_POP_TIME,
300 Self::Priority => MessageConst::PROPERTY_PRIORITY,
301 Self::TransactionPreparedQueueOffset => MessageConst::PROPERTY_TRANSACTION_PREPARED_QUEUE_OFFSET,
302 Self::DupInfo => MessageConst::DUP_INFO,
303 Self::ExtendUniqInfo => MessageConst::PROPERTY_EXTEND_UNIQ_INFO,
304 Self::InstanceId => MessageConst::PROPERTY_INSTANCE_ID,
305 Self::CorrelationId => MessageConst::PROPERTY_CORRELATION_ID,
306 Self::MessageReplyToClient => MessageConst::PROPERTY_MESSAGE_REPLY_TO_CLIENT,
307 Self::MessageTtl => MessageConst::PROPERTY_MESSAGE_TTL,
308 Self::ReplyMessageArriveTime => MessageConst::PROPERTY_REPLY_MESSAGE_ARRIVE_TIME,
309 Self::PushReplyTime => MessageConst::PROPERTY_PUSH_REPLY_TIME,
310 Self::Cluster => MessageConst::PROPERTY_CLUSTER,
311 Self::MessageType => MessageConst::PROPERTY_MESSAGE_TYPE,
312 Self::InnerMultiQueueOffset => MessageConst::PROPERTY_INNER_MULTI_QUEUE_OFFSET,
313 Self::TimerDelayLevel => MessageConst::PROPERTY_TIMER_DELAY_LEVEL,
314 Self::TimerEnqueueMs => MessageConst::PROPERTY_TIMER_ENQUEUE_MS,
315 Self::TimerDequeueMs => MessageConst::PROPERTY_TIMER_DEQUEUE_MS,
316 Self::TimerRollTimes => MessageConst::PROPERTY_TIMER_ROLL_TIMES,
317 Self::TimerRollLabel => MessageConst::PROPERTY_TIMER_ROLL_LABEL,
318 Self::TimerOutMs => MessageConst::PROPERTY_TIMER_OUT_MS,
319 Self::TimerDelUniqkey => MessageConst::PROPERTY_TIMER_DEL_UNIQKEY,
320 Self::BornHost => MessageConst::PROPERTY_BORN_HOST,
321 Self::BornTimestamp => MessageConst::PROPERTY_BORN_TIMESTAMP,
322 Self::DlqOriginTopic => MessageConst::PROPERTY_DLQ_ORIGIN_TOPIC,
323 Self::DlqOriginMessageId => MessageConst::PROPERTY_DLQ_ORIGIN_MESSAGE_ID,
324 Self::Crc32 => MessageConst::PROPERTY_CRC32,
325 Self::Redirect => MessageConst::PROPERTY_REDIRECT,
326 Self::ForwardQueueId => MessageConst::PROPERTY_FORWARD_QUEUE_ID,
327 Self::InnerBase => MessageConst::PROPERTY_INNER_BASE,
328 Self::InnerMultiDispatch => MessageConst::PROPERTY_INNER_MULTI_DISPATCH,
329 Self::InnerNum => MessageConst::PROPERTY_INNER_NUM,
330 Self::Mq2Flag => MessageConst::PROPERTY_MQ2_FLAG,
331 Self::TransOffset => MessageConst::PROPERTY_TRANS_OFFSET,
332 Self::ShardingKey => MessageConst::PROPERTY_SHARDING_KEY,
333 Self::TransactionId => MessageConst::PROPERTY_TRANSACTION_ID,
334 Self::TransactionCheckTimes => MessageConst::PROPERTY_TRANSACTION_CHECK_TIMES,
335 Self::TransientGroupConfig => MessageConst::PROPERTY_TRANSIENT_GROUP_CONFIG,
336 Self::TransientTopicConfig => MessageConst::PROPERTY_TRANSIENT_TOPIC_CONFIG,
337 Self::TraceContext => MessageConst::PROPERTY_TRACE_CONTEXT,
338 }
339 }
340
341 pub(crate) fn to_cheetah_string(self) -> CheetahString {
342 CheetahString::from_static_str(self.as_str())
343 }
344}
345
346#[cfg(test)]
347mod tests {
348 use super::*;
349
350 #[test]
351 fn wait_store_msg_ok_matches_java_boolean_parse_semantics() {
352 let mut properties = MessageProperties::new();
353 assert!(properties.wait_store_msg_ok());
354
355 properties.insert(MessagePropertyKey::WaitStoreMsgOk, "true");
356 assert!(properties.wait_store_msg_ok());
357
358 properties.insert(MessagePropertyKey::WaitStoreMsgOk, "TRUE");
359 assert!(properties.wait_store_msg_ok());
360
361 properties.insert(MessagePropertyKey::WaitStoreMsgOk, "false");
362 assert!(!properties.wait_store_msg_ok());
363
364 properties.insert(MessagePropertyKey::WaitStoreMsgOk, "yes");
365 assert!(!properties.wait_store_msg_ok());
366 }
367
368 #[test]
369 fn priority_matches_java_default_and_parse_semantics() {
370 let mut properties = MessageProperties::new();
371 assert_eq!(properties.priority(), -1);
372
373 properties.insert(MessagePropertyKey::Priority, "7");
374 assert_eq!(properties.priority(), 7);
375
376 properties.insert(MessagePropertyKey::Priority, "invalid");
377 assert_eq!(properties.priority(), -1);
378 }
379
380 #[test]
381 fn protocol_property_keys_include_java_message_const_gaps() {
382 assert_eq!(MessagePropertyKey::OriginGroup.as_str(), "ORIGIN_GROUP");
383 assert_eq!(MessagePropertyKey::TransOffset.as_str(), "TRANS_OFFSET");
384 assert_eq!(MessagePropertyKey::TimerRollLabel.as_str(), "TIMER_ROLL_LABEL");
385 }
386}