image_optimizer/optimization/
image_optimizer.rs

1use 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
11/// Optimizes an image file using the appropriate format-specific optimizer
12///
13/// # Errors
14/// Returns an error if file I/O operations fail, image processing fails, or unsupported format
15pub 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}