Some checks failed
CI / Check typos (push) Successful in 8s
CI / Check links (push) Failing after 10s
CI / Clippy (push) Successful in 55s
CI / Build and test (push) Successful in 1m10s
CI / Build container (push) Successful in 52s
CI / Deploy on waypoint (push) Successful in 46s
134 lines
3.0 KiB
Rust
134 lines
3.0 KiB
Rust
use lazy_static::lazy_static;
|
|
use markdown_it::generics::inline::full_link;
|
|
use markdown_it::{MarkdownIt, Node};
|
|
use maud::{Markup, PreEscaped, Render, html};
|
|
use page::RequestContext;
|
|
use page::page::{Page, PageMetadata};
|
|
|
|
use crate::components::md::emote::InlineEmote;
|
|
use crate::components::md::frontmatter::{TomlFrontMatter, YamlFrontMatter};
|
|
use crate::components::md::link::SmartLink;
|
|
use crate::components::md::mdx::InlineMdx;
|
|
|
|
mod emote;
|
|
mod frontmatter;
|
|
mod link;
|
|
mod mdx;
|
|
|
|
lazy_static! {
|
|
static ref MdParser: MarkdownIt = {
|
|
let mut md = markdown_it::MarkdownIt::new();
|
|
|
|
{
|
|
|
|
use markdown_it::plugins::cmark::*;
|
|
|
|
inline::newline::add(&mut md);
|
|
inline::escape::add(&mut md);
|
|
inline::backticks::add(&mut md);
|
|
inline::emphasis::add(&mut md);
|
|
|
|
// Replaced with smart links
|
|
//inline::link::add(&mut md);
|
|
full_link::add::<false>(&mut md, |href, title| {
|
|
Node::new(SmartLink {
|
|
url: href.unwrap_or_default(),
|
|
title,
|
|
})
|
|
});
|
|
|
|
|
|
inline::image::add(&mut md);
|
|
inline::autolink::add(&mut md);
|
|
inline::entity::add(&mut md);
|
|
|
|
block::code::add(&mut md);
|
|
block::fence::add(&mut md);
|
|
block::blockquote::add(&mut md);
|
|
block::hr::add(&mut md);
|
|
block::list::add(&mut md);
|
|
block::reference::add(&mut md);
|
|
block::heading::add(&mut md);
|
|
block::lheading::add(&mut md);
|
|
block::paragraph::add(&mut md);
|
|
|
|
}
|
|
|
|
{
|
|
markdown_it::plugins::html::add(&mut md);
|
|
md_footnote::add(&mut md);
|
|
}
|
|
|
|
|
|
md.block.add_rule::<YamlFrontMatter>().before_all();
|
|
md.block.add_rule::<TomlFrontMatter>().before_all();
|
|
|
|
md.inline.add_rule::<InlineEmote>();
|
|
md.inline.add_rule::<InlineMdx>();
|
|
|
|
md
|
|
};
|
|
}
|
|
|
|
pub struct Markdown<'a>(pub &'a str);
|
|
|
|
impl Render for Markdown<'_> {
|
|
fn render(&self) -> Markup {
|
|
let md = Self::parse(self.0);
|
|
let html = md.render();
|
|
return PreEscaped(html);
|
|
}
|
|
}
|
|
|
|
impl Markdown<'_> {
|
|
pub fn parse(md_str: &str) -> Node {
|
|
MdParser.parse(md_str)
|
|
}
|
|
}
|
|
|
|
//
|
|
// MARK: helpers
|
|
//
|
|
|
|
/// Try to read page metadata from a markdown file's frontmatter.
|
|
/// - returns `none` if there is no frontmatter
|
|
/// - returns an error if we fail to parse frontmatter
|
|
pub fn meta_from_markdown(root_node: &Node) -> Result<Option<PageMetadata>, toml::de::Error> {
|
|
root_node
|
|
.children
|
|
.first()
|
|
.and_then(|x| x.cast::<TomlFrontMatter>())
|
|
.map(|x| toml::from_str::<PageMetadata>(&x.content))
|
|
.map_or(Ok(None), |v| v.map(Some))
|
|
}
|
|
|
|
pub fn backlinks(page: &Page, ctx: &RequestContext) -> Option<Markup> {
|
|
let mut last = None;
|
|
let mut backlinks = vec![("/", "home")];
|
|
|
|
if page.meta.backlinks.unwrap_or(false) {
|
|
let mut segments = ctx.route.split("/").skip(1).collect::<Vec<_>>();
|
|
last = segments.pop();
|
|
|
|
let mut end = 0;
|
|
for s in segments {
|
|
end += s.len();
|
|
backlinks.push((&ctx.route[0..=end], s));
|
|
end += 1; // trailing slash
|
|
}
|
|
}
|
|
|
|
last.map(|last| {
|
|
html! {
|
|
div {
|
|
@for (url, text) in backlinks {
|
|
a href=(url) style="padding-left:5pt;padding-right:5pt;" { (text) }
|
|
"/"
|
|
}
|
|
|
|
span style="color:var(--metaColor);padding-left:5pt;padding-right:5pt;" { (last) }
|
|
}
|
|
}
|
|
})
|
|
}
|