Something went wrong. Try again.
Watches a file and prints human-readable, coloured diffs.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127use std::{fs::read_to_string, io::Write as _, path::PathBuf, sync::mpsc};
use clap::{Parser, ValueEnum};use notify::{RecursiveMode, Watcher};use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
/// Watch a file and print diffs of changes.#[derive(Debug, Parser)]#[command(version, about, long_about = None)]pub struct Args { /// Path to the file to watch. file: PathBuf,
/// Whether to enable color output. #[arg(long, value_enum, default_value_t = ColorMode::Auto)] color: ColorMode,
/// Show unchanged lines. #[arg(long, default_value_t = false)] show_unchanged: bool,}
#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueEnum)]enum ColorMode { Auto, Always, Never,}
impl From<ColorMode> for ColorChoice { fn from(value: ColorMode) -> Self { match value { ColorMode::Auto => Self::Auto, ColorMode::Always => Self::Always, ColorMode::Never => Self::Never, } }}
fn main() -> anyhow::Result<()> { let args = Args::parse(); let (tx, rx) = mpsc::channel(); let mut stdout = StandardStream::stdout(args.color.into());
let mut watcher = notify::recommended_watcher(tx)?; watcher.watch(&args.file, RecursiveMode::NonRecursive)?;
let mut old_content = read_to_string(&args.file)?;
for evt in rx { let evt = evt?;
match evt.kind { notify::EventKind::Create(_) | notify::EventKind::Modify(_) => { let new_content = read_to_string(&args.file)?;
if old_content == new_content { // ignore continue; }
on_update(&mut stdout, &old_content, &new_content, args.show_unchanged)?; old_content = new_content; } notify::EventKind::Remove(_) => { stdout.reset()?; writeln!(&mut stdout, "................ file deleted!")?; break; } _ => {} } }
stdout.reset()?;
Ok(())}
fn on_update( out: &mut impl WriteColor, old_content: &str, new_content: &str, show_unchanged: bool,) -> std::io::Result<()> { let color_deleted = { let mut color = ColorSpec::new(); color.set_fg(Some(Color::Red)); color }; let color_unchanged = ColorSpec::new(); let color_added = { let mut color = ColorSpec::new(); color.set_fg(Some(Color::Green)); color }; let (mut left_line, mut right_line) = (0, 0);
out.reset()?; writeln!(out, "................ file modified!")?; for line in diff::lines(old_content, new_content) { match line { diff::Result::Left(l) => { left_line += 1;
out.set_color(&color_deleted)?; writeln!(out, "{left_line:>5} - {l}")?; } diff::Result::Both(l, _) => { left_line += 1; right_line += 1; if show_unchanged { out.set_color(&color_unchanged)?; writeln!(out, "{left_line:>5} {right_line:>5} | {l}")?; } } diff::Result::Right(l) => { right_line += 1; out.set_color(&color_added)?; writeln!(out, " {right_line:>5} + {l}")?; } } } out.reset()?;
Ok(())}