use std::net::Ipv6Addr; use log::Level; use pollster::FutureExt; use smol::channel::{Receiver, Sender}; use smol::io::AsyncWriteExt; use smol::net::{TcpListener, TcpStream}; pub struct Console { tcp_socket: TcpStream, messages: Receiver, sender: Sender, } pub enum ConsoleMessage { RuntimeLog { message: String, level: Level, file: String, line: u32, }, ScriptLog { message: String, level: Level, file: String, line: u32, }, } pub struct ConsoleLogger(Sender); impl Console { pub async fn new(port: u16) -> Result { let tcp_listener = TcpListener::bind((Ipv6Addr::LOCALHOST, port)).await?; let tcp_socket = tcp_listener.accept().await?.0; let (sender, reciever) = smol::channel::unbounded(); Ok(Self { tcp_socket, messages: reciever, sender, }) } pub fn sender(&self) -> Sender { self.sender.clone() } pub fn logger(&self) -> ConsoleLogger { ConsoleLogger(self.sender()) } pub async fn flush(&mut self) -> std::io::Result<()> { while let Ok(message) = self.messages.try_recv() { match message { ConsoleMessage::RuntimeLog { message, level, file, line, } => { let msg = format!("runtimelog {level} {file}:{line} {message}\n"); self.tcp_socket.write(msg.as_bytes()).await?; } ConsoleMessage::ScriptLog { message, level, file, line, } => { let msg = format!("scriptlog {level} {file}:{line} {message}\n"); self.tcp_socket.write(msg.as_bytes()).await?; } } } self.tcp_socket.flush().await?; Ok(()) } } impl log::Log for ConsoleLogger { fn enabled(&self, _: &log::Metadata) -> bool { true } fn log(&self, record: &log::Record) { let _ = self .0 .send(ConsoleMessage::RuntimeLog { message: record.args().to_string(), level: record.level(), file: record.file().map(str::to_string).unwrap_or_default(), line: record.line().unwrap_or_default(), }) .block_on(); } fn flush(&self) {} }