extern crate tokio; use std::fmt::Display; use std::time::SystemTime; use tokio::io::{AsyncWriteExt, Stderr}; pub struct Logger { out: Stderr, } impl Logger { pub fn new() -> Self { Logger { out: tokio::io::stderr(), } } async fn log_message(&mut self, msg: LogMessage<'_>) { // These two lines should never fail and we realistically can't do anything about // it if they do. self.out .write_all(format!("{}\n", msg).as_bytes()) .await .expect("failed to write log message"); self.out.flush().await.expect("failed to flush stderr"); } async fn log_payload(&mut self, payload: LogPayload<'_>) { self.log_message(LogMessage { timestamp: SystemTime::now(), payload, }) .await } // ----------------------------------------------------------------------------------- pub async fn debug_msg>(&mut self, what: S) { let what = what.as_ref(); self.log_payload(LogPayload::Debug { what }).await } pub async fn bound(&mut self, port: u16) { self.log_payload(LogPayload::Bound { port }).await } pub async fn recv_data_accepted(&mut self, start: usize, len: usize, order: AcceptedOrder) { self.log_payload(LogPayload::RecvDataAccepted { start, len, order }) .await } pub async fn recv_data_ignored(&mut self) { self.log_payload(LogPayload::RecvDataIgnored).await } pub async fn send_data(&mut self, start: usize, len: usize) { self.log_payload(LogPayload::SendData { start, len }).await } } struct LogMessage<'a> { pub timestamp: SystemTime, pub payload: LogPayload<'a>, } enum LogPayload<'a> { Debug { what: &'a str, }, Bound { port: u16, }, RecvDataAccepted { start: usize, len: usize, order: AcceptedOrder, }, RecvDataIgnored, SendData { start: usize, len: usize, }, } #[derive(Copy, Clone, Eq, PartialEq)] pub enum AcceptedOrder { InOrder, OutOfOrder, } pub use AcceptedOrder::*; impl Display for LogMessage<'_> { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { // TODO: write timestamp self.payload.fmt(f) } } impl Display for LogPayload<'_> { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { LogPayload::Debug { what } => write!(f, "[debug] {}", what), LogPayload::Bound { port } => write!(f, "[bound] {}", port), LogPayload::RecvDataAccepted { start, len, order } => { write!(f, "[recv data] {} ({}) ACCEPTED ({})", start, len, order) } LogPayload::RecvDataIgnored => write!(f, "[recv data] IGNORED"), LogPayload::SendData { start, len } => write!(f, "[send data] {} ({})", start, len), } } } impl Display for AcceptedOrder { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.write_str(match self { InOrder => "in-order", OutOfOrder => "out-of-order", }) } }