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(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(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(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); } }