summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock33
-rw-r--r--Cargo.toml10
-rw-r--r--src/context.rs50
-rw-r--r--src/main.rs20
-rw-r--r--src/picture.rs75
-rw-r--r--src/piece.rs53
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]]
@@ -917,6 +920,26 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -937,6 +960,16 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
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<String>,
}
-#[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<io::Error> for ConfigError {
- fn from(error: io::Error) -> Self {
- ConfigError::Reading(error)
- }
-}
-
-impl From<serde_yaml::Error> for ConfigError {
- fn from(error: serde_yaml::Error) -> Self {
- ConfigError::Parsing(error)
- }
-}
-
-impl From<glob::PatternError> for ConfigError {
- fn from(error: glob::PatternError) -> Self {
- ConfigError::CompilePattern(error)
- }
-}
-
-impl From<tera::Error> 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<dyn Error>> {
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::<Vec<Piece>>();
+ let mut pieces = ctx.get_image_files()?.iter()
+ .map(|file| -> Result<Piece, Box<dyn Error>> {
+ let pic = Picture::new_from_file(&file)?;
+ Ok(Piece::new(&ctx, pic)?)
+ }).collect::<Result<Vec<Piece>, Box<dyn Error>>>()?;
+ 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<String>,
+ taken: Option<String>,
hash: String,
pub path: PathBuf,
pub file_name: String,
+
+ #[serde(skip)]
+ pub taken_chrono: Option<NaiveDateTime>,
}
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<io::Error> for LoadError {
- fn from(error: io::Error) -> Self {
- Self::Io(error)
- }
-}
-
-impl From<exif::Error> for LoadError {
- fn from(error: exif::Error) -> Self {
- Self::ExifParser(error)
- }
-}
-
-impl From<io::Error> for ConversionError {
- fn from(error: io::Error) -> Self {
- Self::Io(error)
- }
-}
-
-impl From<ImageError> 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<String, PathBuf>,
- info: Option<String>,
+ pub scaled: HashMap<String, PathBuf>,
+ pub info: Option<String>,
}
-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<cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl cmp::PartialEq for Piece {
+ fn eq(&self, _: &Self) -> bool {
+ false
+ }
+}
+
+impl cmp::Eq for Piece {}