veloren/server-cli/src/tuilog.rs

91 lines
3.2 KiB
Rust
Raw Normal View History

2024-02-06 15:06:58 +00:00
use ratatui::{
prelude::Line,
style::{Color, Modifier, Style},
text::{Span, Text},
};
2020-08-31 08:41:11 +00:00
use std::{
io::{self, Write},
sync::{Arc, Mutex},
};
2020-09-01 14:33:41 +00:00
use tracing::warn;
2020-08-31 08:41:11 +00:00
#[derive(Debug, Default, Clone)]
pub struct TuiLog<'a> {
pub inner: Arc<Mutex<Text<'a>>>,
}
impl<'a> TuiLog<'a> {
pub fn resize(&self, h: usize) {
let mut inner = self.inner.lock().unwrap();
let len = inner.lines.len().saturating_sub(h);
inner.lines.drain(..len);
2020-08-31 08:41:11 +00:00
}
}
impl<'a> Write for TuiLog<'a> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
// TODO: this processing can probably occur in the consumer of the log lines
// (and instead of having a TuiLog::resize the consumer can take
// ownership of the lines and manage them itself).
// Not super confident this is the ideal parser but it works for now and doesn't
// depend on an old version of nom. Alternatives to consider may include
// `vte`, `anstyle-parse`, `vt100`, or others.
use cansi::v3::categorise_text;
2020-08-31 08:41:11 +00:00
let line =
core::str::from_utf8(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
2020-08-31 08:41:11 +00:00
let mut spans = Vec::new();
let mut lines = Vec::new();
2020-08-31 08:41:11 +00:00
for out in categorise_text(line) {
2024-02-06 15:06:58 +00:00
let mut style = Style::default();
// NOTE: There are other values returned from cansi that we don't bother to use
// for now including background color, italics, blinking, etc.
style.fg = match out.fg {
Some(cansi::Color::Black) => Some(Color::Black),
Some(cansi::Color::Red) => Some(Color::Red),
Some(cansi::Color::Green) => Some(Color::Green),
Some(cansi::Color::Yellow) => Some(Color::Yellow),
Some(cansi::Color::Blue) => Some(Color::Blue),
Some(cansi::Color::Magenta) => Some(Color::Magenta),
Some(cansi::Color::Cyan) => Some(Color::Cyan),
Some(cansi::Color::White) => Some(Color::White),
// "Bright" versions currently not handled
Some(c) => {
warn!("Unknown color {:#?}", c);
style.fg
2020-08-31 08:41:11 +00:00
},
None => style.fg,
};
match out.intensity {
Some(cansi::Intensity::Normal) | None => {},
Some(cansi::Intensity::Bold) => style.add_modifier = Modifier::BOLD,
Some(cansi::Intensity::Faint) => style.add_modifier = Modifier::DIM,
2020-08-31 08:41:11 +00:00
}
// search for newlines
for t in out.text.split_inclusive('\n') {
if !t.is_empty() {
spans.push(Span::styled(t.to_owned(), style));
}
if t.ends_with('\n') {
2024-02-06 15:06:58 +00:00
lines.push(Line::from(core::mem::take(&mut spans)));
}
}
2020-08-31 08:41:11 +00:00
}
if !spans.is_empty() {
2024-02-06 15:06:58 +00:00
lines.push(Line::from(spans));
}
2020-08-31 08:41:11 +00:00
self.inner.lock().unwrap().lines.append(&mut lines);
2020-08-31 08:41:11 +00:00
Ok(buf.len())
}
// We can potentially use this to reduce locking frequency?
2020-08-31 08:41:11 +00:00
fn flush(&mut self) -> io::Result<()> { Ok(()) }
}