use std::sync::Arc;
use tokio::sync::Notify;
#[cfg(windows)]
use tracing::error;
use tracing::info;
pub fn clean_message(text: &str) -> String {
let chars_to_replace = ['\n', '\r'];
let stripped = text.replace(chars_to_replace, "");
let mut in_brackets: usize = 0;
let mut first_comma: Option<usize> = None;
for (i, c) in stripped.char_indices() {
match c {
'{' => in_brackets += 1,
'}' => in_brackets = in_brackets.saturating_sub(1),
',' if in_brackets == 0 => {
first_comma = Some(i);
break;
}
_ => {}
}
}
match first_comma {
Some(pos) => {
let mut result = String::with_capacity(stripped.len());
result.push_str(&stripped[..pos].to_lowercase());
result.push_str(&stripped[pos..]);
result
}
None => {
stripped.to_lowercase()
}
}
}
pub fn parse_arguments(input: &str) -> Vec<&str> {
let mut arguments = Vec::new();
let mut start = 0;
let mut in_brackets = 0;
for (i, c) in input.char_indices() {
match c {
'{' => in_brackets += 1,
'}' => in_brackets -= 1,
',' if in_brackets == 0 => {
let slice = &input[start..i].trim();
if !slice.is_empty() {
arguments.push(*slice); }
start = i + 1;
}
_ => {}
}
}
if start < input.len() {
let slice = &input[start..].trim();
if !slice.is_empty() {
arguments.push(*slice); }
}
arguments
}
pub async fn setup_signal_hook(shutdown_signal: Arc<Notify>) {
#[cfg(unix)]
{
use tokio::signal::unix::{SignalKind, signal};
let mut sigint = signal(SignalKind::interrupt()).expect("Failed to create SIGINT handler");
let mut sigterm =
signal(SignalKind::terminate()).expect("Failed to create SIGTERM handler");
tokio::spawn(async move {
tokio::select! {
_ = sigint.recv() => {
info!("Received SIGINT signal");
shutdown_signal.notify_one();
}
_ = sigterm.recv() => {
info!("Received SIGTERM signal");
shutdown_signal.notify_one();
}
}
});
}
#[cfg(windows)]
{
use tokio::signal;
tokio::spawn(async move {
match signal::ctrl_c().await {
Ok(()) => {
info!("Received Ctrl+C signal");
shutdown_signal.notify_one();
}
Err(err) => {
error!("Failed to listen for Ctrl+C: {}", err);
}
}
});
}
}
#[cfg(test)]
mod tests {
use super::*;
mod clean_message_tests {
use super::*;
#[test]
fn test_clean_message_basic() {
let text = "Hello\nWorld";
let result = clean_message(text);
assert_eq!(result, "helloworld");
}
#[test]
fn test_clean_message_with_partial_braces() {
let text = "{partial brace content} followed by text";
let result = clean_message(text);
assert_eq!(result, "{partial brace content} followed by text");
}
#[test]
fn test_clean_message_with_ending_brace() {
let text = "text followed by {partial brace content}";
let result = clean_message(text);
assert_eq!(result, "text followed by {partial brace content}");
}
#[test]
fn test_clean_message_with_carriage_return() {
let text = "Hello\r\nWorld";
let result = clean_message(text);
assert_eq!(result, "helloworld");
}
#[test]
fn test_clean_message_lowercase_conversion() {
let text = "Hello WORLD";
let result = clean_message(text);
assert_eq!(result, "hello world");
}
#[test]
fn test_clean_message_empty_string() {
let text = "";
let result = clean_message(text);
assert_eq!(result, "");
}
#[test]
fn test_clean_message_preserve_braces_content() {
let text = "Message with {Preserved\nContent} and not preserved\nContent";
let result = clean_message(text);
assert_eq!(
result,
"message with {preservedcontent} and not preservedcontent"
);
}
#[test]
fn test_clean_message_nested_braces() {
let text = "Message with {Outer{Inner\nContent}Outer} and regular\nContent";
let result = clean_message(text);
assert_eq!(
result,
"message with {outer{innercontent}outer} and regularcontent"
);
}
#[test]
fn test_clean_message_unbalanced_braces() {
let text = "Message with {Unbalanced and regular\nContent";
let result = clean_message(text);
assert_eq!(result, "message with {unbalanced and regularcontent");
}
#[test]
fn test_clean_message_protocol_example() {
let text = "CONOK,S8f4aec42c3c14ad0,50000,5000,*\r\n";
let result = clean_message(text);
assert_eq!(result, "conok,S8f4aec42c3c14ad0,50000,5000,*");
let text = "PROBE\r\n";
let result = clean_message(text);
assert_eq!(result, "probe");
}
#[test]
fn test_clean_message_update_preserves_field_values() {
let text = "U,1,1,DEAL|1.32|#|^P\r\n";
let result = clean_message(text);
assert_eq!(result, "u,1,1,DEAL|1.32|#|^P");
}
#[test]
fn test_clean_message_update_preserves_caret_commands() {
let text = "U,1,1,^5|CLOSED|-0.5\r\n";
let result = clean_message(text);
assert_eq!(result, "u,1,1,^5|CLOSED|-0.5");
}
#[test]
fn test_clean_message_reqerr_preserves_details() {
let text = "REQERR,21,InvalidField\r\n";
let result = clean_message(text);
assert_eq!(result, "reqerr,21,InvalidField");
}
}
mod parse_arguments_tests {
use super::*;
#[test]
fn test_parse_arguments_basic() {
let input = "arg1,arg2,arg3";
let result = parse_arguments(input);
assert_eq!(result, vec!["arg1", "arg2", "arg3"]);
}
#[test]
fn test_parse_arguments_empty_string() {
let input = "";
let result = parse_arguments(input);
assert_eq!(result, Vec::<&str>::new());
}
#[test]
fn test_parse_arguments_single_argument() {
let input = "arg1";
let result = parse_arguments(input);
assert_eq!(result, vec!["arg1"]);
}
#[test]
fn test_parse_arguments_with_whitespace() {
let input = " arg1 , arg2 , arg3 ";
let result = parse_arguments(input);
assert_eq!(result, vec!["arg1", "arg2", "arg3"]);
}
#[test]
fn test_parse_arguments_empty_arguments() {
let input = "arg1,,arg3";
let result = parse_arguments(input);
assert_eq!(result, vec!["arg1", "arg3"]);
}
#[test]
fn test_parse_arguments_with_braces() {
let input = "arg1,{inner1,inner2},arg3";
let result = parse_arguments(input);
assert_eq!(result, vec!["arg1", "{inner1,inner2}", "arg3"]);
}
#[test]
fn test_parse_arguments_nested_braces() {
let input = "arg1,{outer{inner1,inner2}outer},arg3";
let result = parse_arguments(input);
assert_eq!(result, vec!["arg1", "{outer{inner1,inner2}outer}", "arg3"]);
}
#[test]
fn test_parse_arguments_unbalanced_braces() {
let input = "arg1,{unbalanced,arg3";
let result = parse_arguments(input);
assert_eq!(result, vec!["arg1", "{unbalanced,arg3"]);
}
#[test]
fn test_parse_arguments_protocol_examples() {
let input = "CONOK,S8f4aec42c3c14ad0,50000,5000,*";
let result = parse_arguments(input);
assert_eq!(
result,
vec!["CONOK", "S8f4aec42c3c14ad0", "50000", "5000", "*"]
);
let input = "u,1,1,a|b|c";
let result = parse_arguments(input);
assert_eq!(result, vec!["u", "1", "1", "a|b|c"]);
}
}
}