From f0648bcc4e7a6b7ce3f59013f25413a667dc507a Mon Sep 17 00:00:00 2001 From: Julian T Date: Tue, 27 Jul 2021 17:46:28 +0200 Subject: Sort by timestamps and switch to thiserror crate --- Cargo.lock | 33 ++++++++++++++++++++++++++ Cargo.toml | 10 ++++---- src/context.rs | 50 ++++++++++++++++----------------------- src/main.rs | 20 ++++++++++------ src/picture.rs | 75 ++++++++++++++++++++++++++++++++-------------------------- src/piece.rs | 53 +++++++++++++++++++++++++++++++++++++---- 6 files changed, 162 insertions(+), 79 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8b9e736..cee8997 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -116,6 +116,7 @@ dependencies = [ "libc", "num-integer", "num-traits", + "time", "winapi", ] @@ -256,6 +257,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" name = "gallery" version = "0.1.0" dependencies = [ + "chrono", "glob", "image", "kamadak-exif", @@ -264,6 +266,7 @@ dependencies = [ "serde_yaml", "structopt", "tera", + "thiserror", ] [[package]] @@ -916,6 +919,26 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "thiserror" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.1.3" @@ -936,6 +959,16 @@ dependencies = [ "weezl", ] +[[package]] +name = "time" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "typenum" version = "1.13.0" diff --git a/Cargo.toml b/Cargo.toml index 7450ed5..6ae8868 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,11 +7,13 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tera = "1" +chrono = "0.4" +glob = "0.3" image = "0.23" +kamadak-exif = "0.5" +md5 = "0.7" serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.8" structopt = "0.3" -glob = "0.3" -md5 = "0.7" -kamadak-exif = "0.5" +tera = "1" +thiserror = "1" diff --git a/src/context.rs b/src/context.rs index f7b97ed..660c201 100644 --- a/src/context.rs +++ b/src/context.rs @@ -27,12 +27,28 @@ pub struct Options { pub url_prefix: Option, } -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] pub enum ConfigError { - Reading(io::Error), - Parsing(serde_yaml::Error), - CompilePattern(glob::PatternError), - CompileTemplate(tera::Error), + #[error("reading from file")] + Reading { + #[from] + source: io::Error, + }, + #[error("parsing config file")] + Parsing { + #[from] + source: serde_yaml::Error, + }, + #[error("compiling glob pattern")] + CompilePattern { + #[from] + source: glob::PatternError, + }, + #[error("compiling templates")] + CompileTemplate { + #[from] + source: tera::Error, + }, } #[derive(Deserialize, Debug, Serialize)] @@ -148,27 +164,3 @@ impl Config { } - -impl From for ConfigError { - fn from(error: io::Error) -> Self { - ConfigError::Reading(error) - } -} - -impl From for ConfigError { - fn from(error: serde_yaml::Error) -> Self { - ConfigError::Parsing(error) - } -} - -impl From for ConfigError { - fn from(error: glob::PatternError) -> Self { - ConfigError::CompilePattern(error) - } -} - -impl From for ConfigError { - fn from(error: tera::Error) -> Self { - ConfigError::CompileTemplate(error) - } -} diff --git a/src/main.rs b/src/main.rs index b647cdc..40b1faf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,20 +2,26 @@ mod context; mod picture; mod piece; +use std::error::Error; + use context::Context; use picture::Picture; use piece::Piece; -fn main() { +fn main() -> Result<(), Box> { println!("Hello, world!"); - let ctx = Context::new_with_args().unwrap(); + let ctx = Context::new_with_args()?; - let pieces = ctx.get_image_files().unwrap().iter() - .map(|file| Picture::new_from_file(&file).unwrap()) - .map(|pic| Piece::new(&ctx, pic).unwrap()) - .collect::>(); + let mut pieces = ctx.get_image_files()?.iter() + .map(|file| -> Result> { + let pic = Picture::new_from_file(&file)?; + Ok(Piece::new(&ctx, pic)?) + }).collect::, Box>>()?; + pieces.sort(); + pieces.reverse(); - piece::create_index(&ctx, &pieces[..]).unwrap(); + piece::create_index(&ctx, &pieces[..])?; + Ok(()) } diff --git a/src/picture.rs b/src/picture.rs index 3f78f90..d72fa84 100644 --- a/src/picture.rs +++ b/src/picture.rs @@ -6,28 +6,54 @@ use image::io::Reader as ImageReader; use image::error::ImageError; use image::imageops; use serde::Serialize; +use chrono::naive::NaiveDateTime; use crate::context::Context; -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] pub enum LoadError { - PathError, - Io(io::Error), - ExifParser(exif::Error) + #[error("not a valid path: `{0}`")] + PathError(String), + #[error("loading configuration file")] + Io { + #[from] + source: io::Error, + }, + #[error("parsing exif data")] + ExifParser { + #[from] + source: exif::Error, + }, + #[error("parsing taken datetime")] + ExifTimestamp { + #[from] + source: chrono::ParseError, + }, } -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] pub enum ConversionError { - Io(io::Error), - ImageError(ImageError), + #[error("reading picture from file")] + Io { + #[from] + source: io::Error, + }, + #[error("loading image")] + Image { + #[from] + source: ImageError, + }, } #[derive(Debug, Serialize)] pub struct Picture { - pub taken: Option, + taken: Option, hash: String, pub path: PathBuf, pub file_name: String, + + #[serde(skip)] + pub taken_chrono: Option, } pub struct Converter<'a> { @@ -67,16 +93,21 @@ impl Picture { } }; + let taken = taken.map(|taken| + NaiveDateTime::parse_from_str(&taken, "%Y-%m-%d %H:%M:%S") + ).transpose()?; + // Move back to start of file for hashing reader.seek(io::SeekFrom::Start(0))?; Ok(Picture { - taken, + taken_chrono: taken, + taken: taken.map(|taken| taken.format("%Y-%m-%d").to_string()), hash: hash_reader(&mut reader)?, path: path.to_path_buf(), file_name: match path.file_name() { Some(fname) => Ok(fname.to_string_lossy().to_string()), - None => Err(LoadError::PathError), + None => Err(LoadError::PathError(path.to_string_lossy().to_string())), }?, }) } @@ -130,27 +161,3 @@ impl Converter<'_> { } } } - -impl From for LoadError { - fn from(error: io::Error) -> Self { - Self::Io(error) - } -} - -impl From for LoadError { - fn from(error: exif::Error) -> Self { - Self::ExifParser(error) - } -} - -impl From for ConversionError { - fn from(error: io::Error) -> Self { - Self::Io(error) - } -} - -impl From for ConversionError { - fn from(error: ImageError) -> Self { - Self::ImageError(error) - } -} diff --git a/src/piece.rs b/src/piece.rs index 1ebad66..82d0313 100644 --- a/src/piece.rs +++ b/src/piece.rs @@ -1,24 +1,47 @@ use std::path::PathBuf; use std::collections::HashMap; +use std::fs; +use std::io; +use std::cmp; use crate::picture::{Picture, ConversionError}; use crate::context::Context; use tera::Context as WebContext; use serde::Serialize; +#[derive(Debug, thiserror::Error)] +pub enum RenderError { + #[error("saving to file")] + Io { + #[from] + source: io::Error, + }, + #[error("rendering template")] + Templating { + #[from] + source: tera::Error, + }, +} + #[derive(Debug, Serialize)] pub struct Piece { - pic: Picture, + pub pic: Picture, - scaled: HashMap, - info: Option, + pub scaled: HashMap, + pub info: Option, } -pub fn create_index(ctx: &Context, pieces: &[Piece]) -> Result<(), tera::Error> { +pub fn create_index(ctx: &Context, pieces: &[Piece]) -> Result<(), RenderError> { let mut web_ctx = WebContext::new(); web_ctx.insert("pieces", pieces); web_ctx.insert("opts", &ctx.options); - println!("render: {}", ctx.tmpl.render("index.html", &web_ctx)?); + println!("Rendering index template"); + + // Open file for writing and render template + let mut wr = fs::File::create(ctx.options.builddir.join("index.html"))?; + let mut wr = io::BufWriter::new(&mut wr); + + ctx.tmpl.render_to("index.html", &web_ctx, &mut wr)?; Ok(()) } @@ -42,3 +65,23 @@ impl Piece { }) } } + +impl cmp::Ord for Piece { + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.pic.taken_chrono.cmp(&other.pic.taken_chrono) + } +} + +impl cmp::PartialOrd for Piece { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl cmp::PartialEq for Piece { + fn eq(&self, _: &Self) -> bool { + false + } +} + +impl cmp::Eq for Piece {} -- cgit v1.2.3