bread gallery data and generator script
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
bread-gallery/src/main.rs

207 lines
7.4 KiB

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(&note);
}
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();
let mut head_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("_head.html")).unwrap().read_to_string(&mut head_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("{head}", &head_tpl.replace("{title}", &format!("Bread from {}", date)).trim())
.replace("{title}", &date)
.replace("{note}", note.trim());
let mut pics = String::new();
for img in &bread.images {
pics.push_str(&format!(" <a href=\"{src}\"><img src=\"{src}\"></a>\n", src=&utf8_percent_encode(img.to_str().unwrap(), DEFAULT_ENCODE_SET).to_string()))
}
let detail = detail.replace("{images}", &pics.trim());
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.trim())
.replace("{head}", &head_tpl.replace("{title}", "Piggo's breads").trim());
{
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();
}
}