use std::io::{self, Write};
use std::sync::{Arc, Mutex, OnceLock};
static LOG_LINES: OnceLock<Arc<Mutex<Vec<String>>>> = OnceLock::new();
pub fn init() -> Arc<Mutex<Vec<String>>> {
LOG_LINES
.get_or_init(|| Arc::new(Mutex::new(Vec::new())))
.clone()
}
pub fn drain() -> Vec<String> {
let Some(lines) = LOG_LINES.get() else {
return Vec::new();
};
let Ok(mut guard) = lines.lock() else {
return Vec::new();
};
guard.drain(..).collect()
}
fn push_line(line: String) {
if line.trim().is_empty() {
return;
}
let lines = init();
if let Ok(mut guard) = lines.lock() {
guard.push(line);
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct TuiLogMakeWriter;
impl<'a> tracing_subscriber::fmt::MakeWriter<'a> for TuiLogMakeWriter {
type Writer = TuiLogWriter;
fn make_writer(&'a self) -> Self::Writer {
TuiLogWriter { buf: Vec::new() }
}
}
pub struct TuiLogWriter {
buf: Vec<u8>,
}
impl Write for TuiLogWriter {
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
self.buf.extend_from_slice(data);
while let Some(pos) = self.buf.iter().position(|&b| b == b'\n') {
let line: Vec<u8> = self.buf.drain(..=pos).collect();
let line = String::from_utf8_lossy(&line).trim_end().to_string();
push_line(line);
}
Ok(data.len())
}
fn flush(&mut self) -> io::Result<()> {
self.flush_partial();
Ok(())
}
}
impl TuiLogWriter {
fn flush_partial(&mut self) {
if self.buf.is_empty() {
return;
}
let line = String::from_utf8_lossy(&self.buf).trim_end().to_string();
self.buf.clear();
push_line(line);
}
}
impl Drop for TuiLogWriter {
fn drop(&mut self) {
self.flush_partial();
}
}