summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/context.rs50
-rw-r--r--src/main.rs20
-rw-r--r--src/picture.rs75
-rw-r--r--src/piece.rs53
4 files changed, 123 insertions, 75 deletions
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 {}