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<ConsoleMessage>,
sender: Sender<ConsoleMessage>,
}
pub struct ConsoleLogger(Sender<ConsoleMessage>);
pub enum ConsoleMessage {
RuntimeLog {
message: String,
level: Level,
file: String,
line: u32,
},
ScriptLog {
message: String,
level: Level,
file: String,
line: u32,
},
FrameTime {
timestamp: i64,
},
ScopeStart {
scope_name: String,
timestamp: i64,
},
ScopeEnd {
timestamp: i64,
},
}
impl Console {
pub async fn new(port: u16) -> Result<Self, std::io::Error> {
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,
})
}
fn sender(&self) -> Sender<ConsoleMessage> {
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 message = message.replace('\n', "\\n");
let msg = format!("runtimelog {level} {file}:{line} {message}\n");
self.tcp_socket.write(msg.as_bytes()).await?;
}
ConsoleMessage::ScriptLog {
message,
level,
file,
line,
} => {
let message = message.replace('\n', "\\n");
let msg = format!("scriptlog {level} {file}:{line} {message}\n");
self.tcp_socket.write(msg.as_bytes()).await?;
}
ConsoleMessage::FrameTime {
timestamp: unix_timestamp,
} => {
let msg = format!("frametime {unix_timestamp}");
self.tcp_socket.write(msg.as_bytes()).await?;
}
ConsoleMessage::ScopeStart {
scope_name,
timestamp: unix_timestamp,
} => {
let msg = format!("scopestart {scope_name} {unix_timestamp}");
self.tcp_socket.write(msg.as_bytes()).await?;
}
ConsoleMessage::ScopeEnd {
timestamp: unix_timestamp,
} => {
let msg = format!("scopeend {unix_timestamp}");
self.tcp_socket.write(msg.as_bytes()).await?;
}
}
}
self.tcp_socket.flush().await?;
Ok(())
}
}
impl ConsoleLogger {
pub fn send(&self, message: ConsoleMessage) {
_ = self.0.send(message).block_on();
}
}
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) {}
}
|