use chrono::prelude::*; use derive_more::Constructor; use markdown::{to_html_with_options, CompileOptions, Options}; use regex::Regex; use std::collections::HashMap; use std::ffi::OsString; use std::fs::{self, read_dir, read_to_string}; use std::path::PathBuf; use tera::{Context, Tera}; pub fn read_src_files(src_dir: &str) -> Vec<SrcMD> { let mut files: Vec<SrcMD> = Vec::new(); for file in read_dir(src_dir).expect("Cant read src dir") { let file = file.unwrap(); let kv = get_kv(read_to_string(file.path()).unwrap()).0; let title = kv.get("title").unwrap(); let date = kv.get("date").unwrap(); files.push(SrcMD::new( file.path(), title.to_string(), DateTime::parse_from_str(&format!("{date} 00:00:00 +00:00"), "%Y-%m-%d %H:%M:%S %z") .unwrap(), file.path().file_stem().unwrap().to_os_string(), )) } files } pub fn write_to_fs(html: String, output_dir: &String, file_name: &String) { fs::write(format!("{output_dir}/{file_name}.html"), html) .unwrap_or_else(|_| panic!("Error writing {file_name}")); } #[derive(Constructor)] pub struct SrcMD { pub path: PathBuf, pub title: String, pub date: DateTime<FixedOffset>, pub file_name: OsString, } pub fn generate_blog_entry(markdown: String, template_dir: &String) -> (String, String) { let markdown = markdown.clone(); let mut tera = Tera::new(&format!("{template_dir}/*")).unwrap(); tera.autoescape_on(vec![]); let (mut key_value, markdown) = get_kv(markdown); let html_markdown = to_html_with_options( &markdown, &Options { compile: CompileOptions { allow_dangerous_html: true, allow_dangerous_protocol: true, ..CompileOptions::default() }, ..Options::gfm() }, ) .unwrap(); key_value.insert("blog_content".to_string(), html_markdown.clone()); let context = Context::from_serialize(&key_value).unwrap(); let templated_html = tera .render(key_value.get("template").unwrap(), &context) .unwrap(); (templated_html, html_markdown) } pub fn get_kv(markdown: String) -> (HashMap<String, String>, String) { let re_key_value = Regex::new(r"(?ms)---(.*)---(?:\n)").unwrap(); let key_value_string = re_key_value .find(markdown.as_str()) .expect("Can't find key value map in markdown"); let content_markdown = re_key_value .replace(markdown.clone().as_str(), "") .to_string(); let mut key_value: HashMap<String, String> = HashMap::new(); for line in key_value_string.as_str().lines() { if line == "---" { continue; } key_value.insert( line.split(':') .collect::<Vec<&str>>() .first() .unwrap() .trim() .to_string(), line.split(':') .collect::<Vec<&str>>() .get(1) .unwrap() .trim() .to_string(), ); } (key_value, content_markdown) }