lightstreamer_rs/utils/
util.rs1use std::sync::Arc;
2use tokio::sync::Notify;
3#[cfg(windows)]
4use tracing::error;
5use tracing::info;
6
7pub fn clean_message(text: &str) -> String {
9 let mut result = String::new();
10 let mut inside_braces = false;
11
12 for part in text.split_inclusive(&['{', '}']) {
13 if part.starts_with('{') && part.ends_with('}') {
14 inside_braces = true;
16 result.push_str(part);
17 } else if inside_braces {
18 inside_braces = false;
20 result.push_str(part);
21 } else {
22 let chars_to_replace = ['\n', '\r']; result.push_str(&part.replace(chars_to_replace, "").to_lowercase());
25 }
26 }
27
28 result
29}
30
31pub fn parse_arguments(input: &str) -> Vec<&str> {
61 let mut arguments = Vec::new();
62 let mut start = 0;
63 let mut in_brackets = 0; for (i, c) in input.char_indices() {
66 match c {
67 '{' => in_brackets += 1,
68 '}' => in_brackets -= 1,
69 ',' if in_brackets == 0 => {
70 let slice = &input[start..i].trim();
72 if !slice.is_empty() {
73 arguments.push(*slice); }
75 start = i + 1;
76 }
77 _ => {}
78 }
79 }
80
81 if start < input.len() {
83 let slice = &input[start..].trim();
84 if !slice.is_empty() {
85 arguments.push(*slice); }
87 }
88
89 arguments
90}
91
92pub async fn setup_signal_hook(shutdown_signal: Arc<Notify>) {
105 #[cfg(unix)]
106 {
107 use tokio::signal::unix::{SignalKind, signal};
108 let mut sigint = signal(SignalKind::interrupt()).expect("Failed to create SIGINT handler");
109 let mut sigterm =
110 signal(SignalKind::terminate()).expect("Failed to create SIGTERM handler");
111
112 tokio::spawn(async move {
113 tokio::select! {
114 _ = sigint.recv() => {
115 info!("Received SIGINT signal");
116 shutdown_signal.notify_one();
117 }
118 _ = sigterm.recv() => {
119 info!("Received SIGTERM signal");
120 shutdown_signal.notify_one();
121 }
122 }
123 });
124 }
125
126 #[cfg(windows)]
127 {
128 use tokio::signal;
129 tokio::spawn(async move {
130 match signal::ctrl_c().await {
131 Ok(()) => {
132 info!("Received Ctrl+C signal");
133 shutdown_signal.notify_one();
134 }
135 Err(err) => {
136 error!("Failed to listen for Ctrl+C: {}", err);
137 }
138 }
139 });
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 mod clean_message_tests {
148 use super::*;
149
150 #[test]
151 fn test_clean_message_basic() {
152 let text = "Hello\nWorld";
153 let result = clean_message(text);
154 assert_eq!(result, "helloworld");
155 }
156
157 #[test]
158 fn test_clean_message_with_partial_braces() {
159 let text = "{partial brace content} followed by text";
161 let result = clean_message(text);
162 assert_eq!(result, "{partial brace content} followed by text");
163 }
164
165 #[test]
166 fn test_clean_message_with_ending_brace() {
167 let text = "text followed by {partial brace content}";
169 let result = clean_message(text);
170 assert_eq!(result, "text followed by {partial brace content}");
171 }
172
173 #[test]
174 fn test_clean_message_with_carriage_return() {
175 let text = "Hello\r\nWorld";
176 let result = clean_message(text);
177 assert_eq!(result, "helloworld");
178 }
179
180 #[test]
181 fn test_clean_message_lowercase_conversion() {
182 let text = "Hello WORLD";
183 let result = clean_message(text);
184 assert_eq!(result, "hello world");
185 }
186
187 #[test]
188 fn test_clean_message_empty_string() {
189 let text = "";
190 let result = clean_message(text);
191 assert_eq!(result, "");
192 }
193
194 #[test]
195 fn test_clean_message_preserve_braces_content() {
196 let text = "Message with {Preserved\nContent} and not preserved\nContent";
197 let result = clean_message(text);
198 assert_eq!(
199 result,
200 "message with {preservedcontent} and not preservedcontent"
201 );
202 }
203
204 #[test]
205 fn test_clean_message_nested_braces() {
206 let text = "Message with {Outer{Inner\nContent}Outer} and regular\nContent";
207 let result = clean_message(text);
208 assert_eq!(
209 result,
210 "message with {outer{innercontent}outer} and regularcontent"
211 );
212 }
213
214 #[test]
215 fn test_clean_message_unbalanced_braces() {
216 let text = "Message with {Unbalanced and regular\nContent";
217 let result = clean_message(text);
218 assert_eq!(result, "message with {unbalanced and regularcontent");
219 }
220
221 #[test]
222 fn test_clean_message_protocol_example() {
223 let text = "CONOK,S8f4aec42c3c14ad0,50000,5000,*\r\n";
225 let result = clean_message(text);
226 assert_eq!(result, "conok,s8f4aec42c3c14ad0,50000,5000,*");
227
228 let text = "PROBE\r\n";
229 let result = clean_message(text);
230 assert_eq!(result, "probe");
231 }
232 }
233
234 mod parse_arguments_tests {
235 use super::*;
236
237 #[test]
238 fn test_parse_arguments_basic() {
239 let input = "arg1,arg2,arg3";
240 let result = parse_arguments(input);
241 assert_eq!(result, vec!["arg1", "arg2", "arg3"]);
242 }
243
244 #[test]
245 fn test_parse_arguments_empty_string() {
246 let input = "";
247 let result = parse_arguments(input);
248 assert_eq!(result, Vec::<&str>::new());
249 }
250
251 #[test]
252 fn test_parse_arguments_single_argument() {
253 let input = "arg1";
254 let result = parse_arguments(input);
255 assert_eq!(result, vec!["arg1"]);
256 }
257
258 #[test]
259 fn test_parse_arguments_with_whitespace() {
260 let input = " arg1 , arg2 , arg3 ";
261 let result = parse_arguments(input);
262 assert_eq!(result, vec!["arg1", "arg2", "arg3"]);
263 }
264
265 #[test]
266 fn test_parse_arguments_empty_arguments() {
267 let input = "arg1,,arg3";
268 let result = parse_arguments(input);
269 assert_eq!(result, vec!["arg1", "arg3"]);
270 }
271
272 #[test]
273 fn test_parse_arguments_with_braces() {
274 let input = "arg1,{inner1,inner2},arg3";
275 let result = parse_arguments(input);
276 assert_eq!(result, vec!["arg1", "{inner1,inner2}", "arg3"]);
277 }
278
279 #[test]
280 fn test_parse_arguments_nested_braces() {
281 let input = "arg1,{outer{inner1,inner2}outer},arg3";
282 let result = parse_arguments(input);
283 assert_eq!(result, vec!["arg1", "{outer{inner1,inner2}outer}", "arg3"]);
284 }
285
286 #[test]
287 fn test_parse_arguments_unbalanced_braces() {
288 let input = "arg1,{unbalanced,arg3";
289 let result = parse_arguments(input);
290 assert_eq!(result, vec!["arg1", "{unbalanced,arg3"]);
292 }
293
294 #[test]
295 fn test_parse_arguments_protocol_examples() {
296 let input = "CONOK,S8f4aec42c3c14ad0,50000,5000,*";
298 let result = parse_arguments(input);
299 assert_eq!(
300 result,
301 vec!["CONOK", "S8f4aec42c3c14ad0", "50000", "5000", "*"]
302 );
303
304 let input = "u,1,1,a|b|c";
305 let result = parse_arguments(input);
306 assert_eq!(result, vec!["u", "1", "1", "a|b|c"]);
307 }
308 }
309}