rocketmq_protocol/protocol/header/
create_topic_request_header.rs1use cheetah_string::CheetahString;
16use rocketmq_macros::RequestHeaderCodec;
17use serde::Deserialize;
18use serde::Serialize;
19
20use crate::rpc::topic_request_header::TopicRequestHeader;
21
22#[derive(Serialize, Deserialize, Debug, RequestHeaderCodec)]
23#[header(
24 type_id = "rocketmq_protocol::protocol::header::create_topic_request_header::CreateTopicRequestHeader",
25 java_class = "org.apache.rocketmq.remoting.protocol.header.CreateTopicRequestHeader",
26 validate = "Self::validate"
27)]
28pub struct CreateTopicRequestHeader {
29 #[header(required)]
30 #[serde(rename = "topic")]
31 pub topic: CheetahString,
32
33 #[header(required)]
34 #[serde(rename = "defaultTopic")]
35 pub default_topic: CheetahString,
36
37 #[header(required)]
38 #[serde(rename = "readQueueNums")]
39 pub read_queue_nums: i32,
40
41 #[header(required)]
42 #[serde(rename = "writeQueueNums")]
43 pub write_queue_nums: i32,
44
45 #[header(required)]
46 #[serde(rename = "perm")]
47 pub perm: i32,
48
49 #[header(required)]
50 #[serde(rename = "topicFilterType")]
51 pub topic_filter_type: CheetahString,
52
53 #[serde(rename = "topicSysFlag")]
54 pub topic_sys_flag: Option<i32>,
55
56 #[header(required)]
57 #[serde(rename = "order")]
58 pub order: bool,
59
60 #[serde(rename = "attributes")]
61 pub attributes: Option<CheetahString>,
62
63 #[serde(rename = "force")]
64 #[serde(default = "default_force")]
65 #[header(default_with = "default_force", default_semantic = "literal:false")]
66 pub force: Option<bool>,
67
68 #[serde(flatten)]
69 #[header(flatten, presence = "always")]
70 pub topic_request_header: Option<TopicRequestHeader>,
71}
72
73fn default_force() -> Option<bool> {
74 Some(false)
75}
76
77impl CreateTopicRequestHeader {
78 fn validate(&self) -> Result<(), crate::ProtocolContractViolation> {
79 match self.topic_filter_type.as_str() {
80 "SINGLE_TAG" | "MULTI_TAG" => Ok(()),
81 _ => Err(crate::ProtocolContractViolation::Validation {
82 header: "rocketmq_protocol::protocol::header::create_topic_request_header::CreateTopicRequestHeader",
83 rule: "supported_topic_filter_type",
84 }),
85 }
86 }
87}
88
89#[cfg(test)]
90impl CreateTopicRequestHeader {
91 const TOPIC: &'static str = "topic";
92 const DEFAULT_TOPIC: &'static str = "defaultTopic";
93 const READ_QUEUE_NUMS: &'static str = "readQueueNums";
94 const WRITE_QUEUE_NUMS: &'static str = "writeQueueNums";
95 const PERM: &'static str = "perm";
96 const TOPIC_FILTER_TYPE: &'static str = "topicFilterType";
97 const TOPIC_SYS_FLAG: &'static str = "topicSysFlag";
98 const ORDER: &'static str = "order";
99 const ATTRIBUTES: &'static str = "attributes";
100 const FORCE: &'static str = "force";
101}
102
103#[cfg(test)]
104mod tests {
105 use std::collections::HashMap;
106
107 use super::*;
108 use crate::protocol::command_custom_header::CommandCustomHeader;
109 use crate::protocol::command_custom_header::FromMap;
110
111 #[test]
112 fn create_topic_request_header_to_map() {
113 let header = CreateTopicRequestHeader {
114 topic: CheetahString::from("test_topic"),
115 default_topic: CheetahString::from("default_topic"),
116 read_queue_nums: 4,
117 write_queue_nums: 4,
118 perm: 6,
119 topic_filter_type: CheetahString::from("SINGLE_TAG"),
120 topic_sys_flag: Some(1),
121 order: true,
122 attributes: Some(CheetahString::from("attributes")),
123 force: Some(true),
124 topic_request_header: None,
125 };
126
127 let map = header.to_map().unwrap();
128 assert_eq!(
129 map.get(&CheetahString::from_static_str(CreateTopicRequestHeader::TOPIC))
130 .unwrap(),
131 &CheetahString::from("test_topic")
132 );
133 assert_eq!(
134 map.get(&CheetahString::from_static_str(CreateTopicRequestHeader::DEFAULT_TOPIC))
135 .unwrap(),
136 &CheetahString::from("default_topic")
137 );
138 assert_eq!(
139 map.get(&CheetahString::from_static_str(
140 CreateTopicRequestHeader::READ_QUEUE_NUMS
141 ))
142 .unwrap(),
143 &CheetahString::from("4")
144 );
145 assert_eq!(
146 map.get(&CheetahString::from_static_str(
147 CreateTopicRequestHeader::WRITE_QUEUE_NUMS
148 ))
149 .unwrap(),
150 &CheetahString::from("4")
151 );
152 assert_eq!(
153 map.get(&CheetahString::from_static_str(CreateTopicRequestHeader::PERM))
154 .unwrap(),
155 &CheetahString::from("6")
156 );
157 assert_eq!(
158 map.get(&CheetahString::from_static_str(
159 CreateTopicRequestHeader::TOPIC_FILTER_TYPE
160 ))
161 .unwrap(),
162 &CheetahString::from("SINGLE_TAG")
163 );
164 assert_eq!(
165 map.get(&CheetahString::from_static_str(
166 CreateTopicRequestHeader::TOPIC_SYS_FLAG
167 ))
168 .unwrap(),
169 &CheetahString::from("1")
170 );
171 assert_eq!(
172 map.get(&CheetahString::from_static_str(CreateTopicRequestHeader::ORDER))
173 .unwrap(),
174 &CheetahString::from("true")
175 );
176 assert_eq!(
177 map.get(&CheetahString::from_static_str(CreateTopicRequestHeader::ATTRIBUTES))
178 .unwrap(),
179 &CheetahString::from("attributes")
180 );
181 assert_eq!(
182 map.get(&CheetahString::from_static_str(CreateTopicRequestHeader::FORCE))
183 .unwrap(),
184 &CheetahString::from("true")
185 );
186 }
187
188 #[test]
189 fn create_topic_request_header_from_map() {
190 let mut map = HashMap::new();
191 map.insert(
192 CheetahString::from_static_str(CreateTopicRequestHeader::TOPIC),
193 CheetahString::from("test_topic"),
194 );
195 map.insert(
196 CheetahString::from_static_str(CreateTopicRequestHeader::DEFAULT_TOPIC),
197 CheetahString::from("default_topic"),
198 );
199 map.insert(
200 CheetahString::from_static_str(CreateTopicRequestHeader::READ_QUEUE_NUMS),
201 CheetahString::from("4"),
202 );
203 map.insert(
204 CheetahString::from_static_str(CreateTopicRequestHeader::WRITE_QUEUE_NUMS),
205 CheetahString::from("4"),
206 );
207 map.insert(
208 CheetahString::from_static_str(CreateTopicRequestHeader::PERM),
209 CheetahString::from("6"),
210 );
211 map.insert(
212 CheetahString::from_static_str(CreateTopicRequestHeader::TOPIC_FILTER_TYPE),
213 CheetahString::from("SINGLE_TAG"),
214 );
215 map.insert(
216 CheetahString::from_static_str(CreateTopicRequestHeader::TOPIC_SYS_FLAG),
217 CheetahString::from("1"),
218 );
219 map.insert(
220 CheetahString::from_static_str(CreateTopicRequestHeader::ORDER),
221 CheetahString::from("true"),
222 );
223 map.insert(
224 CheetahString::from_static_str(CreateTopicRequestHeader::ATTRIBUTES),
225 CheetahString::from("attributes"),
226 );
227 map.insert(
228 CheetahString::from_static_str(CreateTopicRequestHeader::FORCE),
229 CheetahString::from("true"),
230 );
231
232 let header = <CreateTopicRequestHeader as FromMap>::from(&map).unwrap();
233 assert_eq!(header.topic, CheetahString::from("test_topic"));
234 assert_eq!(header.default_topic, CheetahString::from("default_topic"));
235 assert_eq!(header.read_queue_nums, 4);
236 assert_eq!(header.write_queue_nums, 4);
237 assert_eq!(header.perm, 6);
238 assert_eq!(header.topic_filter_type, CheetahString::from("SINGLE_TAG"));
239 assert_eq!(header.topic_sys_flag, Some(1));
240 assert!(header.order);
241 assert_eq!(header.attributes, Some(CheetahString::from("attributes")));
242 assert_eq!(header.force, Some(true));
243 }
244
245 #[test]
246 fn create_topic_request_header_from_map_missing_optional_fields() {
247 let mut map = HashMap::new();
248 map.insert(
249 CheetahString::from_static_str(CreateTopicRequestHeader::TOPIC),
250 CheetahString::from("test_topic"),
251 );
252 map.insert(
253 CheetahString::from_static_str(CreateTopicRequestHeader::DEFAULT_TOPIC),
254 CheetahString::from("default_topic"),
255 );
256 map.insert(
257 CheetahString::from_static_str(CreateTopicRequestHeader::READ_QUEUE_NUMS),
258 CheetahString::from("4"),
259 );
260 map.insert(
261 CheetahString::from_static_str(CreateTopicRequestHeader::WRITE_QUEUE_NUMS),
262 CheetahString::from("4"),
263 );
264 map.insert(
265 CheetahString::from_static_str(CreateTopicRequestHeader::PERM),
266 CheetahString::from("6"),
267 );
268 map.insert(
269 CheetahString::from_static_str(CreateTopicRequestHeader::TOPIC_FILTER_TYPE),
270 CheetahString::from("SINGLE_TAG"),
271 );
272 map.insert(
273 CheetahString::from_static_str(CreateTopicRequestHeader::ORDER),
274 CheetahString::from("true"),
275 );
276
277 let header = <CreateTopicRequestHeader as FromMap>::from(&map).unwrap();
278 assert_eq!(header.topic, CheetahString::from("test_topic"));
279 assert_eq!(header.default_topic, CheetahString::from("default_topic"));
280 assert_eq!(header.read_queue_nums, 4);
281 assert_eq!(header.write_queue_nums, 4);
282 assert_eq!(header.perm, 6);
283 assert_eq!(header.topic_filter_type, CheetahString::from("SINGLE_TAG"));
284 assert_eq!(header.topic_sys_flag, None);
285 assert!(header.order);
286 assert_eq!(header.attributes, None);
287 assert_eq!(header.force, Some(false));
288 }
289
290 #[test]
291 fn create_topic_request_header_validates_java_filter_types() {
292 let base = HashMap::from([
293 ("topic".into(), "TopicA".into()),
294 ("defaultTopic".into(), "TBW102".into()),
295 ("readQueueNums".into(), "4".into()),
296 ("writeQueueNums".into(), "4".into()),
297 ("perm".into(), "6".into()),
298 ("order".into(), "false".into()),
299 ]);
300 for value in ["SINGLE_TAG", "MULTI_TAG"] {
301 let mut fields = base.clone();
302 fields.insert("topicFilterType".into(), value.into());
303 assert!(<CreateTopicRequestHeader as FromMap>::from(&fields).is_ok());
304 }
305 let mut fields = base;
306 fields.insert("topicFilterType".into(), "SQL92".into());
307 assert!(<CreateTopicRequestHeader as FromMap>::from(&fields).is_err());
308 }
309}