diff --git a/Cargo.lock b/Cargo.lock index 7489246..6fe07ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -463,9 +463,12 @@ dependencies = [ "actix-files", "actix-web", "glob", + "latex2mathml", + "markdown", "minify-html", "ramhorns", "serde", + "serde_yaml", "toml", ] @@ -664,6 +667,12 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" +[[package]] +name = "latex2mathml" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678cf5bdb3ba63a264e6e0c9eee36538ca1d2da0afa4dd801c1f96309e710765" + [[package]] name = "lazy_static" version = "1.4.0" @@ -736,6 +745,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "markdown" +version = "1.0.0-alpha.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de49c677e95e00eaa74c42a0b07ea55e1e0b1ebca5b2cbc7657f288cd714eb" +dependencies = [ + "unicode-id", +] + [[package]] name = "memchr" version = "2.5.0" @@ -1126,6 +1144,19 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yaml" +version = "0.9.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9d684e3ec7de3bf5466b32bd75303ac16f0736426e5a4e0d6e489559ce1249c" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "sha1" version = "0.10.5" @@ -1342,6 +1373,12 @@ version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +[[package]] +name = "unicode-id" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d70b6494226b36008c8366c288d77190b3fad2eb4c10533139c1c1f461127f1a" + [[package]] name = "unicode-ident" version = "1.0.8" @@ -1363,6 +1400,12 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +[[package]] +name = "unsafe-libyaml" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6" + [[package]] name = "url" version = "2.3.1" diff --git a/Cargo.toml b/Cargo.toml index 8ab6a74..f8e35c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,5 +15,8 @@ actix-files = "0.6" ramhorns = "0.14.0" toml = "0.7.3" serde = { version = "1.0.159", features = ["derive"] } +serde_yaml = "0.9" minify-html = "0.10.8" glob = "0.3.1" +markdown = "1.0.0-alpha.7" +latex2mathml = "0.2.3" diff --git a/src/config.rs b/src/config.rs index 56a1037..429efef 100644 --- a/src/config.rs +++ b/src/config.rs @@ -14,6 +14,7 @@ pub struct FileConfig { pub mail: Option, pub lang: Option, pub onion: Option, + pub app_name: Option, } impl FileConfig { @@ -21,6 +22,7 @@ impl FileConfig { Self { scheme: Some("http".to_string()), port: Some(8080), + app_name: Some("EWP".to_string()), ..FileConfig::default() } } @@ -44,6 +46,7 @@ impl FileConfig { mail: test(a.mail, d.mail), lang: test(a.lang, d.lang), onion: test(a.onion, d.onion), + app_name: test(a.app_name, d.app_name), } } } @@ -83,10 +86,11 @@ pub fn get_config(file_path: &str) -> Config { ); Config { - fc: internal_config, + fc: internal_config.clone(), static_location: format!("{}/{}", files_root, static_dir), tmpl: Template { directory: format!("{}/{}", files_root, templates_dir), + app_name: internal_config.app_name.unwrap(), }, } } diff --git a/src/template.rs b/src/template.rs index 1c8e015..df63f4d 100644 --- a/src/template.rs +++ b/src/template.rs @@ -1,8 +1,16 @@ use ramhorns::{Content, Ramhorns}; +use serde::Deserialize; #[derive(Clone)] pub struct Template { pub directory: String, + pub app_name: String, +} + +#[derive(Content)] +struct Data { + app_name: String, + data: T, } impl Template { @@ -10,6 +18,100 @@ impl Template { let mut templates: Ramhorns = Ramhorns::lazy(&self.directory).unwrap(); let tplt = templates.from_file(template).unwrap(); - tplt.render(&data) + tplt.render(&Data { + app_name: self.app_name.clone(), + data, + }) } } + +enum FrontMatter<'a> { + Yaml(&'a str), + Toml(&'a str), + Json(&'a str), +} + +impl FrontMatter<'_> { + fn parse(&self) -> FileMetadata { + match self { + Self::Yaml(val) => serde_yaml::from_str(val).unwrap_or_default(), + Self::Toml(_val) => todo!(), + Self::Json(_val) => todo!(), + } + } +} + +#[derive(Default, Deserialize, Content)] +pub struct FileMetadata { + pub title: Option, + pub link: Option, +} + +#[derive(Content)] +pub struct Metadata { + pub info: FileMetadata, + pub mermaid: bool, + pub syntax_highlight: bool, +} + +pub fn read_md(filename: &str) -> (Metadata, String) { + // Read markdown file + let mut text = std::fs::read_to_string(filename).unwrap(); + + // Transform LaTeX to MathML + text = latex2mathml::replace(&text).unwrap(); + + let parse_option = markdown::ParseOptions { + constructs: markdown::Constructs { + frontmatter: true, + ..markdown::Constructs::gfm() + }, + ..markdown::ParseOptions::gfm() + }; + + let compile_option = markdown::CompileOptions { + allow_dangerous_html: true, + ..markdown::CompileOptions::gfm() + }; + + let md_tree = markdown::to_mdast(&text, &parse_option).unwrap(); + let md_nodes = md_tree.children().unwrap(); + let metadata = match &md_nodes[0] { + markdown::mdast::Node::Yaml(v) => FrontMatter::Yaml(&v.value).parse(), + markdown::mdast::Node::Toml(v) => FrontMatter::Toml(&v.value).parse(), + markdown::mdast::Node::MdxjsEsm(v) => FrontMatter::Json(&v.value).parse(), + _ => FileMetadata::default(), + }; + + // Convert to HTML + let html = markdown::to_html_with_options( + &text, + &markdown::Options { + parse: parse_option, + compile: compile_option, + }, + ) + .unwrap(); + + // Find if document contains mermaid diagram + let mermaid = Some(String::from("mermaid")); + let presence_mermaid = md_nodes.iter().any(|x| match x { + markdown::mdast::Node::Code(code) => code.lang == mermaid, + _ => false, + }); + + // Find if document contains code to highlight + let presence_code = md_nodes.iter().any(|x| match x { + markdown::mdast::Node::Code(code) => code.lang != mermaid, + _ => false, + }); + + ( + Metadata { + info: metadata, + mermaid: presence_mermaid, + syntax_highlight: presence_code, + }, + html, + ) +}