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, svg_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 extension == "svg" {
42 None
43 } else if args.max_size.is_some() {
44 let img = image::open(input_path)?;
45 let (width, height) = (img.width(), img.height());
46
47 if let Some(max_size) = args.max_size {
48 let (new_width, new_height) = calculate_resize_dimensions(width, height, max_size);
49 if new_width != width || new_height != height {
50 Some(img.resize(new_width, new_height, image::imageops::FilterType::Lanczos3))
51 } else {
52 Some(img)
53 }
54 } else {
55 Some(img)
56 }
57 } else {
58 None
59 };
60
61 match extension.as_str() {
62 "jpg" | "jpeg" => jpeg_optimizer::optimize_jpeg(input_path, &output_path, args, img)?,
63 "png" => png_optimizer::optimize_png(input_path, &output_path, args, img)?,
64 "webp" => webp_optimizer::optimize_webp(input_path, &output_path, args, img)?,
65 "svg" => svg_optimizer::optimize_svg(input_path, &output_path, args, img)?,
66 _ => return Err(anyhow::anyhow!("Unsupported file format: {}", extension)),
67 }
68
69 let optimized_size = fs::metadata(&output_path)?.len();
70
71 if optimized_size < original_size {
72 if is_in_place {
73 fs::rename(&output_path, input_path)?;
74 }
75 Ok(original_size - optimized_size)
76 } else {
77 if is_in_place {
78 fs::remove_file(&output_path)?;
79 } else {
80 fs::copy(input_path, &output_path)?;
81 }
82 Ok(0)
83 }
84}