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