summaryrefslogtreecommitdiff
path: root/src/blog
diff options
context:
space:
mode:
Diffstat (limited to 'src/blog')
-rw-r--r--src/blog/mod.rs96
1 files changed, 83 insertions, 13 deletions
diff --git a/src/blog/mod.rs b/src/blog/mod.rs
index b301171..49fbd4b 100644
--- a/src/blog/mod.rs
+++ b/src/blog/mod.rs
@@ -8,8 +8,14 @@ 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";
@@ -49,28 +55,92 @@ pub async fn r_blog_article(name: &str) -> MyResult<DynScaffold<'static>> {
.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 html = markdown::to_html_with_options(
+ let ast = markdown::to_mdast(
&text,
- &markdown::Options {
- parse: markdown::ParseOptions {
- constructs: markdown::Constructs {
- math_flow: true,
- math_text: true,
- ..Default::default()
- },
- ..Default::default()
- },
- compile: markdown::CompileOptions {
+ &markdown::ParseOptions {
+ constructs: markdown::Constructs {
+ math_flow: true,
+ math_text: true,
..Default::default()
},
+ ..Default::default()
},
)
- .map_err(|e| anyhow!("the server had trouble compiling markdown: {e}"))?;
+ .map_err(|e| anyhow!("the server had trouble parsing markdown: {e}"))?;
+
Ok(Scaffold {
title: a.title,
content: markup::new! {
- @markup::raw(&html)
+ @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))
+ }
+ }
+}