stapix

Yet another static page generator for photo galleries

Commit
9a6ecf01d9100b4b48d8bed3fbf7ff2c8b7b8a5c
Parent
022054eacbaa48faac9ac74c241f6437f46d636c
Author
Pablo <pablo-escobar@riseup.net>
Date

Refactored the structure of the mods

Moved the Picture code to it's own module

Moved the Escaped code to main.rs

Diffstat

3 files changed, 145 insertions, 144 deletions

Status File Name N° Changes Insertions Deletions
Modified src/main.rs 25 23 2
Added src/picture.rs 122 122 0
Deleted src/types.rs 142 0 142
diff --git a/src/main.rs b/src/main.rs
@@ -1,11 +1,15 @@
 use std::env;
+use std::fmt::{self, Display};
 use std::fs::{self, File};
 use std::io::{self, Write};
 use std::path::PathBuf;
 use std::process::exit;
-use types::{Escaped, Picture};
+use picture::Picture;
 
-mod types;
+mod picture;
+
+/// A wrapper for HTML-escaped strings
+pub struct Escaped<'a>(pub &'a str);
 
 const TARGET_PATH:  &str = "./site";
 const PAGES_PATH:   &str = "photos";
@@ -287,3 +291,20 @@ fn usage_config() {
     eprintln!("     alt: \"Text alternative for the second photo\"");
     eprintln!("   ...");
 }
+
+impl<'a> Display for Escaped<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+        for c in self.0.chars() {
+            match c {
+                '<'  => write!(f, "&lt;")?,
+                '>'  => write!(f, "&gt;")?,
+                '&'  => write!(f, "&amp;")?,
+                '"'  => write!(f, "&quot;")?,
+                '\'' => write!(f, "&apos;")?,
+                c    => c.fmt(f)?,
+            }
+        }
+
+        Ok(())
+    }
+}
diff --git a/src/picture.rs b/src/picture.rs
@@ -0,0 +1,122 @@
+use image::io::Reader as ImageReader;
+use image::DynamicImage;
+use serde::{
+    de::{Deserializer, Error, Unexpected},
+    Deserialize,
+};
+use std::fs::{self, File};
+use std::io::{self, Write};
+use std::path::PathBuf;
+use std::process::exit;
+
+/// WebP image quality
+const IMAGE_QUALITY: f32 = 50.0;
+
+/// Target height of the thumbnails, depending on wether the image is vertical
+/// or horizontal
+const HORIZONTAL_THUMB_HEIGHT: u32 = 300;
+const VERTICAL_THUMB_HEIGHT:   u32 = 800;
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Picture {
+    pub path: PathBuf,
+    pub file_name: String,
+    pub alt: String,
+}
+
+impl Picture {
+    pub fn render_thumbnail(&self, target_dir: &PathBuf) -> io::Result<()> {
+        let thumb_path = target_dir.join(self.file_name.clone() + ".webp");
+
+        // Only try to render thumbnail in case the thumbnail file in the machine
+        // is older than the source file
+        if let (Ok(thumb_meta), Ok(img_meta)) = (fs::metadata(&thumb_path), fs::metadata(&self.path)) {
+            if thumb_meta.modified()? > img_meta.modified()? {
+                // TODO: Report a warning to the user
+                return Ok(());
+            }
+        }
+
+        let mut thumb_file = match File::create(&thumb_path) {
+            Ok(f)    => f,
+            Err(err) => {
+                eprintln!("ERROR: Couldn't open WebP thumbnail file {thumb_path:?}: {err}");
+                exit(1);
+            }
+        };
+
+        let img_reader = match ImageReader::open(&self.path) {
+            Ok(r)    => r,
+            Err(err) => {
+                eprintln!(
+                    "ERROR: Couldn't open file {path:?} to render WebP thumbnail: {err}",
+                    path = self.file_name
+                );
+                exit(1);
+            }
+        };
+
+        let img = match img_reader.decode() {
+            Ok(img)  => img,
+            Err(err) => {
+                eprintln!(
+                    "ERROR: Failed to decode image file {name:?}: {err}",
+                    name = self.file_name
+                );
+                exit(1);
+            }
+        };
+
+        let h = if img.width() > img.height() {
+            HORIZONTAL_THUMB_HEIGHT
+        } else {
+            VERTICAL_THUMB_HEIGHT
+        };
+        let w = (h * img.width()) / img.height();
+
+        // We should make sure that the image is in the RGBA8 format so that
+        // the webp crate can encode it
+        let img = DynamicImage::from(img.thumbnail(w, h).into_rgba8());
+        let mem = webp::Encoder::from_image(&img)
+            .expect("image should be in the RGBA8 format")
+            .encode(IMAGE_QUALITY);
+
+        if let Err(err) = thumb_file.write_all(&mem) {
+            eprintln!("ERROR: Couldn't write WebP thumnail to file {thumb_path:?}: {err}");
+            exit(1);
+        }
+
+        Ok(())
+    }
+}
+
+impl<'de> Deserialize<'de> for Picture {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        #[derive(Deserialize)]
+        struct Info {
+            path: String,
+            alt: String,
+        }
+
+        let Info { path: path_str, alt, } = Info::deserialize(deserializer)?;
+
+        let mut path = PathBuf::new();
+        path.push(path_str.clone());
+
+        if let Some(file_name) = path.file_name().and_then(|s| s.to_str()) {
+            Ok(Self {
+                path: path.clone(),
+                alt: alt.trim().to_string(),
+                file_name: String::from(file_name),
+            })
+        } else {
+            Err(D::Error::invalid_value(
+                Unexpected::Str(&path_str),
+                &"Couldn't parse file name",
+            ))
+        }
+    }
+}
diff --git a/src/types.rs b/src/types.rs
@@ -1,142 +0,0 @@
-use image::io::Reader as ImageReader;
-use image::DynamicImage;
-use serde::{
-    de::{Deserializer, Error, Unexpected},
-    Deserialize,
-};
-use std::fmt::{self, Display};
-use std::fs::{self, File};
-use std::io::{self, Write};
-use std::path::PathBuf;
-use std::process::exit;
-
-/// WebP image quality
-const IMAGE_QUALITY: f32 = 50.0;
-
-/// Target height of the thumbnails, depending on wether the image is vertical
-/// or horizontal
-const HORIZONTAL_THUMB_HEIGHT: u32 = 300;
-const VERTICAL_THUMB_HEIGHT:   u32 = 800;
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct Picture {
-    pub path: PathBuf,
-    pub file_name: String,
-    pub alt: String,
-}
-
-pub struct Escaped<'a>(pub &'a str);
-
-impl Picture {
-    pub fn render_thumbnail(&self, target_dir: &PathBuf) -> io::Result<()> {
-        let thumb_path = target_dir.join(self.file_name.clone() + ".webp");
-
-        // Only try to render thumbnail in case the thumbnail file in the machine
-        // is older than the source file
-        if let (Ok(thumb_meta), Ok(img_meta)) = (fs::metadata(&thumb_path), fs::metadata(&self.path)) {
-            if thumb_meta.modified()? > img_meta.modified()? {
-                // TODO: Report a warning to the user
-                return Ok(());
-            }
-        }
-
-        let mut thumb_file = match File::create(&thumb_path) {
-            Ok(f)    => f,
-            Err(err) => {
-                eprintln!("ERROR: Couldn't open WebP thumbnail file {thumb_path:?}: {err}");
-                exit(1);
-            }
-        };
-
-        let img_reader = match ImageReader::open(&self.path) {
-            Ok(r)    => r,
-            Err(err) => {
-                eprintln!(
-                    "ERROR: Couldn't open file {path:?} to render WebP thumbnail: {err}",
-                    path = self.file_name
-                );
-                exit(1);
-            }
-        };
-
-        let img = match img_reader.decode() {
-            Ok(img)  => img,
-            Err(err) => {
-                eprintln!(
-                    "ERROR: Failed to decode image file {name:?}: {err}",
-                    name = self.file_name
-                );
-                exit(1);
-            }
-        };
-
-        let h = if img.width() > img.height() {
-            HORIZONTAL_THUMB_HEIGHT
-        } else {
-            VERTICAL_THUMB_HEIGHT
-        };
-        let w = (h * img.width()) / img.height();
-
-        // We should make sure that the image is in the RGBA8 format so that
-        // the webp crate can encode it
-        let img = DynamicImage::from(img.thumbnail(w, h).into_rgba8());
-        let mem = webp::Encoder::from_image(&img)
-            .expect("image should be in the RGBA8 format")
-            .encode(IMAGE_QUALITY);
-
-        if let Err(err) = thumb_file.write_all(&mem) {
-            eprintln!("ERROR: Couldn't write WebP thumnail to file {thumb_path:?}: {err}");
-            exit(1);
-        }
-
-        Ok(())
-    }
-}
-
-impl<'de> Deserialize<'de> for Picture {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: Deserializer<'de>,
-    {
-        #[derive(Deserialize)]
-        struct Info {
-            path: String,
-            alt: String,
-        }
-
-        let Info { path: path_str, alt, } = Info::deserialize(deserializer)?;
-
-        let mut path = PathBuf::new();
-        path.push(path_str.clone());
-
-        if let Some(file_name) = path.file_name().and_then(|s| s.to_str()) {
-            Ok(Self {
-                path: path.clone(),
-                alt: alt.trim().to_string(),
-                file_name: String::from(file_name),
-            })
-        } else {
-            Err(D::Error::invalid_value(
-                Unexpected::Str(&path_str),
-                &"Couldn't parse file name",
-            ))
-        }
-    }
-}
-
-impl<'a> Display for Escaped<'a> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
-        for c in self.0.chars() {
-            match c {
-                '<'  => write!(f, "&lt;")?,
-                '>'  => write!(f, "&gt;")?,
-                '&'  => write!(f, "&amp;")?,
-                '"'  => write!(f, "&quot;")?,
-                '\'' => write!(f, "&apos;")?,
-                c    => c.fmt(f)?,
-            }
-        }
-
-        Ok(())
-    }
-}