about summary refs log tree commit diff
path: root/src/atom
diff options
context:
space:
mode:
Diffstat (limited to 'src/atom')
-rw-r--r--src/atom/mod.rs154
-rw-r--r--src/atom/urn.rs49
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()
+}