logtimes/src/main.rs

118 lines
2.7 KiB
Rust

use std::{
env::args,
fs::File,
io::{stdin, stdout, Error as IoError, ErrorKind, Read, Write},
};
use chrono::prelude::*;
const TIME_FORMAT: &str = "%H:%M:%S%.6f";
type LogtimesResult = Result<(), IoError>;
#[inline(always)]
fn print_time<T>(output: &mut T) -> LogtimesResult
where
T: Write,
{
let date_now = Local::now().format(TIME_FORMAT);
write!(output, "[{}] ", &date_now)
}
#[inline(always)]
fn print_time_color<T>(output: &mut T) -> Result<(), std::io::Error>
where
T: Write,
{
let date_now = Local::now().format(TIME_FORMAT);
let color_green = "\x1b\x5b\x30\x3b\x33\x32\x6d";
let color_off = "\x1b\x5b\x30\x6d";
write!(output, "{}[{}]{} ", color_green, &date_now, color_off)
}
#[inline(always)]
fn print_delete_line<T>(output: &mut T) -> LogtimesResult
where
T: Write,
{
// tput dl1 und tput hpa 0
let bytes = "\x1b\x5b\x4d\x1b\x5b\x31\x47";
write!(output, "{bytes}")
}
fn trim_end(line: &[u8]) -> usize {
let mut end = line.len();
line.iter()
.rev()
.take_while(|ch| ch.is_ascii_whitespace())
.for_each(|_| end -= 1);
end
}
fn run() -> LogtimesResult {
let mut log_file = args().nth(1).map(|f| {
File::options()
.create(true)
.append(true)
.open(f)
.expect("Could not open log file")
});
let mut out = stdout().lock();
let mut inp = stdin().lock();
let mut buf = [0; 1];
let mut linebuf = Vec::with_capacity(64 * 1024);
loop {
// read 1 char
if let Err(e) = inp.read_exact(&mut buf) {
if e.kind() == ErrorKind::UnexpectedEof {
return Ok(());
}
return Err(e);
}
// push that char to the "current line" buffer
linebuf.push(buf[0]);
// we encounter a newline -> delete the line and write it new as:
// [timestamp] actual content\n
// also write the line to the log file if there is one
if buf[0] == 0xa {
print_delete_line(&mut out)?;
print_time_color(&mut out)?;
let end = trim_end(&linebuf);
out.write_all(&linebuf[..end])?;
out.write_all(&[b'\r', b'\n'])?;
out.flush()?;
if let Some(ref mut f) = log_file {
print_time(f)?;
f.write_all(&linebuf[..end])?;
f.write_all(&[b'\n'])?;
f.flush()?;
};
// clear line buffer, so it is fresh for the next line
linebuf.clear();
continue;
}
out.write_all(&buf)?;
out.flush()?;
}
}
fn main() {
if let Err(e) = run() {
println!("{e:?}");
std::process::exit(1);
}
}