Skip to main content

Create a proper type to hold the target dimension

ID
aac0292
date
2024-08-20 11:14:18+00:00
author
Alex Chan <alex@alexwlchan.net>
parent
7861191
message
Create a proper type to hold the target dimension
changed files
2 files, 53 additions, 77 deletions

Changed files

src/get_thumbnail_dimensions.rs (4497) → src/get_thumbnail_dimensions.rs (3810)

diff --git a/src/get_thumbnail_dimensions.rs b/src/get_thumbnail_dimensions.rs
index d596f7c..95c9f1e 100644
--- a/src/get_thumbnail_dimensions.rs
+++ b/src/get_thumbnail_dimensions.rs
@@ -2,6 +2,14 @@ use std::path::PathBuf;
 
 use image::GenericImageView;
 
+/// Represents the target dimensions of the thumbnail.
+///
+/// The user can choose a max width, or a max height, but not both.
+pub enum TargetDimension {
+    MaxWidth(u32),
+    MaxHeight(u32),
+}
+
 /// Given the path to the original image and the target width/height,
 /// calculate the dimensions of the new image.
 ///
@@ -17,25 +25,16 @@ use image::GenericImageView;
 ///
 pub fn get_thumbnail_dimensions(
     path: &PathBuf,
-    target_width: Option<u32>,
-    target_height: Option<u32>,
+    target: TargetDimension,
 ) -> Result<(u32, u32), image::error::ImageError> {
-    // Assert that exactly one of `target_width` and `target_height` are defined
-    assert!(target_width.is_some() || target_height.is_some());
-    assert!(target_width.is_none() || target_height.is_none());
-
-    // Open the image, and compare its dimensions to the target
     let img = image::open(path)?;
 
-    // Calculate the new width/height of the image
-    let (new_width, new_height) = match (target_width, target_height) {
-        (Some(w), None) if w >= img.width() => img.dimensions(),
-        (None, Some(h)) if h >= img.height() => img.dimensions(),
-
-        (Some(w), None) => (w, w * img.height() / img.width()),
-        (None, Some(h)) => (h * img.width() / img.height(), h),
+    let (new_width, new_height) = match target {
+        TargetDimension::MaxWidth(w) if w >= img.width() => img.dimensions(),
+        TargetDimension::MaxHeight(h) if h >= img.height() => img.dimensions(),
 
-        _ => unreachable!(),
+        TargetDimension::MaxWidth(w) => (w, w * img.height() / img.width()),
+        TargetDimension::MaxHeight(h) => (h * img.width() / img.height(), h),
     };
 
     Ok((new_width, new_height))
@@ -53,10 +52,9 @@ mod test_get_thumbnail_dimensions {
     fn with_target_width() {
         let p = PathBuf::from("src/tests/red.png");
 
-        let target_width: Option<u32> = Some(50);
-        let target_height: Option<u32> = None;
+        let target = TargetDimension::MaxWidth(50);
 
-        let dimensions = get_thumbnail_dimensions(&p, target_width, target_height);
+        let dimensions = get_thumbnail_dimensions(&p, target);
         assert_eq!(dimensions.unwrap(), (50, 100));
     }
 
@@ -64,10 +62,9 @@ mod test_get_thumbnail_dimensions {
     fn with_target_height() {
         let p = PathBuf::from("src/tests/red.png");
 
-        let target_width: Option<u32> = None;
-        let target_height: Option<u32> = Some(100);
+        let target = TargetDimension::MaxHeight(100);
 
-        let dimensions = get_thumbnail_dimensions(&p, target_width, target_height);
+        let dimensions = get_thumbnail_dimensions(&p, target);
         assert_eq!(dimensions.unwrap(), (50, 100));
     }
 
@@ -75,10 +72,9 @@ mod test_get_thumbnail_dimensions {
     fn leaves_image_as_is_if_target_width_greater_than_width() {
         let p = PathBuf::from("src/tests/red.png");
 
-        let target_width: Option<u32> = Some(500);
-        let target_height: Option<u32> = None;
+        let target = TargetDimension::MaxWidth(500);
 
-        let dimensions = get_thumbnail_dimensions(&p, target_width, target_height);
+        let dimensions = get_thumbnail_dimensions(&p, target);
         assert_eq!(dimensions.unwrap(), (100, 200));
     }
 
@@ -86,10 +82,9 @@ mod test_get_thumbnail_dimensions {
     fn leaves_image_as_is_if_target_width_equal_to_width() {
         let p = PathBuf::from("src/tests/red.png");
 
-        let target_width: Option<u32> = Some(500);
-        let target_height: Option<u32> = None;
+        let target = TargetDimension::MaxWidth(500);
 
-        let dimensions = get_thumbnail_dimensions(&p, target_width, target_height);
+        let dimensions = get_thumbnail_dimensions(&p, target);
         assert_eq!(dimensions.unwrap(), (100, 200));
     }
 
@@ -97,10 +92,9 @@ mod test_get_thumbnail_dimensions {
     fn leaves_image_as_is_if_target_height_greater_than_height() {
         let p = PathBuf::from("src/tests/red.png");
 
-        let target_width: Option<u32> = None;
-        let target_height: Option<u32> = Some(500);
+        let target = TargetDimension::MaxHeight(500);
 
-        let dimensions = get_thumbnail_dimensions(&p, target_width, target_height);
+        let dimensions = get_thumbnail_dimensions(&p, target);
         assert_eq!(dimensions.unwrap(), (100, 200));
     }
 
@@ -108,10 +102,9 @@ mod test_get_thumbnail_dimensions {
     fn leaves_image_as_is_if_target_height_equal_to_height() {
         let p = PathBuf::from("src/tests/red.png");
 
-        let target_width: Option<u32> = None;
-        let target_height: Option<u32> = Some(500);
+        let target = TargetDimension::MaxHeight(500);
 
-        let dimensions = get_thumbnail_dimensions(&p, target_width, target_height);
+        let dimensions = get_thumbnail_dimensions(&p, target);
         assert_eq!(dimensions.unwrap(), (100, 200));
     }
 
@@ -119,10 +112,9 @@ mod test_get_thumbnail_dimensions {
     fn errors_if_image_does_not_exist() {
         let p = PathBuf::from("src/tests/doesnotexist.png");
 
-        let target_width: Option<u32> = Some(50);
-        let target_height: Option<u32> = None;
+        let target = TargetDimension::MaxWidth(50);
 
-        let dimensions = get_thumbnail_dimensions(&p, target_width, target_height);
+        let dimensions = get_thumbnail_dimensions(&p, target);
         assert!(dimensions.is_err());
     }
 
@@ -130,10 +122,9 @@ mod test_get_thumbnail_dimensions {
     fn errors_if_cannot_read_image() {
         let p = PathBuf::from("README.md");
 
-        let target_width: Option<u32> = Some(50);
-        let target_height: Option<u32> = None;
+        let target = TargetDimension::MaxWidth(50);
 
-        let dimensions = get_thumbnail_dimensions(&p, target_width, target_height);
+        let dimensions = get_thumbnail_dimensions(&p, target);
         assert!(dimensions.is_err());
     }
 }

src/main.rs (8684) → src/main.rs (8332)

diff --git a/src/main.rs b/src/main.rs
index 6d0f2d1..9d5a40b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -11,19 +11,15 @@ mod get_thumbnail_dimensions;
 mod is_animated_gif;
 
 use crate::create_parent_directory::create_parent_directory;
-use crate::get_thumbnail_dimensions::get_thumbnail_dimensions;
+use crate::get_thumbnail_dimensions::{get_thumbnail_dimensions, TargetDimension};
 use crate::is_animated_gif::is_animated_gif;
 
 /// Create a thumbnail for the image, and return the relative path of
 /// the thumbnail within the collection folder.
-///
-/// TODO: Having two Option<u32> arguments is confusing because they could
-/// easily be swapped.  Replace this with some sort of struct!
 pub fn create_thumbnail(
     path: &PathBuf,
     out_dir: &PathBuf,
-    width: Option<u32>,
-    height: Option<u32>,
+    target: TargetDimension,
 ) -> io::Result<PathBuf> {
     let thumbnail_path = out_dir.join(path.file_name().unwrap());
     create_parent_directory(&thumbnail_path)?;
@@ -31,15 +27,9 @@ pub fn create_thumbnail(
     // TODO: Does this check do what I think?
     assert!(*path != thumbnail_path);
 
-    println!("w = {:?}", width);
-    println!("h = {:?}", height);
-
-    let (new_width, new_height) = get_thumbnail_dimensions(&path, width, height)
+    let (new_width, new_height) = get_thumbnail_dimensions(&path, target)
         .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e.to_string()))?;
 
-    println!("w = {:?}, h = {:?}", new_width, new_height);
-
-    //
     if is_animated_gif(path)? {
         create_thumbnail::create_animated_gif_thumbnail(path, out_dir, new_width, new_height)
     } else {
@@ -52,17 +42,16 @@ mod test_create_thumbnail {
     use std::path::PathBuf;
 
     use super::create_thumbnail;
+    use crate::get_thumbnail_dimensions::TargetDimension;
     use crate::test_utils::{get_dimensions, test_dir};
 
     #[test]
     fn creates_an_animated_gif_thumbnail() {
         let gif_path = PathBuf::from("src/tests/animated_squares.gif");
         let out_dir = test_dir();
-        let target_width = Some(16);
-        let target_height = None;
+        let target = TargetDimension::MaxWidth(16);
 
-        let thumbnail_path =
-            create_thumbnail(&gif_path, &out_dir, target_width, target_height).unwrap();
+        let thumbnail_path = create_thumbnail(&gif_path, &out_dir, target).unwrap();
 
         assert_eq!(thumbnail_path, out_dir.join("animated_squares.mp4"));
         assert!(thumbnail_path.exists());
@@ -72,11 +61,9 @@ mod test_create_thumbnail {
     fn creates_a_static_gif_thumbnail() {
         let gif_path = PathBuf::from("src/tests/yellow.gif");
         let out_dir = test_dir();
-        let target_width = Some(16);
-        let target_height = None;
+        let target = TargetDimension::MaxWidth(16);
 
-        let thumbnail_path =
-            create_thumbnail(&gif_path, &out_dir, target_width, target_height).unwrap();
+        let thumbnail_path = create_thumbnail(&gif_path, &out_dir, target).unwrap();
 
         assert_eq!(thumbnail_path, out_dir.join("yellow.gif"));
         assert!(thumbnail_path.exists());
@@ -87,11 +74,9 @@ mod test_create_thumbnail {
     fn creates_a_png_thumbnail() {
         let gif_path = PathBuf::from("src/tests/red.png");
         let out_dir = test_dir();
-        let target_width = Some(16);
-        let target_height = None;
+        let target = TargetDimension::MaxWidth(16);
 
-        let thumbnail_path =
-            create_thumbnail(&gif_path, &out_dir, target_width, target_height).unwrap();
+        let thumbnail_path = create_thumbnail(&gif_path, &out_dir, target).unwrap();
 
         assert_eq!(thumbnail_path, out_dir.join("red.png"));
         assert!(thumbnail_path.exists());
@@ -102,11 +87,9 @@ mod test_create_thumbnail {
     fn creates_a_jpeg_thumbnail() {
         let gif_path = PathBuf::from("src/tests/noise.jpg");
         let out_dir = test_dir();
-        let target_width = Some(16);
-        let target_height = None;
+        let target = TargetDimension::MaxWidth(16);
 
-        let thumbnail_path =
-            create_thumbnail(&gif_path, &out_dir, target_width, target_height).unwrap();
+        let thumbnail_path = create_thumbnail(&gif_path, &out_dir, target).unwrap();
 
         assert_eq!(thumbnail_path, out_dir.join("noise.jpg"));
         assert!(thumbnail_path.exists());
@@ -117,11 +100,9 @@ mod test_create_thumbnail {
     fn creates_a_tif_thumbnail() {
         let gif_path = PathBuf::from("src/tests/green.tiff");
         let out_dir = test_dir();
-        let target_width = Some(16);
-        let target_height = None;
+        let target = TargetDimension::MaxHeight(16);
 
-        let thumbnail_path =
-            create_thumbnail(&gif_path, &out_dir, target_width, target_height).unwrap();
+        let thumbnail_path = create_thumbnail(&gif_path, &out_dir, target).unwrap();
 
         assert_eq!(thumbnail_path, out_dir.join("green.tiff"));
         assert!(thumbnail_path.exists());
@@ -132,11 +113,9 @@ mod test_create_thumbnail {
     fn creates_a_webp_thumbnail() {
         let gif_path = PathBuf::from("src/tests/purple.webp");
         let out_dir = test_dir();
-        let target_width = Some(16);
-        let target_height = None;
+        let target = TargetDimension::MaxWidth(16);
 
-        let thumbnail_path =
-            create_thumbnail(&gif_path, &out_dir, target_width, target_height).unwrap();
+        let thumbnail_path = create_thumbnail(&gif_path, &out_dir, target).unwrap();
 
         assert_eq!(thumbnail_path, out_dir.join("purple.webp"));
         assert!(thumbnail_path.exists());
@@ -173,7 +152,13 @@ fn main() {
 
     println!("args = {:?}", cli);
 
-    create_thumbnail(&cli.path, &cli.out_dir, cli.width, cli.height).unwrap();
+    let target = match (cli.width, cli.height) {
+        (Some(w), None) => TargetDimension::MaxWidth(w),
+        (None, Some(h)) => TargetDimension::MaxHeight(h),
+        _ => unreachable!(),
+    };
+
+    create_thumbnail(&cli.path, &cli.out_dir, target).unwrap();
 }
 
 #[cfg(test)]