diff --git a/src/main.rs b/src/main.rs
@@ -1,24 +1,23 @@
+use image::io::Reader as ImageReader;
+use image::DynamicImage;
use std::env;
+use std::fs::{self, File};
use std::io::{self, Write};
+use std::path::PathBuf;
use std::process::exit;
-use std::fs::{self, File};
-use std::path::{PathBuf};
-use types::{PictureInfo, Escaped};
-use serde_yaml;
-use image::DynamicImage;
-use image::io::Reader as ImageReader;
-use webp;
+use types::{Escaped, PictureInfo};
mod types;
-const PAGE_TITLE: &'static str = "Pablo's Photo Gallery";
+const PAGE_TITLE: &str = "Pablo's Photo Gallery";
-const IMAGE_QUALITY: f32 = 50.0; /// WebP image quality
+/// 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;
+const VERTICAL_THUMB_HEIGHT: u32 = 800;
fn main() -> io::Result<()> {
let args: Vec<String> = env::args().collect();
@@ -29,28 +28,28 @@ fn main() -> io::Result<()> {
eprintln!("ERROR: Expected one command line argument, found none");
usage(program);
exit(1)
- },
+ }
[program, ..] => {
eprintln!("ERROR: Expected one command line argument, found many");
usage(program);
exit(1)
- },
- [] => unreachable!("args always contains at least the input program")
+ }
+ [] => unreachable!("args always contains at least the input program"),
};
- let f = File::open(&config);
+ let f = File::open(config);
match f.map(serde_yaml::from_reader::<_, Vec<PictureInfo>>) {
// Error opening the config file
Err(err) => {
eprintln!("ERROR: Couldn't open {config:?}: {err}");
Err(err)
- },
+ }
// Error parsing the config file
Ok(Err(err)) => {
eprintln!("ERROR: Couldn't parse {config:?}: {err}");
usage_config();
exit(1)
- },
+ }
Ok(Ok(pic_infos)) => render_gallery(pic_infos),
}
}
@@ -63,8 +62,11 @@ fn render_gallery(pic_infos: Vec<PictureInfo>) -> io::Result<()> {
target_path.push(&pic.file_name);
if let Err(err) = fs::copy(&pic.path, &target_path) {
- eprintln!("ERROR: Couldn't copy file {src:?} to {target:?}: {err}",
- src = pic.path, target = target_path);
+ eprintln!(
+ "ERROR: Couldn't copy file {src:?} to {target:?}: {err}",
+ src = pic.path,
+ target = target_path
+ );
return Err(err);
}
}
@@ -72,13 +74,17 @@ fn render_gallery(pic_infos: Vec<PictureInfo>) -> io::Result<()> {
// Warn the user if a particular path doesn't have an associated alt string
for pic in &pic_infos {
if pic.alt.is_empty() {
- eprintln!("WARNING: Empty text alternative was specified for the file {name:?}",
- name = pic.file_name);
+ eprintln!(
+ "WARNING: Empty text alternative was specified for the file {name:?}",
+ name = pic.file_name
+ );
}
}
// TODO: Parallelize this
- for pic in &pic_infos { render_thumbnail(pic)?; }
+ for pic in &pic_infos {
+ render_thumbnail(pic)?;
+ }
render_index(&pic_infos)?;
@@ -100,9 +106,11 @@ fn render_index(pic_infos: &Vec<PictureInfo>) -> io::Result<()> {
write_head(&mut f)?;
for pic in pic_infos {
- writeln!(f,
+ writeln!(
+ f,
"<link rel=\"preload\" as=\"image\" href=\"/assets/thumbs/{name}.webp\">",
- name = Escaped(&pic.file_name))?;
+ name = Escaped(&pic.file_name)
+ )?;
}
writeln!(f, "</head>")?;
@@ -114,11 +122,17 @@ fn render_index(pic_infos: &Vec<PictureInfo>) -> io::Result<()> {
for pic in pic_infos {
writeln!(f, "<li>")?;
- writeln!(f, "<a aria-label=\"{name}\" href=\"/photos/{name}.html\">",
- name = Escaped(&pic.file_name))?;
- writeln!(f, "<img alt=\"{alt}\" src=\"/assets/thumbs/{name}.webp\">",
- alt = Escaped(&pic.alt),
- name = Escaped(&pic.file_name))?;
+ writeln!(
+ f,
+ "<a aria-label=\"{name}\" href=\"/photos/{name}.html\">",
+ name = Escaped(&pic.file_name)
+ )?;
+ writeln!(
+ f,
+ "<img alt=\"{alt}\" src=\"/assets/thumbs/{name}.webp\">",
+ alt = Escaped(&pic.alt),
+ name = Escaped(&pic.file_name)
+ )?;
writeln!(f, "</a>\n</li>")?;
}
@@ -147,12 +161,17 @@ fn render_pic_page(pic: &PictureInfo) -> io::Result<()> {
write_license(&mut f)?;
writeln!(f, "<html lang=\"en\">")?;
writeln!(f, "<head>")?;
- writeln!(f, "<title>{PAGE_TITLE} ‐ {name}</title>",
- name = Escaped(&pic.file_name))?;
+ writeln!(
+ f,
+ "<title>{PAGE_TITLE} ‐ {name}</title>",
+ name = Escaped(&pic.file_name)
+ )?;
write_head(&mut f)?;
- writeln!(f,
- "<link rel=\"preload\" as=\"image\" href=\"/assets/photos/{n}\">",
- n = Escaped(&pic.file_name))?;
+ writeln!(
+ f,
+ "<link rel=\"preload\" as=\"image\" href=\"/assets/photos/{n}\">",
+ n = Escaped(&pic.file_name)
+ )?;
writeln!(f, "</head>")?;
writeln!(f, "<body>")?;
@@ -161,9 +180,12 @@ fn render_pic_page(pic: &PictureInfo) -> io::Result<()> {
writeln!(f, "<main>")?;
writeln!(f, "<figure>")?;
writeln!(f, "<div id=\"picture-container\">")?;
- writeln!(f, "<img alt=\"{alt}\" src=\"/assets/photos/{file_name}\">",
- alt = Escaped(&pic.alt),
- file_name = Escaped(&pic.file_name))?;
+ writeln!(
+ f,
+ "<img alt=\"{alt}\" src=\"/assets/photos/{file_name}\">",
+ alt = Escaped(&pic.alt),
+ file_name = Escaped(&pic.file_name)
+ )?;
writeln!(f, "</div>")?;
writeln!(f, "</figure>")?;
writeln!(f, "</main>")?;
@@ -187,18 +209,20 @@ fn render_thumbnail(pic: &PictureInfo) -> io::Result<()> {
}
let mut thumb_file = match File::create(&thumb_path) {
- Ok(f) => f,
+ Ok(f) => f,
Err(err) => {
eprintln!("ERROR: Couldn't open WebP thumbnail file {thumb_path:?}: {err}");
exit(1);
- },
+ }
};
let img_reader = match ImageReader::open(&pic.path) {
Ok(r) => r,
Err(err) => {
- eprintln!("ERROR: Couldn't open file {path:?} to render WebP thumbnail: {err}",
- path = pic.file_name);
+ eprintln!(
+ "ERROR: Couldn't open file {path:?} to render WebP thumbnail: {err}",
+ path = pic.file_name
+ );
exit(1);
}
};
@@ -206,8 +230,10 @@ fn render_thumbnail(pic: &PictureInfo) -> io::Result<()> {
let img = match img_reader.decode() {
Ok(img) => img,
Err(err) => {
- eprintln!("ERROR: Failed to decode image file {name:?}: {err}",
- name = pic.file_name);
+ eprintln!(
+ "ERROR: Failed to decode image file {name:?}: {err}",
+ name = pic.file_name
+ );
exit(1);
}
};
@@ -245,32 +271,65 @@ fn write_nav(f: &mut File) -> io::Result<()> {
fn write_footer(f: &mut File) -> io::Result<()> {
writeln!(f, "<footer>")?;
- writeln!(f, "made with 💛 by <a role=\"author\" href=\"https://pablopie.xyz\">@pablo</a>")?;
+ writeln!(
+ f,
+ "made with 💛 by <a role=\"author\" href=\"https://pablopie.xyz\">@pablo</a>"
+ )?;
writeln!(f, "</footer>")
}
/// Prints the common head elements to a given file
fn write_head(f: &mut File) -> io::Result<()> {
- writeln!(f, "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">")?;
+ writeln!(
+ f,
+ "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
+ )?;
writeln!(f, "<meta name=\"author\" content=\"Pablo\">")?;
writeln!(f, "<meta name=\"copyright\" content=\"GPLv2\">")?;
- writeln!(f, "<meta content=\"text/html; charset=utf-8\" http-equiv=\"content-type\">")?;
+ writeln!(
+ f,
+ "<meta content=\"text/html; charset=utf-8\" http-equiv=\"content-type\">"
+ )?;
writeln!(f, "<link rel=\"icon\" href=\"/assets/favicon.ico\" type=\"image/x-icon\" sizes=\"16x16 24x24 32x32\">")?;
writeln!(f, "<link rel=\"stylesheet\" href=\"/styles.css\">")
}
/// Prints a HTML comment with GPL licensing info
fn write_license(f: &mut File) -> io::Result<()> {
- writeln!(f, "<!-- This program is free software: you can redistribute it and/or modify")?;
- writeln!(f, " it under the terms of the GNU General Public License as published by")?;
- writeln!(f, " the Free Software Foundation, either version 3 of the License, or")?;
+ writeln!(
+ f,
+ "<!-- This program is free software: you can redistribute it and/or modify"
+ )?;
+ writeln!(
+ f,
+ " it under the terms of the GNU General Public License as published by"
+ )?;
+ writeln!(
+ f,
+ " the Free Software Foundation, either version 3 of the License, or"
+ )?;
writeln!(f, " (at your option) any later version.\n")?;
- writeln!(f, " This program is distributed in the hope that it will be useful,")?;
- writeln!(f, " but WITHOUT ANY WARRANTY; without even the implied warranty of")?;
- writeln!(f, " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the")?;
+ writeln!(
+ f,
+ " This program is distributed in the hope that it will be useful,"
+ )?;
+ writeln!(
+ f,
+ " but WITHOUT ANY WARRANTY; without even the implied warranty of"
+ )?;
+ writeln!(
+ f,
+ " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the"
+ )?;
writeln!(f, " GNU General Public License for more details.\n")?;
- writeln!(f, " You should have received a copy of the GNU General Public License")?;
- writeln!(f, " along with this program. If not, see <https://www.gnu.org/licenses/>. -->")
+ writeln!(
+ f,
+ " You should have received a copy of the GNU General Public License"
+ )?;
+ writeln!(
+ f,
+ " along with this program. If not, see <https://www.gnu.org/licenses/>. -->"
+ )
}
fn usage(program: &str) {
diff --git a/src/types.rs b/src/types.rs
@@ -1,6 +1,9 @@
-use std::path::PathBuf;
+use serde::{
+ de::{Deserializer, Error, Unexpected},
+ Deserialize,
+};
use std::fmt::{self, Display};
-use serde::{Deserialize, de::{Deserializer, Error, Unexpected}};
+use std::path::PathBuf;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PictureInfo {
@@ -13,30 +16,31 @@ pub struct Escaped<'a>(pub &'a str);
impl<'de> Deserialize<'de> for PictureInfo {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where D: Deserializer<'de>
+ where
+ D: Deserializer<'de>,
{
#[derive(Deserialize)]
- struct Info { path: String, alt: String, }
- let Info { path: path_str, alt } = Info::deserialize(deserializer)?;
+ 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)
- }
- )
+ 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"
- )
- )
+ Err(D::Error::invalid_value(
+ Unexpected::Str(&path_str),
+ &"Couldn't parse file name",
+ ))
}
}
}