rocketmq_remoting/protocol/
namespace_util.rs1use rocketmq_common::common::mix_all;
18use rocketmq_common::common::topic::TopicValidator;
19
20pub struct NamespaceUtil;
21
22impl NamespaceUtil {
23 pub const DLQ_PREFIX_LENGTH: usize = mix_all::DLQ_GROUP_TOPIC_PREFIX.len();
24 pub const NAMESPACE_SEPARATOR: char = '%';
25 pub const RETRY_PREFIX_LENGTH: usize = mix_all::RETRY_GROUP_TOPIC_PREFIX.len();
26 pub const STRING_BLANK: &'static str = "";
27
28 pub fn without_namespace(resource_with_namespace: &str) -> String {
29 if resource_with_namespace.is_empty()
30 || NamespaceUtil::is_system_resource(resource_with_namespace)
31 {
32 return resource_with_namespace.to_string();
33 }
34
35 let mut string_builder = String::new();
36 if NamespaceUtil::is_retry_topic(resource_with_namespace) {
37 string_builder.push_str(mix_all::RETRY_GROUP_TOPIC_PREFIX);
38 }
39 if NamespaceUtil::is_dlq_topic(resource_with_namespace) {
40 string_builder.push_str(mix_all::DLQ_GROUP_TOPIC_PREFIX);
41 }
42
43 if let Some(index) = NamespaceUtil::without_retry_and_dlq(resource_with_namespace)
44 .find(NamespaceUtil::NAMESPACE_SEPARATOR)
45 {
46 let resource_without_namespace =
47 &NamespaceUtil::without_retry_and_dlq(resource_with_namespace)[index + 1..];
48 return string_builder + resource_without_namespace;
49 }
50
51 resource_with_namespace.to_string()
52 }
53
54 pub fn without_namespace_with_namespace(
55 resource_with_namespace: &str,
56 namespace: &str,
57 ) -> String {
58 if resource_with_namespace.is_empty() || namespace.is_empty() {
59 return resource_with_namespace.to_string();
60 }
61
62 let resource_without_retry_and_dlq =
63 NamespaceUtil::without_retry_and_dlq(resource_with_namespace);
64 if resource_without_retry_and_dlq.starts_with(&format!(
65 "{}{}",
66 namespace,
67 NamespaceUtil::NAMESPACE_SEPARATOR
68 )) {
69 return NamespaceUtil::without_namespace(resource_with_namespace);
70 }
71
72 resource_with_namespace.to_string()
73 }
74
75 pub fn wrap_namespace(namespace: &str, resource_without_namespace: &str) -> String {
76 if namespace.is_empty() || resource_without_namespace.is_empty() {
77 return resource_without_namespace.to_string();
78 }
79
80 if NamespaceUtil::is_system_resource(resource_without_namespace)
81 || NamespaceUtil::is_already_with_namespace(resource_without_namespace, namespace)
82 {
83 return resource_without_namespace.to_string();
84 }
85
86 let mut string_builder = String::new();
87
88 if NamespaceUtil::is_retry_topic(resource_without_namespace) {
89 string_builder.push_str(mix_all::RETRY_GROUP_TOPIC_PREFIX);
90 }
91
92 if NamespaceUtil::is_dlq_topic(resource_without_namespace) {
93 string_builder.push_str(mix_all::DLQ_GROUP_TOPIC_PREFIX);
94 }
95 let resource_without_retry_and_dlq =
96 NamespaceUtil::without_retry_and_dlq(resource_without_namespace);
97 string_builder
98 + namespace
99 + &NamespaceUtil::NAMESPACE_SEPARATOR.to_string()
100 + resource_without_retry_and_dlq
101 }
102
103 pub fn is_already_with_namespace(resource: &str, namespace: &str) -> bool {
104 if namespace.is_empty()
105 || resource.is_empty()
106 || NamespaceUtil::is_system_resource(resource)
107 {
108 return false;
109 }
110
111 let resource_without_retry_and_dlq = NamespaceUtil::without_retry_and_dlq(resource);
112
113 resource_without_retry_and_dlq.starts_with(&format!(
114 "{}{}",
115 namespace,
116 NamespaceUtil::NAMESPACE_SEPARATOR
117 ))
118 }
119
120 pub fn wrap_namespace_and_retry(namespace: &str, consumer_group: &str) -> Option<String> {
121 if consumer_group.is_empty() {
122 return None;
123 }
124
125 Some(
126 mix_all::RETRY_GROUP_TOPIC_PREFIX.to_string()
127 + &NamespaceUtil::wrap_namespace(namespace, consumer_group),
128 )
129 }
130
131 pub fn get_namespace_from_resource(resource: &str) -> String {
132 if resource.is_empty() || NamespaceUtil::is_system_resource(resource) {
133 return NamespaceUtil::STRING_BLANK.to_string();
134 }
135 let resource_without_retry_and_dlq = NamespaceUtil::without_retry_and_dlq(resource);
136 if let Some(index) = resource_without_retry_and_dlq.find(NamespaceUtil::NAMESPACE_SEPARATOR)
137 {
138 return resource_without_retry_and_dlq[..index].to_string();
139 }
140
141 NamespaceUtil::STRING_BLANK.to_string()
142 }
143
144 fn without_retry_and_dlq(original_resource: &str) -> &str {
145 if original_resource.is_empty() {
146 return NamespaceUtil::STRING_BLANK;
147 }
148 if NamespaceUtil::is_retry_topic(original_resource) {
149 return &original_resource[NamespaceUtil::RETRY_PREFIX_LENGTH..];
150 }
151
152 if NamespaceUtil::is_dlq_topic(original_resource) {
153 return &original_resource[NamespaceUtil::DLQ_PREFIX_LENGTH..];
154 }
155
156 original_resource
157 }
158
159 fn is_system_resource(resource: &str) -> bool {
160 if resource.is_empty() {
161 return false;
162 }
163 TopicValidator::is_system_topic(resource) || mix_all::is_sys_consumer_group(resource)
164 }
165
166 #[inline]
167 pub fn is_retry_topic(resource: &str) -> bool {
168 !resource.is_empty() && resource.starts_with(mix_all::RETRY_GROUP_TOPIC_PREFIX)
169 }
170
171 fn is_dlq_topic(resource: &str) -> bool {
172 !resource.is_empty() && resource.starts_with(mix_all::DLQ_GROUP_TOPIC_PREFIX)
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 #[test]
181 fn without_namespace_returns_original_when_empty() {
182 assert_eq!(NamespaceUtil::without_namespace(""), "");
183 }
184
185 #[test]
186 fn without_namespace_returns_original_when_system_resource() {
187 assert_eq!(NamespaceUtil::without_namespace("SYS_TOPIC"), "SYS_TOPIC");
188 }
189
190 #[test]
191 fn without_namespace_removes_namespace() {
192 assert_eq!(
193 NamespaceUtil::without_namespace("my_namespace%my_resource"),
194 "my_resource"
195 );
196 }
197
198 #[test]
199 fn without_namespace_with_namespace_returns_original_when_empty() {
200 assert_eq!(
201 NamespaceUtil::without_namespace_with_namespace("", "my_namespace"),
202 ""
203 );
204 }
205
206 #[test]
207 fn without_namespace_with_namespace_removes_namespace() {
208 assert_eq!(
209 NamespaceUtil::without_namespace_with_namespace(
210 "my_namespace%my_resource",
211 "my_namespace"
212 ),
213 "my_resource"
214 );
215 }
216
217 #[test]
218 fn wrap_namespace_returns_original_when_empty() {
219 assert_eq!(NamespaceUtil::wrap_namespace("my_namespace", ""), "");
220 }
221
222 #[test]
223 fn wrap_namespace_adds_namespace() {
224 assert_eq!(
225 NamespaceUtil::wrap_namespace("my_namespace", "my_resource"),
226 "my_namespace%my_resource"
227 );
228 }
229
230 #[test]
231 fn is_already_with_namespace_returns_false_when_empty() {
232 assert!(!NamespaceUtil::is_already_with_namespace(
233 "",
234 "my_namespace"
235 ));
236 }
237
238 #[test]
239 fn is_already_with_namespace_returns_true_when_with_namespace() {
240 assert!(NamespaceUtil::is_already_with_namespace(
241 "my_namespace%my_resource",
242 "my_namespace"
243 ));
244 }
245
246 #[test]
247 fn wrap_namespace_and_retry_returns_none_when_empty() {
248 assert_eq!(
249 NamespaceUtil::wrap_namespace_and_retry("my_namespace", ""),
250 None
251 );
252 }
253
254 #[test]
255 fn wrap_namespace_and_retry_adds_namespace_and_retry() {
256 assert_eq!(
257 NamespaceUtil::wrap_namespace_and_retry("my_namespace", "my_group"),
258 Some("%RETRY%my_namespace%my_group".to_string())
259 );
260 }
261
262 #[test]
263 fn get_namespace_from_resource_returns_blank_when_empty() {
264 assert_eq!(NamespaceUtil::get_namespace_from_resource(""), "");
265 }
266
267 #[test]
268 fn get_namespace_from_resource_returns_namespace() {
269 assert_eq!(
270 NamespaceUtil::get_namespace_from_resource("my_namespace%my_resource"),
271 "my_namespace"
272 );
273 }
274
275 #[test]
276 fn without_retry_and_dlq_returns_original_when_empty() {
277 assert_eq!(NamespaceUtil::without_retry_and_dlq(""), "");
278 }
279
280 #[test]
281 fn without_retry_and_dlq_removes_retry_and_dlq() {
282 assert_eq!(
283 NamespaceUtil::without_retry_and_dlq("RETRY_GROUP_TOPIC_PREFIXmy_resource"),
284 "RETRY_GROUP_TOPIC_PREFIXmy_resource"
285 );
286 assert_eq!(
287 NamespaceUtil::without_retry_and_dlq("DLQ_GROUP_TOPIC_PREFIXmy_resource"),
288 "DLQ_GROUP_TOPIC_PREFIXmy_resource"
289 );
290 }
291
292 #[test]
293 fn is_system_resource_returns_false_when_empty() {
294 assert!(!NamespaceUtil::is_system_resource(""));
295 }
296
297 #[test]
298 fn is_system_resource_returns_true_when_system_resource() {
299 assert!(NamespaceUtil::is_system_resource("CID_RMQ_SYS_"));
300 assert!(NamespaceUtil::is_system_resource("TBW102"));
301 }
302
303 #[test]
304 fn is_retry_topic_returns_false_when_empty() {
305 assert!(!NamespaceUtil::is_retry_topic(""));
306 }
307
308 #[test]
309 fn is_retry_topic_returns_true_when_retry_topic() {
310 assert!(!NamespaceUtil::is_retry_topic(
311 "RETRY_GROUP_TOPIC_PREFIXmy_topic"
312 ));
313 assert!(NamespaceUtil::is_retry_topic(
314 "%RETRY%RETRY_GROUP_TOPIC_PREFIXmy_topic"
315 ));
316 }
317
318 #[test]
319 fn is_dlq_topic_returns_false_when_empty() {
320 assert!(!NamespaceUtil::is_dlq_topic(""));
321 }
322
323 #[test]
324 fn is_dlq_topic_returns_true_when_dlq_topic() {
325 assert!(!NamespaceUtil::is_dlq_topic(
326 "DLQ_GROUP_TOPIC_PREFIXmy_topic"
327 ));
328 assert!(NamespaceUtil::is_dlq_topic(
329 "%DLQ%DLQ_GROUP_TOPIC_PREFIXmy_topic"
330 ));
331 }
332}