Skip to main content

rocketmq_remoting/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::RequestHeaderCodecV2;
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, RequestHeaderCodecV2)]
28#[serde(rename_all = "camelCase")]
29pub struct RecallMessageRequestHeader {
30    /// Producer group name (optional).
31    ///
32    /// The name of the producer group that originally sent the message.
33    /// This may be omitted in some scenarios where the recall handle
34    /// alone is sufficient to identify the message.
35    pub producer_group: Option<CheetahString>,
36
37    /// Topic name (required).
38    ///
39    /// The topic to which the message was originally sent.
40    #[required]
41    pub topic: CheetahString,
42
43    /// Recall handle (required).
44    ///
45    /// A unique identifier or handle that specifies which message to recall.
46    /// The format and semantics of this handle are implementation-specific.
47    #[required]
48    pub recall_handle: CheetahString,
49
50    /// Topic request header containing common request metadata.
51    ///
52    /// This field is flattened during serialization/deserialization to merge
53    /// its fields with the top-level structure.
54    #[serde(flatten)]
55    pub topic_request_header: Option<TopicRequestHeader>,
56}
57
58impl RecallMessageRequestHeader {
59    /// Creates a new `RecallMessageRequestHeader` with the specified values.
60    ///
61    /// # Arguments
62    ///
63    /// * `topic` - The topic name
64    /// * `recall_handle` - The recall handle identifying the message
65    /// * `producer_group` - Optional producer group name
66    ///
67    /// # Returns
68    ///
69    /// A new `RecallMessageRequestHeader` instance
70    pub fn new(
71        topic: impl Into<CheetahString>,
72        recall_handle: impl Into<CheetahString>,
73        producer_group: Option<impl Into<CheetahString>>,
74    ) -> Self {
75        Self {
76            topic: topic.into(),
77            recall_handle: recall_handle.into(),
78            producer_group: producer_group.map(|pg| pg.into()),
79            topic_request_header: None,
80        }
81    }
82
83    /// Gets a reference to the producer group, if set.
84    pub fn producer_group(&self) -> Option<&CheetahString> {
85        self.producer_group.as_ref()
86    }
87
88    /// Sets the producer group.
89    pub fn set_producer_group(&mut self, producer_group: impl Into<CheetahString>) {
90        self.producer_group = Some(producer_group.into());
91    }
92
93    /// Gets a reference to the topic.
94    pub fn topic(&self) -> &CheetahString {
95        &self.topic
96    }
97
98    /// Sets the topic.
99    pub fn set_topic(&mut self, topic: impl Into<CheetahString>) {
100        self.topic = topic.into();
101    }
102
103    /// Gets a reference to the recall handle.
104    pub fn recall_handle(&self) -> &CheetahString {
105        &self.recall_handle
106    }
107
108    /// Sets the recall handle.
109    pub fn set_recall_handle(&mut self, recall_handle: impl Into<CheetahString>) {
110        self.recall_handle = recall_handle.into();
111    }
112}
113
114impl std::fmt::Display for RecallMessageRequestHeader {
115    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116        write!(
117            f,
118            "RecallMessageRequestHeader {{ producer_group: {:?}, topic: {}, recall_handle: {} }}",
119            self.producer_group, self.topic, self.recall_handle
120        )
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use cheetah_string::CheetahString;
127
128    use super::*;
129
130    #[test]
131    fn test_new_with_producer_group() {
132        let header = RecallMessageRequestHeader::new("TestTopic", "handle123", Some("ProducerGroup1"));
133
134        assert_eq!(header.topic(), &CheetahString::from("TestTopic"));
135        assert_eq!(header.recall_handle(), &CheetahString::from("handle123"));
136        assert_eq!(header.producer_group(), Some(&CheetahString::from("ProducerGroup1")));
137    }
138
139    #[test]
140    fn test_new_without_producer_group() {
141        let header = RecallMessageRequestHeader::new("TestTopic", "handle123", None::<&str>);
142
143        assert_eq!(header.topic(), &CheetahString::from("TestTopic"));
144        assert_eq!(header.recall_handle(), &CheetahString::from("handle123"));
145        assert_eq!(header.producer_group(), None);
146    }
147
148    #[test]
149    fn test_setters() {
150        let mut header = RecallMessageRequestHeader::default();
151
152        header.set_topic("NewTopic");
153        header.set_recall_handle("newHandle");
154        header.set_producer_group("NewGroup");
155
156        assert_eq!(header.topic(), &CheetahString::from("NewTopic"));
157        assert_eq!(header.recall_handle(), &CheetahString::from("newHandle"));
158        assert_eq!(header.producer_group(), Some(&CheetahString::from("NewGroup")));
159    }
160
161    #[test]
162    fn test_display() {
163        let header = RecallMessageRequestHeader::new("TestTopic", "handle123", Some("Group1"));
164        let display = format!("{}", header);
165
166        assert!(display.contains("TestTopic"));
167        assert!(display.contains("handle123"));
168        assert!(display.contains("Group1"));
169    }
170
171    #[test]
172    fn test_serialization() {
173        let header = RecallMessageRequestHeader::new("TestTopic", "handle123", Some("ProducerGroup1"));
174
175        let json = serde_json::to_string(&header).unwrap();
176        assert!(json.contains("\"topic\":\"TestTopic\""));
177        assert!(json.contains("\"recallHandle\":\"handle123\""));
178        assert!(json.contains("\"producerGroup\":\"ProducerGroup1\""));
179    }
180
181    #[test]
182    fn test_deserialization() {
183        let json = r#"{
184            "topic": "TestTopic",
185            "recallHandle": "handle123",
186            "producerGroup": "ProducerGroup1"
187        }"#;
188
189        let header: RecallMessageRequestHeader = serde_json::from_str(json).unwrap();
190        assert_eq!(header.topic(), &CheetahString::from("TestTopic"));
191        assert_eq!(header.recall_handle(), &CheetahString::from("handle123"));
192        assert_eq!(header.producer_group(), Some(&CheetahString::from("ProducerGroup1")));
193    }
194
195    #[test]
196    fn test_deserialization_without_producer_group() {
197        let json = r#"{
198            "topic": "TestTopic",
199            "recallHandle": "handle123"
200        }"#;
201
202        let header: RecallMessageRequestHeader = serde_json::from_str(json).unwrap();
203        assert_eq!(header.topic(), &CheetahString::from("TestTopic"));
204        assert_eq!(header.recall_handle(), &CheetahString::from("handle123"));
205        assert_eq!(header.producer_group(), None);
206    }
207
208    #[test]
209    fn test_clone() {
210        let header = RecallMessageRequestHeader::new("TestTopic", "handle123", Some("ProducerGroup1"));
211        let cloned = header.clone();
212
213        assert_eq!(header.topic(), cloned.topic());
214        assert_eq!(header.recall_handle(), cloned.recall_handle());
215        assert_eq!(header.producer_group(), cloned.producer_group());
216    }
217}