image_optimizer/optimization/
image_optimizer.rs1use anyhow::Result;
2use image;
3use std::ffi::OsStr;
4use std::fs;
5use std::path::Path;
6
7use super::{jpeg_optimizer, png_optimizer, webp_optimizer};
8use crate::cli::Cli;
9use crate::file_ops::{calculate_resize_dimensions, create_backup, ensure_output_dir};
10
11pub fn optimize_image(input_path: &Path, args: &Cli, input_dir: &Path) -> Result<u64> {
16 let original_size = fs::metadata(input_path)?.len();
17
18 let is_in_place = args.output.is_none();
19 let output_path = if let Some(ref output_dir) = args.output {
20 ensure_output_dir(output_dir, input_dir, input_path)?
21 } else {
22 input_path.with_extension(format!(
23 "tmp.{}",
24 input_path
25 .extension()
26 .and_then(OsStr::to_str)
27 .unwrap_or("jpg")
28 ))
29 };
30
31 if args.backup && is_in_place {
32 create_backup(input_path)?;
33 }
34
35 let extension = input_path
36 .extension()
37 .and_then(OsStr::to_str)
38 .unwrap_or("")
39 .to_lowercase();
40
41 let img = if args.max_size.is_some() {
42 let img = image::open(input_path)?;
43 let (width, height) = (img.width(), img.height());
44
45 if let Some(max_size) = args.max_size {
46 let (new_width, new_height) = calculate_resize_dimensions(width, height, max_size);
47 if new_width != width || new_height != height {
48 Some(img.resize(new_width, new_height, image::imageops::FilterType::Lanczos3))
49 } else {
50 Some(img)
51 }
52 } else {
53 Some(img)
54 }
55 } else {
56 None
57 };
58
59 match extension.as_str() {
60 "jpg" | "jpeg" => jpeg_optimizer::optimize_jpeg(input_path, &output_path, args, img)?,
61 "png" => png_optimizer::optimize_png(input_path, &output_path, args, img)?,
62 "webp" => webp_optimizer::optimize_webp(input_path, &output_path, args, img)?,
63 _ => return Err(anyhow::anyhow!("Unsupported file format: {}", extension)),
64 }
65
66 let optimized_size = fs::metadata(&output_path)?.len();
67
68 if optimized_size < original_size {
69 if is_in_place {
70 fs::rename(&output_path, input_path)?;
71 }
72 Ok(original_size - optimized_size)
73 } else {
74 if is_in_place {
75 fs::remove_file(&output_path)?;
76 } else {
77 fs::copy(input_path, &output_path)?;
78 }
79 Ok(0)
80 }
81}