roxy_markdown_tera_rewriter 0.1.0

Roxy plugin for allowing Markdown and Tera to be parsed together
Documentation
use std::io::Write;

use once_cell::sync::Lazy;
use regex::{Captures, Regex, RegexBuilder};
use roxy_core::roxy::Parse;

const PLACEHOLDER_RE: Lazy<Regex> = Lazy::new(|| {
    RegexBuilder::new(r"^\n{0,2}(\s+)?(\{[\{|%].+?[\}|%]\})\r?\n{0,2}")
        .multi_line(true)
        .build()
        .unwrap()
});
pub struct MarkdownTeraPreformatter;

impl MarkdownTeraPreformatter {
    pub fn new() -> Self {
        Self
    }
}

impl Parse for MarkdownTeraPreformatter {
    fn parse(&mut self, _path: &str, src: &[u8], dst: &mut Vec<u8>) -> Result<(), roxy_core::error::Error> {
        let str = String::from_utf8_lossy(src).to_string();
        let result = PLACEHOLDER_RE
            .replace_all(str.as_str(), |captures: &Captures| {
                format!("\n{}\n", &captures[2])
            })
            .trim()
            .to_string();
        dst.write_all(result.as_bytes())?;
        Ok(())
    }
}

const EXTENSION_RE: Lazy<Regex> = Lazy::new(|| {
    RegexBuilder::new(r"^<p>(\{[%|\{].+?[%|\}]\}.*?)</p>")
        .dot_matches_new_line(true)
        .multi_line(true)
        .build()
        .unwrap()
});

pub struct MarkdownTeraRewriter;

impl MarkdownTeraRewriter {
    pub fn new() -> Self {
        Self
    }
}

impl Parse for MarkdownTeraRewriter {
    fn parse(&mut self, _path: &str, src: &[u8], dst: &mut Vec<u8>) -> Result<(), roxy_core::error::Error> {
        let utf8 = String::from_utf8_lossy(src).to_string();
        let result = EXTENSION_RE
            .replace_all(utf8.as_str(), |captures: &Captures| {
                format!("{}", &captures[1])
            })
            .replace("&quot;", "\"")
            .to_string();

        dst.write(result.as_bytes())?;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use roxy_core::roxy::Parse;

    use crate::{MarkdownTeraPreformatter, MarkdownTeraRewriter};

    #[test]
    fn basic_parse() {
        let mut parser = MarkdownTeraRewriter::new();
        let mut buf = Vec::new();

        parser
            .parse(
                "test.html",
                b"<p>{% extends \"base.html\" %}\n{% block content %}</p>\n<div><p>hi :3</p></div>{% endblock content %}",
                &mut buf,
            )
            .unwrap();

        assert_eq!(
            "{% extends \"base.html\" %}\n{% block content %}\n<div><p>hi :3</p></div>{% endblock content %}",
            String::from_utf8_lossy(&buf)
        );
    }

    #[test]
    fn import_parse() {
        let mut parser = MarkdownTeraRewriter::new();
        let mut buf = Vec::new();

        parser
            .parse(
                "test.html",
                b"<p>{% extends \"base.html\" %}\n{% import \"macros.html\" as macros %}\n{% block content %}</p>\n<div><p>hi :3</p></div>{% endblock content %}",
                &mut buf,
            )
            .unwrap();

        assert_eq!(
            "{% extends \"base.html\" %}\n{% import \"macros.html\" as macros %}\n{% block content %}\n<div><p>hi :3</p></div>{% endblock content %}",
            String::from_utf8_lossy(&buf)
        );
    }

    #[test]
    fn preformatter() {
        let mut parser = MarkdownTeraPreformatter::new();
        let mut buf = Vec::new();

        parser
            .parse(
                "test.html",
                b"{% extends \"base.html\" %}\n{% block content %}\n# hi :3\n{% endblock content %}",
                &mut buf,
            )
            .unwrap();

        assert_eq!(
            "{% extends \"base.html\" %}\n\n{% block content %}\n# hi :3\n\n{% endblock content %}",
            String::from_utf8_lossy(&buf)
        );
    }
}