use std::fs::{File, OpenOptions};
use std::io::Write;
use std::sync::{Mutex, OnceLock};
static SINK: OnceLock<Option<Mutex<File>>> = OnceLock::new();
fn sink() -> Option<&'static Mutex<File>> {
SINK.get_or_init(|| {
let path = std::env::var_os("LIBEDIT_TRACE")?;
OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(path)
.ok()
.map(Mutex::new)
})
.as_ref()
}
pub(crate) fn enabled() -> bool {
sink().is_some()
}
fn visualize(bytes: &[u8]) -> String {
let mut out = String::with_capacity(bytes.len() + 8);
for &b in bytes {
match b {
0x1b => out.push_str("<ESC>"),
b'\n' => out.push_str("\\n"),
b'\r' => out.push_str("\\r"),
b'\t' => out.push_str("\\t"),
0x01 => out.push_str("<\\x01>"), 0x20..=0x7e => out.push(b as char),
other => out.push_str(&format!("<\\x{other:02x}>")),
}
}
out
}
pub(crate) fn bytes(tag: &str, data: &[u8]) {
let Some(sink) = sink() else { return };
let line = format!("[{tag}] {}\n", visualize(data));
if let Ok(mut f) = sink.lock() {
let _ = f.write_all(line.as_bytes());
let _ = f.flush();
}
}
pub(crate) fn note(msg: &str) {
let Some(sink) = sink() else { return };
let line = format!("* {msg}\n");
if let Ok(mut f) = sink.lock() {
let _ = f.write_all(line.as_bytes());
let _ = f.flush();
}
}