mod urn; use std::str::FromStr; use bempline::{variables, Document, Options}; use camino::{Utf8Path, Utf8PathBuf}; use confindent::{let_get, Confindent, Entry, Node}; use time::{format_description::well_known, OffsetDateTime}; use urn::Urn; use crate::{markup, templated::Templated, timeparse}; pub fn main() -> ! { let awake_conf = Confindent::from_file(std::env::args().nth(2).unwrap()).unwrap(); let root: Utf8PathBuf = awake_conf.child_parse("Webroot").unwrap(); // Grab the atom config and then grab the atom template let atom_conf_path = std::env::args().nth(3).unwrap_or(String::from("atom.conf")); let mut atom_conf = Confindent::from_file(&atom_conf_path).unwrap(); let atom = Document::from_file( root.clone() .join(atom_conf.child_value("Template").unwrap()) .canonicalize_utf8() .unwrap(), Options::default(), ) .unwrap(); let mut changed = false; // Go through every feed and create the entries let feeds = atom_conf.children_mut("Feed"); for feed in feeds { make_feed(feed, &mut changed, &root, atom.clone()) } // If the atom has changed, probably because we created an ID // write it to disk. if changed { std::fs::write(atom_conf_path, atom_conf.to_string()); } std::process::exit(0); } pub struct AtomFeed { /// Where is the atom located in the webroot. (relative to webroot) relpath: Utf8PathBuf, /// What unique ID does this Feed have? id: String, title: String, subtitle: String, updated: OffsetDateTime, author_name: String, author_email: String, entries: Vec, } pub struct AtomEntry { /// What file does this entry direct to? This file will be opened to inspect /// the frontmatter for information to fill out the entry with. It will also /// be used to create the link to the entry. relpath: Utf8PathBuf, /// What unique ID does this Entry have? id: String, title: String, published: OffsetDateTime, updated: OffsetDateTime, } /// Parses all the options of a feed and generates the build job pub fn make_feed(feed: &mut Entry, changed: &mut bool, root: &Utf8Path, mut atom: Document) { let_get!( feed, relpath: Utf8PathBuf = "Output", clone id = "Id", clone title = "Title", clone subtitle = "Subtitle", clone updated = "Updated", ); //FIXME: gen- We need to backresolve the feed URL let feed_link = ""; let author_name = feed.get("Author/Name").unwrap().to_owned(); let author_email = feed.get("Author/Email").unwrap().to_owned(); let mut atomfeed = AtomFeed { relpath, id, title, subtitle, updated: timeparse::parse(&updated).unwrap(), author_name, author_email, entries: vec![], }; let entries = feed.children_mut("Entry"); for entry in entries { if !entry.has_child("Id") { //TODO: gen- Read NID from conf? let id = Urn::new_random("dreamy"); entry.push_entry(("Id", id)); *changed = true; } atomfeed.entries.push(make_entry(entry, &root)); } } fn make_entry>(entry: &Entry, webroot: P) -> AtomEntry { let relpath: Utf8PathBuf = entry.parse().unwrap(); let path = webroot.as_ref().join(&relpath); let entry_content = match std::fs::read_to_string(&path) { Ok(ok) => ok, Err(e) => { panic!("failed to get file at {path}: {e}"); } }; let entry_templated = Templated::from_str(&entry_content).unwrap(); let conf_or_frontmatter = |conf_key: &str, front_key: &str| -> &str { entry .child_value(conf_key) .unwrap_or_else(|| entry_templated.frontmatter.get(front_key).unwrap()) }; let title = conf_or_frontmatter("Title", "title").to_owned(); let id = entry.child_value("Id").unwrap().to_owned(); let entry_published_raw = conf_or_frontmatter("Published", "published"); let updated = timeparse::parse(entry.child_value("Updated").unwrap_or_else(|| { entry_templated .frontmatter .get("updated") .unwrap_or(entry_published_raw) })) .unwrap(); let published = timeparse::parse(entry_published_raw).unwrap(); AtomEntry { relpath, id, title, published, updated, } }