rework main signature + impl builder for optimizer

This commit is contained in:
rmanach 2025-10-24 17:18:43 +02:00
parent 02d9d60686
commit 5575392f73
3 changed files with 64 additions and 27 deletions

View File

@ -195,7 +195,7 @@ pub struct Directory {
impl Directory {
pub fn from_path(path: &str) -> Result<Self, Box<dyn std::error::Error>> {
if !std::path::Path::new(path).is_dir() {
return Err(format!("Directory path: {} must be a directory", path).into());
return Err(format!("directory path: {} must be a directory", path).into());
}
let mut nb_files = 0;

View File

@ -1,3 +1,5 @@
use std::process::exit;
use clap::Parser;
use env_logger::Env;
@ -22,17 +24,29 @@ struct Args {
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
async fn main() {
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
let args = Args::parse();
let directory = Directory::from_path(&args.src)?;
let directory = match Directory::from_path(&args.src) {
Ok(d) => d,
Err(e) => {
log::error!(
"unable to load directory from path: {}, details: {}",
args.src,
e
);
exit(1);
}
};
let optimizer = ImgOptimizer::new(&args.dest, args.workers);
let optimizer = ImgOptimizer::builder(&args.dest, args.workers)
.with_progress()
.build();
let file_group =
directory.get_file_group(Some(FileImgMimetype::Jpeg), Some(FileSizeRange::Tiny));
let result = optimizer.optimize(&file_group).await?;
let result = optimizer.optimize(&file_group).await;
let (optimized, percent, size) = result.stats();
log::info!(
@ -41,6 +55,4 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
percent,
size
);
Ok(())
}

View File

@ -133,17 +133,49 @@ pub struct ImgOptimizer {
queue: Arc<Queue<File>>,
cancel_token: CancellationToken,
tracker: TaskTracker,
progress: Arc<ProgressBar>,
}
impl ImgOptimizer {
pub struct ImgOptimizerBuilder(ImgOptimizer);
impl ImgOptimizerBuilder {
pub fn new(dest_dir: &str, nb_workers: usize) -> Self {
ImgOptimizer {
ImgOptimizerBuilder(ImgOptimizer {
dest_dir: dest_dir.to_string(),
nb_workers,
queue: Arc::new(Queue::new()),
cancel_token: CancellationToken::new(),
tracker: TaskTracker::new(),
progress: Arc::new(ProgressBar::hidden()),
})
}
pub fn with_progress(mut self) -> Self {
let pb = Arc::new(ProgressBar::new(0));
let tmpl_res =
ProgressStyle::default_bar().template("{msg} [{bar:40}] {pos}/{len} ({eta})");
if let Err(e) = tmpl_res {
log::error!("unable to set the progress bar, {}", e);
return self;
}
let style = tmpl_res.unwrap().progress_chars("##-");
pb.set_style(style);
self.0.progress = pb;
self
}
pub fn build(self) -> ImgOptimizer {
self.0
}
}
impl ImgOptimizer {
pub fn builder(dest_dir: &str, nb_workers: usize) -> ImgOptimizerBuilder {
ImgOptimizerBuilder::new(dest_dir, nb_workers)
}
pub async fn stop(&self) {
@ -151,10 +183,7 @@ impl ImgOptimizer {
self.cancel_token.cancel();
}
pub async fn optimize(
&self,
file_group: &FileGroup,
) -> Result<OptimizerResult, Box<dyn std::error::Error>> {
pub async fn optimize(&self, file_group: &FileGroup) -> OptimizerResult {
let start = std::time::Instant::now();
let results = Arc::new(RwLock::new(FileGroup::new()));
@ -164,19 +193,13 @@ impl ImgOptimizer {
self.queue.enqueue(file).await;
}
let pb = Arc::new(ProgressBar::new(file_group.len() as u64));
pb.set_style(
ProgressStyle::default_bar()
.template("{msg} [{bar:40}] {pos}/{len} ({eta})")?
.progress_chars("##-"),
);
pb.set_message("optimizing...");
self.progress.set_message("optimizing...");
for _ in 0..self.nb_workers {
let queue = Arc::clone(&self.queue);
let results = Arc::clone(&results);
let optimized = Arc::clone(&optimized);
let pb = Arc::clone(&pb);
let pb = Arc::clone(&self.progress);
let cancel_token = self.cancel_token.clone();
let dest_dir = self.dest_dir.clone();
@ -211,9 +234,8 @@ impl ImgOptimizer {
});
}
// TODO(rmanach): move it on main not here
// TODO(rmanach): move it on main not here (overwrite progress bar message)
tokio::spawn({
let pb = Arc::clone(&pb);
let optimizer = self.clone();
async move {
if let Err(e) = tokio::signal::ctrl_c().await {
@ -222,23 +244,26 @@ impl ImgOptimizer {
log::warn!("interrupt signal received");
optimizer.stop().await;
pb.finish_with_message("optimization interrupted");
optimizer
.progress
.finish_with_message("optimization interrupted");
}
});
self.tracker.close();
self.tracker.wait().await;
pb.finish_with_message("optimization complete");
self.progress.finish_with_message("optimization complete");
log::info!(
"optimization finished in {:.2}s",
start.elapsed().as_secs_f64()
);
Ok(OptimizerResult {
OptimizerResult {
orig: file_group.clone(),
opti: results.read().await.clone(),
optimized: optimized.load(Ordering::Relaxed),
})
}
}
}