diff options
author | gennyble <gen@nyble.dev> | 2024-10-11 21:54:58 -0500 |
---|---|---|
committer | gennyble <gen@nyble.dev> | 2024-10-11 21:54:58 -0500 |
commit | c2a15e4447d7535fc3b9f8fabcfacf26947a84d2 (patch) | |
tree | 4b566334c8e55518fdf5fb2b0af58eaf49b37d69 /src/atom | |
parent | b0f6242b8b936d47b32c227cab5b18b4902cf9c4 (diff) | |
download | awake-c2a15e4447d7535fc3b9f8fabcfacf26947a84d2.tar.gz awake-c2a15e4447d7535fc3b9f8fabcfacf26947a84d2.zip |
truly i do not know what any of this is; git reinit
Diffstat (limited to 'src/atom')
-rw-r--r-- | src/atom/mod.rs | 154 | ||||
-rw-r--r-- | src/atom/urn.rs | 49 |
2 files changed, 203 insertions, 0 deletions
diff --git a/src/atom/mod.rs b/src/atom/mod.rs new file mode 100644 index 0000000..2020163 --- /dev/null +++ b/src/atom/mod.rs @@ -0,0 +1,154 @@ +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<AtomEntry>, +} + +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<P: AsRef<Utf8Path>>(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, + } +} diff --git a/src/atom/urn.rs b/src/atom/urn.rs new file mode 100644 index 0000000..9ec73ae --- /dev/null +++ b/src/atom/urn.rs @@ -0,0 +1,49 @@ +use core::fmt; + +use rand::{rngs::OsRng, Rng}; + +//TODO: gen- Check URN are valid +// https://www.rfc-editor.org/rfc/rfc2141#section-2.1 +pub struct Urn { + /// Namespace Identifier + nid: String, + /// Namespace Specific string. This is the unique part of the identifier + nss: String, +} + +impl Urn { + /// awake specific function for generating a URN in the form of: + /// `urn:NID:1234-5678` + /// Where 1234-5678 is a randomly generated eight digit ID broken in two. + pub fn new_random<S: Into<String>>(nid: S) -> Self { + let first = random_base58(4); + let second = random_base58(4); + + Self { + nid: nid.into(), + nss: format!("{first}-{second}"), + } + } +} + +impl fmt::Display for Urn { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Urn { nid, nss } = self; + + write!(f, "urn:{nid}:{nss}") + } +} + +const BASE58: &'static [u8] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; +const USER_ID_LENGTH: usize = 6; +const SESSION_ID_LENGTH: usize = 12; + +/// Random Base58 string, `count` characters long, using OsRng which is assumed +/// to be secure +/// > assumed that system always provides high-quality cryptographically secure random data +fn random_base58(count: usize) -> String { + let mut rng = OsRng::default(); + std::iter::from_fn(|| Some(BASE58[rng.gen_range(0..BASE58.len())] as char)) + .take(count) + .collect() +} |