mod stats; mod telemetry; use stats::ReceiveStats; use telemetry::TelemetryFrame; use std::io::Write; const DEFAULT_BIND_ADDR: &str = "127.0.0.1:5005"; const BUFFER_SIZE: usize = 65535; fn print_banner(bind_addr: &str) { println!("========================================"); println!(" rusty-telemetry listener"); println!(" Phase 1 - UDP JSON receiver"); println!("========================================"); println!(" Bind: {}", bind_addr); println!("========================================"); println!(); } #[tokio::main] async fn main() { let bind_addr = std::env::var("RUSTY_TELEMETRY_BIND").unwrap_or_else(|_| DEFAULT_BIND_ADDR.to_string()); print_banner(&bind_addr); let socket = match tokio::net::UdpSocket::bind(&bind_addr).await { Ok(s) => { let local_addr = s.local_addr().unwrap(); println!("[INFO] Listening on {}", local_addr); s } Err(e) => { eprintln!("[ERROR] Failed to bind UDP socket: {}", e); std::process::exit(1); } }; let mut buf = vec![0u8; BUFFER_SIZE]; let mut stats = ReceiveStats::new(); let stdout = std::io::stdout(); loop { match socket.recv_from(&mut buf).await { Ok((len, src)) => { stats.record(len); let text = match std::str::from_utf8(&buf[..len]) { Ok(s) => s, Err(e) => { eprintln!("[WARN] Invalid UTF-8 from {}: {}", src, e); continue; } }; let frame: TelemetryFrame = match serde_json::from_str(text) { Ok(f) => f, Err(e) => { eprintln!("[WARN] JSON parse error from {}: {}", src, e); continue; } }; { let mut handle = stdout.lock(); let _ = writeln!(handle, "{}", frame); let _ = writeln!(handle, " [{}] {}", src, stats); let _ = writeln!(handle); } } Err(e) => { eprintln!("[ERROR] Receive error: {}", e); } } } }