summaryrefslogtreecommitdiff
path: root/src/blog
diff options
context:
space:
mode:
Diffstat (limited to 'src/blog')
-rw-r--r--src/blog/atom.rs75
-rw-r--r--src/blog/helper.rs72
-rw-r--r--src/blog/mod.rs146
3 files changed, 0 insertions, 293 deletions
diff --git a/src/blog/atom.rs b/src/blog/atom.rs
deleted file mode 100644
index a29ffb1..0000000
--- a/src/blog/atom.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-use super::{
- helper::{get_articles, ArticleMeta},
- rocket_uri_macro_r_blog_article, rocket_uri_macro_r_blog_index, ARTICLE_ROOT,
-};
-use crate::{error::MyResult, uri};
-use rocket::get;
-use std::{path::PathBuf, str::FromStr};
-
-#[get("/blog/feed.atom")]
-pub async fn r_blog_atom() -> MyResult<String> {
- let entries = get_articles(&PathBuf::from_str(ARTICLE_ROOT).unwrap())
- .await?
- .iter()
- .map(
- |ArticleMeta {
- title,
- date,
- canonical_name,
- ..
- }| {
- let title = horrible_escape_function(title);
- let datetime = iso8601::DateTime {
- date: date.clone(),
- time: iso8601::Time::default(),
- };
- let href = uri!(r_blog_article(canonical_name));
- format!(
- r#"
- <entry>
- <title>{title}</title>
- <link href="{href}" />
- <id>tag:metamuffin.org,{date},{canonical_name}</id>
- <published>{datetime}</published>
- <summary>N/A</summary>
- <author>
- <name>metamuffin</name>
- <email>metamuffin@disroot.org</email>
- </author>
- </entry>"#
- )
- },
- )
- .collect::<Vec<_>>();
-
- let feed_url = uri!(r_blog_atom());
- let index_url = uri!(r_blog_index());
- let now = chrono::Utc::now().to_rfc3339();
-
- Ok(format!(
- r#"<?xml version="1.0" encoding="utf-8"?>
-<feed xmlns="http://www.w3.org/2005/Atom">
- <title>metamuffin's blog</title>
- <subtitle>where they post pointless stuff</subtitle>
- <link href="{feed_url}" rel="self" />
- <link href="{index_url}" />
- <id>urn:uuid:3cf2b704-3d94-4f1f-b194-42798ab5b47c</id>
- <updated>{now}</updated>
- <author>
- <name>metamuffin</name>
- <email>metamuffin@disroot.org</email>
- </author>
- {}
-</feed>
- "#,
- entries.join("\n")
- ))
-}
-
-pub fn horrible_escape_function(text: &str) -> String {
- text.replace("&", "&amp;")
- .replace("<", "&lt;")
- .replace(">", "&gt;")
- .replace("'", "&#8217;")
- .replace("\"", "&quot;")
-}
diff --git a/src/blog/helper.rs b/src/blog/helper.rs
deleted file mode 100644
index 35396c0..0000000
--- a/src/blog/helper.rs
+++ /dev/null
@@ -1,72 +0,0 @@
-use anyhow::{anyhow, Context};
-use futures::future::join_all;
-use std::{
- path::{Path, PathBuf},
- process::Stdio,
-};
-use tokio::{
- fs::File,
- io::{AsyncBufReadExt, BufReader},
- process::Command,
-};
-
-#[derive(Clone)]
-pub struct ArticleMeta {
- pub title: String,
- pub canonical_name: String,
- pub date: iso8601::Date,
-}
-
-pub async fn article_metadata(path: PathBuf) -> anyhow::Result<ArticleMeta> {
- let f = File::open(&path).await.context("article not found")?;
- let mut f = BufReader::new(f);
- let mut buf = String::new();
- f.read_line(&mut buf).await.context("cant read the file")?; // assume the 1st line has the title
- Ok(ArticleMeta {
- title: String::from(buf[2..].trim()),
- canonical_name: path
- .file_stem()
- .ok_or(anyhow!("no file stem"))?
- .to_str()
- .ok_or(anyhow!("this file's name is broken"))?
- .to_string(),
- date: iso8601::date(
- &path
- .file_name()
- .ok_or(anyhow!("file has no name"))?
- .to_str()
- .ok_or(anyhow!("this file's name is broken"))?[0..10],
- )
- .map_err(|e| anyhow!("file date wrong: {e}"))?,
- })
-}
-
-pub async fn get_articles(blog_root: &Path) -> anyhow::Result<Vec<ArticleMeta>> {
- let mut a = join_all(
- std::fs::read_dir(blog_root)?
- .map(|e| e.unwrap())
- .map(|e| article_metadata(e.path())),
- )
- .await
- .into_iter()
- .collect::<anyhow::Result<Vec<_>>>()?;
- a.sort_by_cached_key(|e| -match e.date {
- iso8601::Date::YMD { year, month, day } => day as i32 + month as i32 * 100 + year * 10000,
- _ => unreachable!(),
- });
- Ok(a)
-}
-
-// TODO use this somewhere
-pub async fn file_history(filename: &str) -> String {
- String::from_utf8(
- Command::new("/usr/bin/git")
- .args(&["log", "--follow", "--pretty=tformat:%as %h %s", filename])
- .stdout(Stdio::piped())
- .output()
- .await
- .unwrap()
- .stdout,
- )
- .unwrap()
-}
diff --git a/src/blog/mod.rs b/src/blog/mod.rs
deleted file mode 100644
index b3ab5a4..0000000
--- a/src/blog/mod.rs
+++ /dev/null
@@ -1,146 +0,0 @@
-pub mod atom;
-pub mod helper;
-
-use self::helper::{article_metadata, get_articles};
-use crate::error::MyResult;
-use crate::layout::{DynScaffold, Scaffold};
-use crate::uri;
-use anyhow::anyhow;
-pub use atom::r_blog_atom;
-use atom::rocket_uri_macro_r_blog_atom;
-use latex2mathml::DisplayStyle;
-use markdown::mdast::Node;
-use markup::{raw, DynRender};
-use rocket::{get, response::Redirect};
-use std::{path::PathBuf, str::FromStr};
-use syntect::highlighting::ThemeSet;
-use syntect::html::highlighted_html_for_string;
-use syntect::parsing::SyntaxSet;
-use tokio::fs::read_to_string;
-
-pub const ARTICLE_ROOT: &'static str = "./blog/articles";
-pub const ASSET_ROOT: &'static str = "./blog/assets";
-
-#[get("/blog")]
-pub fn r_blog() -> Redirect {
- Redirect::to(rocket::uri!(r_blog_index()))
-}
-
-#[get("/blog/index")]
-pub async fn r_blog_index() -> MyResult<DynScaffold<'static>> {
- // TODO this is a major performance issue here. requires O(n) syscalls to complete
- let articles = get_articles(&PathBuf::from_str(ARTICLE_ROOT).unwrap()).await?;
- Ok(Scaffold {
- title: "blog index".to_string(),
- content: markup::new! {
- h2 { "The Weblog" }
- p { i { "Articles in reverse-chronological order." } }
- p { a[href=uri!(r_blog_atom())]{ "Atom feed" } }
- ul {
- @for a in &articles {
- li {
- @a.date.to_string() ": "
- a[href=uri!(r_blog_article(&a.canonical_name))] { @a.title }
- }
- }
- }
- },
- })
-}
-
-#[get("/blog/<name>")]
-pub async fn r_blog_article(name: &str) -> MyResult<DynScaffold<'static>> {
- let apath = PathBuf::from_str(ARTICLE_ROOT)
- .unwrap()
- .join(PathBuf::new().with_file_name(name).with_extension("md"));
- let a = article_metadata(apath.clone()).await?;
- let text = read_to_string(apath).await?;
- let ast = markdown::to_mdast(
- &text,
- &markdown::ParseOptions {
- constructs: markdown::Constructs {
- math_flow: true,
- math_text: true,
- ..Default::default()
- },
- ..Default::default()
- },
- )
- .map_err(|e| anyhow!("the server had trouble parsing markdown: {e}"))?;
-
- Ok(Scaffold {
- title: a.title,
- content: markup::new! {
- @node_to_render(&ast)
- p{i{ "Article written by metamuffin, text licenced under CC BY-ND 4.0, non-trivial code blocks under GPL-3.0-only except where indicated otherwise" }}
- },
- })
-}
-
-fn node_to_render<'a>(node: &'a Node) -> DynRender<'a> {
- match node {
- Node::Text(s) => markup::new!(@s.value),
- Node::Paragraph(el) => markup::new!(p { @for e in &el.children { @node_to_render(e) } }),
- Node::List(list) => markup::new!(ul { @for e in &list.children { @node_to_render(e) } }),
- Node::Root(el) => markup::new!(article { @for e in &el.children { @node_to_render(e) } }),
- Node::ListItem(el) => markup::new!(li { @for e in &el.children { @node_to_render(e) } }),
- Node::Emphasis(el) => markup::new!(i { @for e in &el.children { @node_to_render(e) } }),
- Node::Strong(el) => markup::new!(strong { @for e in &el.children { @node_to_render(e) } }),
- Node::Html(html) => markup::new!(@raw(&html.value)),
- Node::Link(l) => {
- markup::new!(a[href=&l.url, alt=&l.title] { @for e in &l.children { @node_to_render(e) } })
- }
- Node::Break(_) => markup::new!(br;),
- Node::InlineCode(s) => markup::new!(code { @s.value }),
- Node::Delete(_) => markup::new!("todo1"),
- Node::FootnoteReference(_) => markup::new!("todo3"),
- Node::FootnoteDefinition(_) => markup::new!("todo4"),
- Node::Image(_) => markup::new!("todo5"),
- Node::ImageReference(_) => markup::new!("todo6"),
- Node::LinkReference(_) => markup::new!("todo8"),
- Node::Blockquote(_) => markup::new!("todo10"),
- Node::Table(_) => markup::new!("todo12"),
- Node::ThematicBreak(_) => markup::new!("todo13"),
- Node::TableRow(_) => markup::new!("todo14"),
- Node::TableCell(_) => markup::new!("todo15"),
- Node::Definition(_) => markup::new!("todo16"),
- Node::Toml(_)
- | Node::Yaml(_)
- | Node::MdxjsEsm(_)
- | Node::MdxJsxFlowElement(_)
- | Node::MdxJsxTextElement(_)
- | Node::MdxTextExpression(_)
- | Node::MdxFlowExpression(_) => markup::new!("unsupported"),
- Node::Heading(h) => {
- let inner = markup::new!(@for e in &h.children { @node_to_render(e) });
- match h.depth {
- 1 => markup::new!(h2{@inner}),
- 2 => markup::new!(h3{@inner}),
- 3 => markup::new!(h4{@inner}),
- 4 => markup::new!(h5{@inner}),
- 5 => markup::new!(h6{@inner}),
- 6 => markup::new!(h6{@inner}),
- _ => unreachable!(),
- }
- }
- Node::Code(code) => {
- let theme = &ThemeSet::load_defaults().themes["base16-ocean.dark"];
- let syntax = &SyntaxSet::load_defaults_newlines();
- let lang = syntax
- .find_syntax_by_extension(&code.lang.to_owned().unwrap_or("txt".to_owned()))
- .unwrap_or_else(|| syntax.find_syntax_by_extension("txt").unwrap());
- let html = highlighted_html_for_string(&code.value, syntax, lang, theme).unwrap();
- markup::new!(@raw(&html))
- }
- Node::Math(s) => {
- let mathml = latex2mathml::latex_to_mathml(&s.value, DisplayStyle::Block)
- .expect("invalid block math");
- markup::new!(@raw(&mathml))
- }
- Node::InlineMath(s) => {
- let mathml = latex2mathml::latex_to_mathml(&s.value, DisplayStyle::Inline)
- .expect("invalid inline math");
- markup::new!(@raw(&mathml))
- }
- }
-}