Skip to main content

rocketmq_protocol/protocol/header/
recall_message_request_header.rs

1// Copyright 2023 The RocketMQ Rust Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use cheetah_string::CheetahString;
16use rocketmq_macros::RequestHeaderCodec;
17use serde::Deserialize;
18use serde::Serialize;
19
20use crate::rpc::topic_request_header::TopicRequestHeader;
21
22/// Request header for recalling (withdrawing) a message.
23///
24/// This header is used with `RequestCode::RECALL_MESSAGE` to recall a previously
25/// sent message from the broker. The recall operation requires the producer group,
26/// topic, and a recall handle that identifies the message to be recalled.
27#[derive(Clone, Debug, Serialize, Deserialize, Default, RequestHeaderCodec)]
28#[header(
29    type_id = "rocketmq_protocol::protocol::header::recall_message_request_header::RecallMessageRequestHeader",
30    java_class = "org.apache.rocketmq.remoting.protocol.header.RecallMessageRequestHeader"
31)]
32#[serde(rename_all = "camelCase")]
33pub struct RecallMessageRequestHeader {
34    /// Producer group name (optional).
35    ///
36    /// The name of the producer group that originally sent the message.
37    /// This may be omitted in some scenarios where the recall handle
38    /// alone is sufficient to identify the message.
39    pub producer_group: Option<CheetahString>,
40
41    /// Topic name (required).
42    ///
43    /// The topic to which the message was originally sent.
44    #[header(required)]
45    pub topic: CheetahString,
46
47    /// Recall handle (required).
48    ///
49    /// A unique identifier or handle that specifies which message to recall.
50    /// The format and semantics of this handle are implementation-specific.
51    #[header(required)]
52    pub recall_handle: CheetahString,
53
54    /// Topic request header containing common request metadata.
55    ///
56    /// This field is flattened during serialization/deserialization to merge
57    /// its fields with the top-level structure.
58    #[serde(flatten)]
59    #[header(flatten, presence = "always")]
60    pub topic_request_header: Option<TopicRequestHeader>,
61}
62
63impl RecallMessageRequestHeader {
64    /// Creates a new `RecallMessageRequestHeader` with the specified values.
65    ///
66    /// # Arguments
67    ///
68    /// * `topic` - The topic name
69    /// * `recall_handle` - The recall handle identifying the message
70    /// * `producer_group` - Optional producer group name
71    ///
72    /// # Returns
73    ///
74    /// A new `RecallMessageRequestHeader` instance
75    pub fn new(
76        topic: impl Into<CheetahString>,
77        recall_handle: impl Into<CheetahString>,
78        producer_group: Option<impl Into<CheetahString>>,
79    ) -> Self {
80        Self {
81            topic: topic.into(),
82            recall_handle: recall_handle.into(),
83            producer_group: producer_group.map(|pg| pg.into()),
84            topic_request_header: None,
85        }
86    }
87
88    /// Gets a reference to the producer group, if set.
89    pub fn producer_group(&self) -> Option<&CheetahString> {
90        self.producer_group.as_ref()
91    }
92
93    /// Sets the producer group.
94    pub fn set_producer_group(&mut self, producer_group: impl Into<CheetahString>) {
95        self.producer_group = Some(producer_group.into());
96    }
97
98    /// Gets a reference to the topic.
99    pub fn topic(&self) -> &CheetahString {
100        &self.topic
101    }
102
103    /// Sets the topic.
104    pub fn set_topic(&mut self, topic: impl Into<CheetahString>) {
105        self.topic = topic.into();
106    }
107
108    /// Gets a reference to the recall handle.
109    pub fn recall_handle(&self) -> &CheetahString {
110        &self.recall_handle
111    }
112
113    /// Sets the recall handle.
114    pub fn set_recall_handle(&mut self, recall_handle: impl Into<CheetahString>) {
115        self.recall_handle = recall_handle.into();
116    }
117}
118
119impl std::fmt::Display for RecallMessageRequestHeader {
120    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121        write!(
122            f,
123            "RecallMessageRequestHeader {{ producer_group: {:?}, topic: {}, recall_handle: {} }}",
124            self.producer_group, self.topic, self.recall_handle
125        )
126    }
127}
128
129#[cfg(test)]
130mod tests {
131    use cheetah_string::CheetahString;
132
133    use super::*;
134
135    #[test]
136    fn test_display() {
137        let header = RecallMessageRequestHeader::new("TestTopic", "handle123", Some("Group1"));
138        let display = format!("{}", header);
139
140        assert!(display.contains("TestTopic"));
141        assert!(display.contains("handle123"));
142        assert!(display.contains("Group1"));
143    }
144
145    #[test]
146    fn test_serialization() {
147        let mut header = RecallMessageRequestHeader::new("initial-topic", "initial-handle", None::<&str>);
148        assert!(header.producer_group().is_none());
149
150        header.set_topic("TestTopic");
151        header.set_recall_handle("handle123");
152        header.set_producer_group("ProducerGroup1");
153
154        let json = serde_json::to_string(&header).unwrap();
155        assert!(json.contains("\"topic\":\"TestTopic\""));
156        assert!(json.contains("\"recallHandle\":\"handle123\""));
157        assert!(json.contains("\"producerGroup\":\"ProducerGroup1\""));
158    }
159
160    #[test]
161    fn test_deserialization() {
162        let json = r#"{
163            "topic": "TestTopic",
164            "recallHandle": "handle123",
165            "producerGroup": "ProducerGroup1"
166        }"#;
167
168        let header: RecallMessageRequestHeader = serde_json::from_str(json).unwrap();
169        assert_eq!(header.topic(), &CheetahString::from("TestTopic"));
170        assert_eq!(header.recall_handle(), &CheetahString::from("handle123"));
171        assert_eq!(header.producer_group(), Some(&CheetahString::from("ProducerGroup1")));
172    }
173
174    #[test]
175    fn test_deserialization_without_producer_group() {
176        let json = r#"{
177            "topic": "TestTopic",
178            "recallHandle": "handle123"
179        }"#;
180
181        let header: RecallMessageRequestHeader = serde_json::from_str(json).unwrap();
182        assert_eq!(header.topic(), &CheetahString::from("TestTopic"));
183        assert_eq!(header.recall_handle(), &CheetahString::from("handle123"));
184        assert_eq!(header.producer_group(), None);
185    }
186}