wayver's git archive


an obsidian renderer
git clone https://git.wayver.dev/sable

sable-renderer/src/templates/markdown.rs@main

raw
Date Commit Message Author Files + -
2026-02-23 01:55 initial mvp wayverd 139 17808 0
...

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