raw
1use std::collections::HashMap;
2
3use sable_markdown::render::UrlRewriter;
4use sable_vault::{SharedVault, Vault};
5use tera::Filter;
6
7use crate::templates::FilterError;
8
9pub(super) struct Markdown {
10 vault: SharedVault,
11}
12
13impl Markdown {
14 pub(super) const fn new(vault: SharedVault) -> Self {
15 Self { vault }
16 }
17}
18
19impl Filter for Markdown {
20 fn filter(
21 &self,
22 value: &tera::Value,
23 _args: &HashMap<String, tera::Value>,
24 ) -> tera::Result<tera::Value> {
25 let tera::Value::String(raw) = value else {
26 return Err(tera::Error::call_filter(
27 "markdown",
28 FilterError::new("filter only accepts string as an imput"),
29 ));
30 };
31
32 let root = match sable_markdown::parser::parse_markdown(raw) {
33 Ok(root) => root,
34 Err(err) => {
35 return Err(tera::Error::call_filter("markdown", FilterError::new(err)));
36 }
37 };
38
39 let vault = self.vault.read();
40
41 let config = sable_markdown::render::Config::default()
42 .with_link_rewriter(LinkRewriter { _vault: &vault })
43 .with_wikilink_rewriter(WikiLinkRewriter { vault: &vault });
44
45 let rendered = sable_markdown::render::render_html(&root, &config);
46
47 drop(config);
48 drop(vault);
49
50 Ok(tera::Value::String(rendered))
51 }
52}
53
54struct LinkRewriter<'v> {
55 _vault: &'v Vault,
56}
57
58impl UrlRewriter for LinkRewriter<'_> {
59 fn rewrite(&self, path: &str, _target: Option<&str>) -> String {
60 if path.starts_with("ftp") || path.starts_with("http") || path.starts_with("mailto") {
61 return path.to_string();
62 }
63
64 crate::slugify_path(path)
65 }
66}
67
68struct WikiLinkRewriter<'v> {
69 vault: &'v Vault,
70}
71
72impl UrlRewriter for WikiLinkRewriter<'_> {
73 fn rewrite(&self, path: &str, target: Option<&str>) -> String {
74 if path.starts_with("ftp") || path.starts_with("http") || path.starts_with("mailto") {
75 return path.to_string();
76 }
77
78 let Some((path, _note)) = self
79 .vault
80 .find_note_by_title(path)
81 .or_else(|| self.vault.find_note_by_name(path))
82 else {
83 tracing::warn!(path=?path, target=?target, "unknown wikilink note");
84
85 return path.to_string();
86 };
87
88 let path = path.slug.with_extension("html");
89
90 format!("/{}", url_escape::encode_path(path.as_str()))
91 }
92}
93