aboutsummaryrefslogtreecommitdiff
path: root/src/post
diff options
context:
space:
mode:
authorXe Iaso <me@christine.website>2023-09-30 10:36:37 -0400
committerGitHub <noreply@github.com>2023-09-30 10:36:37 -0400
commitac6a3df0d18cc73524c0096d954a57d24cad5669 (patch)
tree81474177d730440657f490ae29892d62392251ea /src/post
parentcbdea8ba3fca9a663778af71f8df5965aeb6c090 (diff)
downloadxesite-ac6a3df0d18cc73524c0096d954a57d24cad5669.tar.xz
xesite-ac6a3df0d18cc73524c0096d954a57d24cad5669.zip
Xesite V4 (#723)
* scripts/ditherify: fix quoting Signed-off-by: Xe Iaso <me@xeiaso.net> * clean up some old files Signed-off-by: Xe Iaso <me@xeiaso.net> * import site into lume Signed-off-by: Xe Iaso <me@xeiaso.net> * initial go code Signed-off-by: Xe Iaso <me@xeiaso.net> * move vods index to top level Signed-off-by: Xe Iaso <me@xeiaso.net> * remove the ads Signed-off-by: Xe Iaso <me@xeiaso.net> * internal/lume: metrics Signed-off-by: Xe Iaso <me@xeiaso.net> * delete old code Signed-off-by: Xe Iaso <me@xeiaso.net> * load config into memory Signed-off-by: Xe Iaso <me@xeiaso.net> * autogenerate data from dhall config Signed-off-by: Xe Iaso <me@xeiaso.net> * various cleanups, import clackset logic Signed-off-by: Xe Iaso <me@xeiaso.net> * Update signalboost.dhall (#722) Added myself, and also fixed someone’s typo * Add Connor Edwards to signal boost (#721) * add cache headers Signed-off-by: Xe Iaso <me@xeiaso.net> * move command to xesite folder Signed-off-by: Xe Iaso <me@xeiaso.net> * xesite: listen for GitHub webhook push events Signed-off-by: Xe Iaso <me@xeiaso.net> * xesite: 5 minute timeout for rebuilding the site Signed-off-by: Xe Iaso <me@xeiaso.net> * xesite: add rebuild metrics Signed-off-by: Xe Iaso <me@xeiaso.net> * xesite: update default variables Signed-off-by: Xe Iaso <me@xeiaso.net> * don't commit binaries oops lol Signed-off-by: Xe Iaso <me@xeiaso.net> * lume: make search have a light background Signed-off-by: Xe Iaso <me@xeiaso.net> * add a notfound page Signed-off-by: Xe Iaso <me@xeiaso.net> * fetch info from patreon API Signed-off-by: Xe Iaso <me@xeiaso.net> * create contact page Signed-off-by: Xe Iaso <me@xeiaso.net> * Toot embedding Signed-off-by: Xe Iaso <me@xeiaso.net> * attempt a docker image Signed-off-by: Xe Iaso <me@xeiaso.net> * lume: fix deno lock Signed-off-by: Xe Iaso <me@xeiaso.net> * add gokrazy post Signed-off-by: Xe Iaso <me@xeiaso.net> * cmd/xesite: go up before trying to connect to the saas proxy Signed-off-by: Xe Iaso <me@xeiaso.net> * blog: add Sine post/demo Signed-off-by: Xe Iaso <me@xeiaso.net> --------- Signed-off-by: Xe Iaso <me@xeiaso.net> Co-authored-by: bri <284789+b-@users.noreply.github.com> Co-authored-by: Connor Edwards <38229097+cedws@users.noreply.github.com>
Diffstat (limited to 'src/post')
-rw-r--r--src/post/frontmatter.rs99
-rw-r--r--src/post/mod.rs226
-rw-r--r--src/post/schemaorg.rs14
3 files changed, 0 insertions, 339 deletions
diff --git a/src/post/frontmatter.rs b/src/post/frontmatter.rs
deleted file mode 100644
index 67b96ab..0000000
--- a/src/post/frontmatter.rs
+++ /dev/null
@@ -1,99 +0,0 @@
-/// This code was borrowed from @fasterthanlime.
-use color_eyre::eyre::Result;
-pub use xesite_types::Frontmatter as Data;
-
-enum State {
- SearchForStart,
- ReadingMarker { count: usize, end: bool },
- ReadingFrontMatter { buf: String, line_start: bool },
- SkipNewline { end: bool },
-}
-
-#[derive(Debug, thiserror::Error)]
-enum Error {
- #[error("EOF while parsing frontmatter")]
- EOF,
- #[error("Error parsing yaml: {0:?}")]
- Yaml(#[from] serde_yaml::Error),
-}
-
-pub fn parse(input: &str) -> Result<(Data, usize)> {
- let mut state = State::SearchForStart;
-
- let mut payload = None;
- let offset;
-
- let mut chars = input.char_indices();
- 'parse: loop {
- let (idx, ch) = match chars.next() {
- Some(x) => x,
- None => return Err(Error::EOF)?,
- };
- match &mut state {
- State::SearchForStart => match ch {
- '-' => {
- state = State::ReadingMarker {
- count: 1,
- end: false,
- };
- }
- '\n' | '\t' | ' ' => {
- // ignore whitespace
- }
- _ => {
- panic!("Start of frontmatter not found");
- }
- },
- State::ReadingMarker { count, end } => match ch {
- '-' => {
- *count += 1;
- if *count == 3 {
- state = State::SkipNewline { end: *end };
- }
- }
- _ => {
- panic!("Malformed frontmatter marker");
- }
- },
- State::SkipNewline { end } => match ch {
- '\n' => {
- if *end {
- offset = idx + 1;
- break 'parse;
- } else {
- state = State::ReadingFrontMatter {
- buf: String::new(),
- line_start: true,
- };
- }
- }
- _ => panic!("Expected newline, got {:?}", ch),
- },
- State::ReadingFrontMatter { buf, line_start } => match ch {
- '-' if *line_start => {
- let mut state_temp = State::ReadingMarker {
- count: 1,
- end: true,
- };
- std::mem::swap(&mut state, &mut state_temp);
- if let State::ReadingFrontMatter { buf, .. } = state_temp {
- payload = Some(buf);
- } else {
- unreachable!();
- }
- }
- ch => {
- buf.push(ch);
- *line_start = ch == '\n';
- }
- },
- }
- }
-
- // unwrap justification: option set in state machine, Rust can't statically analyze it
- let payload = payload.unwrap();
-
- let fm: Data = serde_yaml::from_str(&payload)?;
-
- Ok((fm, offset))
-}
diff --git a/src/post/mod.rs b/src/post/mod.rs
deleted file mode 100644
index fb7d3bd..0000000
--- a/src/post/mod.rs
+++ /dev/null
@@ -1,226 +0,0 @@
-use chrono::prelude::*;
-use color_eyre::eyre::{eyre, Result, WrapErr};
-use glob::glob;
-use serde::{Deserialize, Serialize};
-use std::{borrow::Borrow, cmp::Ordering, path::PathBuf};
-use tokio::fs;
-
-pub mod frontmatter;
-pub mod schemaorg;
-
-#[derive(Eq, PartialEq, Debug, Clone, Serialize, Deserialize)]
-pub struct Post {
- pub front_matter: frontmatter::Data,
- pub link: String,
- pub body_html: String,
- pub date: DateTime<FixedOffset>,
- pub mentions: Vec<mi::WebMention>,
- pub new_post: NewPost,
- pub read_time_estimate_minutes: u64,
-}
-
-/// Used with the Android app to show information in a widget.
-#[derive(Eq, PartialEq, Debug, Clone, Serialize, Deserialize)]
-pub struct NewPost {
- pub title: String,
- pub summary: String,
- pub link: String,
-}
-
-impl Into<schemaorg::Article> for &Post {
- fn into(self) -> schemaorg::Article {
- schemaorg::Article {
- context: "https://schema.org".to_string(),
- r#type: "Article".to_string(),
- headline: self.front_matter.title.clone(),
- image: "https://xeiaso.net/static/img/avatar.png".to_string(),
- url: format!("https://xeiaso.net/{}", self.link),
- date_published: self.date.format("%Y-%m-%d").to_string(),
- }
- }
-}
-
-impl Into<xe_jsonfeed::Item> for Post {
- fn into(self) -> xe_jsonfeed::Item {
- let mut result = xe_jsonfeed::Item::builder()
- .title(self.front_matter.title.clone())
- .content_html(self.body_html)
- .id(format!("https://xeiaso.net/{}", self.link))
- .url(if let Some(url) = self.front_matter.redirect_to.as_ref() {
- url.clone()
- } else {
- format!("https://xeiaso.net/{}", self.link)
- })
- .date_published(self.date.to_rfc3339())
- .author(
- xe_jsonfeed::Author::new()
- .name("Xe Iaso")
- .url("https://xeiaso.net")
- .avatar("https://xeiaso.net/static/img/avatar.png"),
- )
- .xesite_frontmatter(self.front_matter.clone());
-
- let mut tags: Vec<String> = vec![];
-
- if let Some(series) = self.front_matter.series {
- tags.push(series);
- }
-
- if let Some(mut meta_tags) = self.front_matter.tags {
- tags.append(&mut meta_tags);
- }
-
- if tags.is_empty() {
- result = result.tags(tags);
- }
-
- if let Some(image_url) = self.front_matter.image {
- result = result.image(image_url);
- }
-
- result.build().unwrap()
- }
-}
-
-impl Ord for Post {
- fn cmp(&self, other: &Self) -> Ordering {
- self.partial_cmp(other).unwrap()
- }
-}
-
-impl PartialOrd for Post {
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- Some(self.date.cmp(&other.date))
- }
-}
-
-impl Post {
- pub fn detri(&self) -> String {
- self.date.format("M%m %d %Y").to_string()
- }
-}
-
-async fn read_post(dir: &str, fname: PathBuf, cli: &Option<mi::Client>) -> Result<Post> {
- debug!(
- "loading {}",
- fname.clone().into_os_string().into_string().unwrap()
- );
-
- let body = fs::read_to_string(fname.clone())
- .await
- .wrap_err_with(|| format!("can't read {:?}", fname))?;
- let (front_matter, content_offset) = frontmatter::parse(body.clone().as_str())
- .wrap_err_with(|| format!("can't parse frontmatter of {:?}", fname))?;
- let body = &body[content_offset..];
- let date = NaiveDate::parse_from_str(&front_matter.clone().date, "%Y-%m-%d")
- .map_err(|why| eyre!("error parsing date in {:?}: {}", fname, why))?;
- let link = format!("{}/{}", dir, fname.file_stem().unwrap().to_str().unwrap());
- let body_html = xesite_markdown::render(body)
- .wrap_err_with(|| format!("can't parse markdown for {:?}", fname))?;
- let date: DateTime<FixedOffset> = DateTime::<Utc>::from_utc(
- NaiveDateTime::new(date, NaiveTime::from_hms_opt(0, 0, 0).unwrap()),
- Utc,
- )
- .with_timezone(&Utc)
- .into();
-
- let mentions: Vec<mi::WebMention> = match cli {
- Some(cli) => cli
- .mentioners(format!("https://xeiaso.net/{}", link))
- .await
- .map_err(|why| tracing::error!("error: can't load mentions for {}: {}", link, why))
- .unwrap_or(vec![])
- .into_iter()
- .filter(|wm| {
- wm.title.as_ref().unwrap_or(&"".to_string()) != &"Bridgy Response".to_string()
- })
- .map(|mut wm| {
- wm.title = Some(
- mastodon2text::convert(
- wm.title.as_ref().unwrap_or(&"".to_string()).to_string(),
- )
- .unwrap(),
- );
- wm
- })
- .collect(),
- None => vec![],
- };
-
- let time_taken = estimated_read_time::text(
- body,
- &estimated_read_time::Options::new()
- .technical_document(true)
- .technical_difficulty(1)
- .build()
- .unwrap_or_default(),
- );
- let read_time_estimate_minutes = time_taken.seconds() / 60;
-
- let new_post = NewPost {
- title: front_matter.title.clone(),
- summary: format!("{} minute read", read_time_estimate_minutes),
- link: format!("https://xeiaso.net/{}", link),
- };
-
- Ok(Post {
- front_matter,
- link,
- body_html,
- date,
- mentions,
- new_post,
- read_time_estimate_minutes,
- })
-}
-
-pub async fn load(dir: &str) -> Result<Vec<Post>> {
- let cli = match std::env::var("MI_TOKEN") {
- Ok(token) => mi::Client::new(token, crate::APPLICATION_NAME.to_string()).ok(),
- Err(_) => None,
- };
-
- let futs = glob(&format!("{}/*.markdown", dir))?
- .filter_map(Result::ok)
- .map(|fname| read_post(dir, fname, cli.borrow()));
-
- let mut result: Vec<Post> = futures::future::join_all(futs)
- .await
- .into_iter()
- .map(Result::unwrap)
- .collect();
-
- if result.is_empty() {
- Err(eyre!("no posts loaded"))
- } else {
- result.sort();
- result.reverse();
- Ok(result)
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use color_eyre::eyre::Result;
-
- #[tokio::test]
- async fn blog() {
- let _ = pretty_env_logger::try_init();
- load("blog").await.expect("posts to load");
- }
-
- #[tokio::test]
- async fn gallery() -> Result<()> {
- let _ = pretty_env_logger::try_init();
- load("gallery").await?;
- Ok(())
- }
-
- #[tokio::test]
- async fn talks() -> Result<()> {
- let _ = pretty_env_logger::try_init();
- load("talks").await?;
- Ok(())
- }
-}
diff --git a/src/post/schemaorg.rs b/src/post/schemaorg.rs
deleted file mode 100644
index 18c238a..0000000
--- a/src/post/schemaorg.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-use serde::{Deserialize, Serialize};
-
-#[derive(Eq, PartialEq, Debug, Clone, Serialize, Deserialize)]
-pub struct Article {
- #[serde(rename = "@context")]
- pub context: String,
- #[serde(rename = "@type")]
- pub r#type: String,
- pub headline: String,
- pub image: String,
- pub url: String,
- #[serde(rename = "datePublished")]
- pub date_published: String,
-}