1use std::{error::Error, sync::Arc};
2
3use error::CompileError;
4use escape_string::escape;
5use headless_chrome::{Browser, Tab};
6use unescape::unescape;
7
8mod error;
9
10#[derive(Clone)]
13pub struct Mermaid {
14 _browser: Browser,
15 tab: Arc<Tab>,
16}
17
18impl Mermaid {
19 pub fn new() -> Result<Self, Box<dyn Error>> {
21 let browser = Browser::default()?;
22 let mermaid_js = include_str!("../payload/mermaid.min.js");
23 let html_payload = include_str!("../payload/index.html");
24
25 let tab = browser.new_tab()?;
26 tab.navigate_to(&format!("data:text/html;charset=utf-8,{}", html_payload))?;
27 tab.evaluate(mermaid_js, false)?;
28
29 Ok(Self {
30 _browser: browser,
31 tab,
32 })
33 }
34
35 pub fn render(&self, input: &str) -> Result<String, Box<dyn Error>> {
43 let data = self
44 .tab
45 .evaluate(&format!("render('{}')", escape(input)), true)?;
46 let string = data.value.unwrap_or_default().to_string();
47 let slice = unescape(string.trim_matches('"')).unwrap_or_default();
48
49 if slice == "null" {
50 return Err(Box::new(CompileError));
51 }
52
53 Ok(slice.to_string())
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60
61 #[test]
62 fn create_mermaid_instance_without_crashing() {
63 let mermaid = Mermaid::new();
64 assert!(mermaid.is_ok());
65 }
66
67 #[test]
68 fn render_mermaid() {
69 let mermaid = Mermaid::new().unwrap();
70 let rendered = mermaid.render("graph TB\na-->b");
71 assert!(rendered.is_ok());
72 assert!(rendered.unwrap().starts_with("<svg"));
74 }
75
76 #[test]
77 fn syntax_error() {
78 let mermaid = Mermaid::new().unwrap();
79 let rendered = mermaid.render("grph TB\na-->b");
80 assert!(rendered.is_err());
81 }
82}