Add watchfile

This commit is contained in:
2023-10-28 19:44:47 +02:00
parent 3736fab8ed
commit 3517d88a1c
3 changed files with 795 additions and 94 deletions

View File

@ -1,10 +1,17 @@
use std::process::Command;
use terminal_size::{Width, Height, terminal_size};
use colored::{Color, Colorize};
use std::io::{self, Write, stdout};
use std::thread::sleep;
use hotwatch::Hotwatch;
use std::fs::remove_file;
use std::future::pending;
use std::io::{stdout, BufWriter, Write};
use std::path::PathBuf;
use std::process::{self, Command};
use std::sync::Arc;
use std::time::Duration;
use structopt::StructOpt;
use terminal_size::{terminal_size, Height, Width};
use tokio::sync::Notify;
use tokio::time::sleep;
use tokio::{fs::File, select, spawn};
#[derive(Debug, StructOpt)]
#[structopt()]
@ -20,52 +27,43 @@ struct Opt {
/// Set number of rows in column
#[structopt(short, long)]
timeout: Option<u64>,
/// Watch for changes on a file. If the file changes, restart the timeout.
#[structopt(short, long)]
watch: Option<PathBuf>,
}
const COLORS: &[Color] = &[
Color::Green,
Color::Blue,
Color::Yellow,
Color::Red,
];
type Rendered = Vec<Vec<(char, Color)>>;
const BLOCK_CHARS: &[char] = &[
' ',
'▁',
'▂',
'▃',
'▄',
'▅',
'▆',
'▇',
'█',
];
const COLORS: &[Color] = &[Color::Green, Color::Blue, Color::Yellow, Color::Red];
fn main() -> io::Result<()> {
let opt = Opt::from_args();
const BLOCK_CHARS: &[char] = &[' ', '▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
fn get_volume() -> eyre::Result<f64> {
let output = Command::new("pamixer").args(["--get-volume"]).output()?;
let out =
std::str::from_utf8(&output.stdout).expect("failed to parse paxmixer output as utf-8");
let volume: u16 = out
.trim()
.parse()
.expect("failed to parse pamixer output as u16");
let percentage = volume as f64 / 100.0;
Ok(percentage)
}
fn render_bars(opt: &Opt, volume: f64) -> eyre::Result<Rendered> {
let (Width(calc_max_cols), Height(calc_max_rows)) = terminal_size().unwrap();
let calc_max_rows = calc_max_rows.min(4);
let max_cols = opt.cols.unwrap_or(calc_max_cols);
let max_rows = opt.rows.unwrap_or(calc_max_rows);
let output = Command::new("pamixer")
.args(&["--get-volume"])
.output()?;
let out = std::str::from_utf8(&output.stdout)
.expect("failed to parse paxmixer output as utf-8");
let volume: u16 = out.trim().parse().expect("failed to parse pamixer output as u16");
let percentage = volume as f64 / 100.0;
let num_cols = ((max_cols as f64 * percentage) as u16).min(max_cols);
let num_cols = ((max_cols as f64 * volume) as u16).min(max_cols);
let max_chars = max_cols / 2;
let num_chars = num_cols / 2;
let mut lines = vec![vec![]; max_rows as usize];
for i in 0..num_chars {
@ -83,22 +81,98 @@ fn main() -> io::Result<()> {
}
lines.reverse();
Ok(lines)
}
fn draw_bars(lines: &Rendered) -> eyre::Result<()> {
let stdout = stdout();
let mut handle = stdout.lock();
let mut stdout = BufWriter::new(stdout.lock());
// clear console and more cursor to 0,0
write!(&mut stdout, "{esc}[2J{esc}[1;1H", esc = 27 as char)?;
let mut char_buf = [0u8; 4];
for line in &lines {
writeln!(&mut handle)?;
for line in lines {
writeln!(&mut stdout)?;
for &(bc, color) in line {
let bc = bc.encode_utf8(&mut char_buf);
write!(&mut handle, " {}", bc.color(color))?;
let bc = bc.encode_utf8(&mut char_buf);
write!(&mut stdout, " {}", bc.color(color))?;
}
}
handle.flush()?;
stdout.flush()?;
Ok(())
}
#[tokio::main(flavor = "current_thread")]
async fn main() -> eyre::Result<()> {
let opt = Opt::from_args();
color_eyre::install()?;
if let Some(path) = &opt.watch {
let _file = File::options()
.create_new(true)
.write(true)
.open(path)
.await?;
}
let mut volume = get_volume()?;
{
let rendered = render_bars(&opt, volume)?;
draw_bars(&rendered)?;
}
let extend_timeout = Arc::new(Notify::new());
if let Some(millis) = opt.timeout {
sleep(Duration::from_millis(millis));
let duration = Duration::from_millis(millis);
let extend_timeout = Arc::clone(&extend_timeout);
let watchfile_path = opt.watch.clone();
spawn(async move {
loop {
select! {
_ = extend_timeout.notified() => {}
_ = sleep(duration) => break,
}
}
if let Some(path) = watchfile_path {
let _ = remove_file(path);
}
process::exit(0);
});
}
if let Some(path) = &opt.watch {
let mut watcher = Hotwatch::new()?;
let render = Arc::new(Notify::new());
{
let render = Arc::clone(&render);
watcher.watch(path, move |_ev| {
render.notify_waiters();
extend_timeout.notify_waiters();
})?;
}
loop {
render.notified().await;
let new_volume = get_volume()?;
if new_volume == volume {
continue;
}
volume = new_volume;
let rendered = render_bars(&opt, volume)?;
draw_bars(&rendered)?;
}
}
if opt.timeout.is_some() {
return pending().await;
}
Ok(())