|
|
|
use std::env;
|
|
|
|
use std::fs;
|
|
|
|
use std::fs::DirEntry;
|
|
|
|
use std::fs::File;
|
|
|
|
use std::io::prelude::*;
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use chrono;
|
|
|
|
use chrono::NaiveDate;
|
|
|
|
use markdown;
|
|
|
|
use std::fs::OpenOptions;
|
|
|
|
use rss::{Channel, ChannelBuilder, Item, ItemBuilder, Guid};
|
|
|
|
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
|
|
|
|
use image_utils;
|
|
|
|
use chrono::offset::TimeZone;
|
|
|
|
use chrono::Date;
|
|
|
|
use chrono::Utc;
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct Bread {
|
|
|
|
path: PathBuf,
|
|
|
|
rel_path: PathBuf,
|
|
|
|
date: chrono::NaiveDate,
|
|
|
|
note: String,
|
|
|
|
images: Vec<PathBuf>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Bread {
|
|
|
|
fn thumb_photo(&self) -> (&str, &str) {
|
|
|
|
let mut first_img : &PathBuf = self.images.get(0).unwrap();
|
|
|
|
|
|
|
|
for im in &self.images {
|
|
|
|
if im.file_name().unwrap().to_str().unwrap().contains("cover") {
|
|
|
|
first_img = im;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let img_path = first_img.to_str().unwrap();
|
|
|
|
let img_alt = first_img.file_name().unwrap().to_str().unwrap();
|
|
|
|
|
|
|
|
(img_path, img_alt)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse(base_dir : &PathBuf, bread_dir : &DirEntry) -> Result<Bread, std::io::Error> {
|
|
|
|
let bpath = bread_dir.path();
|
|
|
|
let mut note = String::new();
|
|
|
|
|
|
|
|
let mut note_path = bpath.join("note.txt");
|
|
|
|
|
|
|
|
// try a md one as a fallback
|
|
|
|
if !note_path.exists() {
|
|
|
|
note_path = bpath.join("note.md");
|
|
|
|
}
|
|
|
|
|
|
|
|
if note_path.exists() {
|
|
|
|
let mut note_file = File::open(note_path)?;
|
|
|
|
note_file.read_to_string(&mut note)?;
|
|
|
|
note = markdown::to_html(¬e);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut bread_files: Vec<DirEntry> = fs::read_dir(&bpath)?.map(|e| e.unwrap()).collect();
|
|
|
|
bread_files.sort_by(|x, y| x.file_name().cmp(&y.file_name()));
|
|
|
|
|
|
|
|
let images = bread_files.iter().filter(|&f| {
|
|
|
|
let fname = f.file_name();
|
|
|
|
let name = fname.to_str().unwrap();
|
|
|
|
return
|
|
|
|
name.ends_with(".png") ||
|
|
|
|
name.ends_with(".jpg") ||
|
|
|
|
name.ends_with(".jpeg") ||
|
|
|
|
name.ends_with(".gif");
|
|
|
|
}).map(|x| x.path().strip_prefix(base_dir).unwrap().to_path_buf()).collect();
|
|
|
|
|
|
|
|
return Ok(Bread {
|
|
|
|
date: NaiveDate::parse_from_str(bpath.file_name().unwrap().to_str().unwrap(), "%Y-%m-%d").unwrap(),
|
|
|
|
rel_path: bpath.strip_prefix(base_dir).unwrap().to_path_buf(),
|
|
|
|
path: bpath,
|
|
|
|
note,
|
|
|
|
images
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
let cwd = env::current_dir().unwrap();
|
|
|
|
let web_path = Path::new(&cwd).join("web");
|
|
|
|
let data_path = web_path.join("data");
|
|
|
|
let tpl_path = web_path.join("templates");
|
|
|
|
let thumbs_path = web_path.join("thumbs");
|
|
|
|
|
|
|
|
let mut bread_dirs: Vec<DirEntry> = fs::read_dir(&data_path).unwrap().map(|e| e.unwrap()).collect();
|
|
|
|
bread_dirs.sort_by(|x, y| x.file_name().cmp(&y.file_name()));
|
|
|
|
|
|
|
|
let mut breads : Vec<Bread> = Vec::new();
|
|
|
|
|
|
|
|
for bread_dir in bread_dirs {
|
|
|
|
if let Ok(b) = Bread::parse(&web_path, &bread_dir) {
|
|
|
|
breads.push(b);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut main_tpl = String::new();
|
|
|
|
let mut thumb_tpl = String::new();
|
|
|
|
let mut detail_tpl = String::new();
|
|
|
|
File::open(tpl_path.join("index.html")).unwrap().read_to_string(&mut main_tpl).unwrap();
|
|
|
|
File::open(tpl_path.join("_thumb.html")).unwrap().read_to_string(&mut thumb_tpl).unwrap();
|
|
|
|
File::open(tpl_path.join("detail.html")).unwrap().read_to_string(&mut detail_tpl).unwrap();
|
|
|
|
|
|
|
|
let mut thumbs = String::new();
|
|
|
|
|
|
|
|
let mut channel : Channel = ChannelBuilder::default()
|
|
|
|
.title("Piggo's Bread Gallery")
|
|
|
|
.link("https://www.ondrovo.com/bread")
|
|
|
|
.description("Sourdough feed")
|
|
|
|
.build()
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let mut channel_items = Vec::<Item>::new();
|
|
|
|
|
|
|
|
for bread in &breads {
|
|
|
|
let date = bread.date.format("%Y/%m/%d").to_string();
|
|
|
|
let date_slug = bread.date.format("%Y-%m-%d").to_string();
|
|
|
|
let detail_file = date_slug.clone() + ".html";
|
|
|
|
|
|
|
|
let (img_path, img_alt) = bread.thumb_photo();
|
|
|
|
let note = if bread.note.is_empty() { "<i>There's no note about this bread.</i>" } else { &bread.note };
|
|
|
|
|
|
|
|
let thumb_fname = date_slug.clone() + "." + Path::new(&img_path).extension().unwrap().to_str().unwrap();
|
|
|
|
let thumb_path = thumbs_path.join(&thumb_fname);
|
|
|
|
let thumb_relpath = thumb_path.strip_prefix(&web_path).unwrap();
|
|
|
|
|
|
|
|
let image_path_encoded = utf8_percent_encode(thumb_relpath.to_str().unwrap(), DEFAULT_ENCODE_SET).to_string();
|
|
|
|
|
|
|
|
let im = image::open(&web_path.join(img_path)).unwrap();
|
|
|
|
let im = im.thumbnail(500, 500);
|
|
|
|
//let mut output = File::create(&thumb_path);
|
|
|
|
im.save(&thumb_path).unwrap();
|
|
|
|
|
|
|
|
//image_utils::resize(&web_path.join(img_path), 500, 500, &thumb_path).unwrap();
|
|
|
|
|
|
|
|
// bread pic for the thumbnails page
|
|
|
|
{
|
|
|
|
let thumb = thumb_tpl
|
|
|
|
.replace("{detail_url}", &detail_file)
|
|
|
|
.replace("{img_src}", &image_path_encoded)
|
|
|
|
.replace("{img_alt}", &img_alt)
|
|
|
|
.replace("{title}", &date);
|
|
|
|
|
|
|
|
thumbs.push_str(&thumb);
|
|
|
|
}
|
|
|
|
|
|
|
|
// add to rss
|
|
|
|
{
|
|
|
|
let image_url : String = channel.link().to_string() + "/" + &image_path_encoded;
|
|
|
|
|
|
|
|
let link : String = channel.link().to_string() + "/" + &detail_file;
|
|
|
|
let mut guid = Guid::default();
|
|
|
|
guid.set_value(link.clone());
|
|
|
|
guid.set_permalink(true);
|
|
|
|
|
|
|
|
let date_formatted : Date<Utc> = chrono::Utc.from_local_date(&bread.date).unwrap();
|
|
|
|
let dt = date_formatted.and_hms(12,0,0);
|
|
|
|
|
|
|
|
channel_items.push(ItemBuilder::default()
|
|
|
|
.title(date.clone())
|
|
|
|
.link(link.clone())
|
|
|
|
.description(note.to_string() + &format!("<img src=\"{}\" alt=\"{}\"><p>Open the link for more...</p>", image_url, img_alt))
|
|
|
|
.guid(guid)
|
|
|
|
.pub_date(dt.to_rfc2822())
|
|
|
|
.build().unwrap());
|
|
|
|
}
|
|
|
|
|
|
|
|
// generate the detail page
|
|
|
|
{
|
|
|
|
let detail = detail_tpl
|
|
|
|
.replace("{title}", &date)
|
|
|
|
.replace("{note}", note);
|
|
|
|
|
|
|
|
let mut pics = String::new();
|
|
|
|
for img in &bread.images {
|
|
|
|
pics.push_str(&format!("<a href=\"{src}\"><img src=\"{src}\"><a/>", src=&utf8_percent_encode(img.to_str().unwrap(), DEFAULT_ENCODE_SET).to_string()))
|
|
|
|
}
|
|
|
|
|
|
|
|
let detail = detail.replace("{images}", &pics);
|
|
|
|
|
|
|
|
let mut f = OpenOptions::new().write(true).truncate(true).create(true).open(web_path.join(detail_file)).unwrap();
|
|
|
|
f.write(detail.as_bytes()).unwrap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let main = main_tpl.replace("{breads}", &thumbs);
|
|
|
|
|
|
|
|
{
|
|
|
|
let mut f = OpenOptions::new().write(true).truncate(true).create(true).open(web_path.join("index.html")).unwrap();
|
|
|
|
f.write(main.as_bytes()).unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let f = OpenOptions::new().write(true).truncate(true).create(true).open(web_path.join("feed.xml")).unwrap();
|
|
|
|
channel.set_items(channel_items);
|
|
|
|
channel.pretty_write_to(f, b' ', 2).unwrap();
|
|
|
|
}
|
|
|
|
}
|