use anyhow::{Context, Result};
use oxur_repl::server::ReplServer;
use std::time::Duration;
use tokio::io::AsyncWriteExt;
use tokio::net::TcpStream;
use tokio::signal;
pub async fn run(addr: &str, ack_port: Option<u16>) -> Result<()> {
let mut server = ReplServer::new(addr).with_shutdown_timeout(Duration::from_secs(30));
let shutdown_handle = server.shutdown_handle();
if let Some(port) = ack_port {
send_ack(addr, port).await?;
}
tokio::spawn(async move {
wait_for_shutdown_signal().await;
eprintln!("\n[INFO] Received shutdown signal, stopping server...");
shutdown_handle.shutdown();
});
server.start().await.context("Server error")?;
Ok(())
}
async fn wait_for_shutdown_signal() {
let _ = signal::ctrl_c().await;
}
async fn send_ack(our_addr: &str, ack_port: u16) -> Result<()> {
let ack_addr = format!("127.0.0.1:{}", ack_port);
eprintln!("[INFO] Sending ACK to {}", ack_addr);
match TcpStream::connect(&ack_addr).await {
Ok(mut stream) => {
let ack_msg = format!("{{:port {}}}\n", extract_port(our_addr));
stream.write_all(ack_msg.as_bytes()).await.context("Failed to send ACK")?;
stream.shutdown().await.context("Failed to close ACK connection")?;
eprintln!("[INFO] ACK sent successfully");
Ok(())
}
Err(e) => {
eprintln!("[WARN] Failed to send ACK to {}: {}", ack_addr, e);
Ok(())
}
}
}
fn extract_port(addr: &str) -> &str {
addr.rsplit(':').next().unwrap_or("0")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_extract_port() {
assert_eq!(extract_port("127.0.0.1:5099"), "5099");
assert_eq!(extract_port("localhost:9000"), "9000");
assert_eq!(extract_port("[::1]:8080"), "8080");
}
}