1pub const SEPARATOR: char = '$';
16pub const LITE_TOPIC_PREFIX: &str = "%LMQ%$";
17
18pub fn to_lmq_name(parent_topic: &str, lite_topic: &str) -> Option<String> {
19 if parent_topic.is_empty() || lite_topic.is_empty() {
20 return None;
21 }
22 Some(format!("{}{}${}", LITE_TOPIC_PREFIX, parent_topic, lite_topic))
23}
24
25pub fn is_lite_topic_queue(lmq_name: &str) -> bool {
26 !lmq_name.is_empty() && lmq_name.starts_with(LITE_TOPIC_PREFIX)
27}
28
29pub fn get_parent_topic(lmq_name: &str) -> Option<String> {
30 if !is_lite_topic_queue(lmq_name) {
31 return None;
32 }
33
34 let remaining = &lmq_name[LITE_TOPIC_PREFIX.len()..];
35 let index = remaining.find(SEPARATOR)?;
36
37 if index == 0 || index == remaining.len() - 1 {
38 return None;
39 }
40
41 if remaining[index + 1..].contains(SEPARATOR) {
43 return None;
44 }
45
46 Some(remaining[..index].to_string())
47}
48
49pub fn get_lite_topic(lmq_name: &str) -> Option<String> {
50 if !is_lite_topic_queue(lmq_name) {
51 return None;
52 }
53
54 let remaining = &lmq_name[LITE_TOPIC_PREFIX.len()..];
55 let index = remaining.find(SEPARATOR)?;
56
57 if index == 0 || index == remaining.len() - 1 {
58 return None;
59 }
60
61 if remaining[index + 1..].contains(SEPARATOR) {
63 return None;
64 }
65
66 Some(remaining[index + 1..].to_string())
67}
68
69pub fn get_parent_and_lite_topic(lmq_name: &str) -> Option<(String, String)> {
70 if !lmq_name.starts_with(LITE_TOPIC_PREFIX) {
71 return None;
72 }
73
74 let remaining = &lmq_name[LITE_TOPIC_PREFIX.len()..];
75 let parts: Vec<&str> = remaining.split(SEPARATOR).collect();
76
77 if parts.len() != 2 {
78 return None;
79 }
80
81 if parts[0].is_empty() || parts[1].is_empty() {
82 return None;
83 }
84
85 Some((parts[0].to_string(), parts[1].to_string()))
86}
87
88pub fn belongs_to(lmq_name: &str, parent_topic: &str) -> bool {
89 let prefix = format!("{}{}{}", LITE_TOPIC_PREFIX, parent_topic, SEPARATOR);
90 lmq_name.starts_with(&prefix)
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 #[test]
98 fn test_to_lmq_name() {
99 let result = to_lmq_name("parent", "child");
100 assert!(result.is_some());
101 let lmq_name = result.unwrap();
102 assert!(lmq_name.contains("parent"));
103 assert!(lmq_name.contains("child"));
104 assert!(lmq_name.contains("$"));
105 }
106
107 #[test]
108 fn test_to_lmq_name_empty_parent() {
109 let result = to_lmq_name("", "child");
110 assert!(result.is_none());
111 }
112
113 #[test]
114 fn test_to_lmq_name_empty_lite_topic() {
115 let result = to_lmq_name("parent", "");
116 assert!(result.is_none());
117 }
118
119 #[test]
120 fn test_is_lite_topic_queue_valid() {
121 let lmq_name = to_lmq_name("parent", "child").unwrap();
122 assert!(is_lite_topic_queue(&lmq_name));
123 }
124
125 #[test]
126 fn test_is_lite_topic_queue_invalid() {
127 assert!(!is_lite_topic_queue("regular_topic"));
128 assert!(!is_lite_topic_queue(""));
129 }
130
131 #[test]
132 fn test_get_parent_topic() {
133 let lmq_name = to_lmq_name("my_parent", "my_child").unwrap();
134 let parent = get_parent_topic(&lmq_name);
135 assert_eq!(parent, Some("my_parent".to_string()));
136 }
137
138 #[test]
139 fn test_get_parent_topic_not_lite() {
140 let parent = get_parent_topic("regular_topic");
141 assert!(parent.is_none());
142 }
143
144 #[test]
145 fn test_get_lite_topic() {
146 let lmq_name = to_lmq_name("my_parent", "my_child").unwrap();
147 let lite = get_lite_topic(&lmq_name);
148 assert_eq!(lite, Some("my_child".to_string()));
149 }
150
151 #[test]
152 fn test_get_lite_topic_not_lite() {
153 let lite = get_lite_topic("regular_topic");
154 assert!(lite.is_none());
155 }
156
157 #[test]
158 fn test_get_parent_and_lite_topic() {
159 let lmq_name = to_lmq_name("parent_test", "child_test").unwrap();
160 let pair = get_parent_and_lite_topic(&lmq_name);
161 assert!(pair.is_some());
162 let (parent, child) = pair.unwrap();
163 assert_eq!(parent, "parent_test");
164 assert_eq!(child, "child_test");
165 }
166
167 #[test]
168 fn test_get_parent_and_lite_topic_invalid() {
169 let pair = get_parent_and_lite_topic("not_a_lite_topic");
170 assert!(pair.is_none());
171 }
172
173 #[test]
174 fn test_belongs_to_true() {
175 let lmq_name = to_lmq_name("my_parent", "my_child").unwrap();
176 assert!(belongs_to(&lmq_name, "my_parent"));
177 }
178
179 #[test]
180 fn test_belongs_to_false() {
181 let lmq_name = to_lmq_name("parent1", "child1").unwrap();
182 assert!(!belongs_to(&lmq_name, "parent2"));
183 }
184
185 #[test]
186 fn test_belongs_to_invalid() {
187 assert!(!belongs_to("regular_topic", "parent"));
188 }
189
190 #[test]
191 fn test_roundtrip() {
192 let original_parent = "test_parent";
193 let original_child = "test_child";
194
195 let lmq_name = to_lmq_name(original_parent, original_child).unwrap();
196
197 let extracted_parent = get_parent_topic(&lmq_name).unwrap();
198 let extracted_child = get_lite_topic(&lmq_name).unwrap();
199
200 assert_eq!(extracted_parent, original_parent);
201 assert_eq!(extracted_child, original_child);
202 }
203
204 #[test]
205 fn test_special_characters_in_names() {
206 let parent = "parent-123";
207 let child = "child_456";
208
209 let lmq_name = to_lmq_name(parent, child).unwrap();
210
211 assert_eq!(get_parent_topic(&lmq_name), Some(parent.to_string()));
212 assert_eq!(get_lite_topic(&lmq_name), Some(child.to_string()));
213 }
214
215 #[test]
216 fn test_multiple_separators_in_name() {
217 let malformed = format!("{}parent{}child{}", LITE_TOPIC_PREFIX, SEPARATOR, SEPARATOR);
219
220 assert!(is_lite_topic_queue(&malformed));
221 assert!(get_parent_topic(&malformed).is_none());
223 assert!(get_lite_topic(&malformed).is_none());
224 }
225}