-
Notifications
You must be signed in to change notification settings - Fork 2
/
mod.rs
119 lines (106 loc) · 3.53 KB
/
mod.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use super::frontmatter::{FrontMatter, RawFrontMatter};
use serde::Serialize;
use std::path::{Path, PathBuf};
use tera::Tera;
mod katex;
pub mod markdown;
mod plantuml;
mod pygments;
lazy_static::lazy_static! {
pub static ref TEMPLATES: Tera = {
let mut tera = match Tera::new("templates/**/*") {
Ok(t) => t,
Err(e) => {
println!("Parsing error(s): {}", e);
::std::process::exit(1);
}
};
tera.autoescape_on(vec!["html"]);
tera
};
}
#[derive(Serialize)]
pub struct Post {
pub front: FrontMatter,
pub source: PathBuf,
pub url: String,
pub contents: String,
}
impl Post {
fn extract_frontmatter(
src: &str,
) -> Result<(Option<RawFrontMatter>, String), Box<dyn std::error::Error>> {
if src.starts_with("---\n") {
let slice = &src[4..];
let end = slice.find("---\n");
if end.is_none() {
return Ok((None, src.to_owned()));
}
let end = end.unwrap();
let front = &slice[..end];
let contents = &slice[end + 4..];
let front: RawFrontMatter = serde_yaml::from_str(front)?;
Ok((Some(front), contents.to_owned()))
} else if src.starts_with("---\r\n") {
let slice = &src[5..];
let end = slice.find("---\r\n");
if end.is_none() {
return Ok((None, src.to_owned()));
}
let end = end.unwrap();
let front = &slice[..end];
let contents = &slice[end + 5..];
let front: RawFrontMatter = serde_yaml::from_str(front)?;
Ok((Some(front), contents.to_owned()))
} else {
Ok((None, src.to_owned()))
}
}
pub fn load<P: AsRef<Path>>(src: P) -> Result<Option<Post>, Box<dyn std::error::Error>> {
let contents = std::fs::read_to_string(src.as_ref())?;
let (front, contents) = Post::extract_frontmatter(&contents)?;
if front.is_none() {
eprintln!(
"skipping `{}` as it contains invalid metadata",
src.as_ref().display()
);
return Ok(None);
}
let front: Option<FrontMatter> = front.unwrap().into();
if front.is_none() {
eprintln!(
"skipping `{}` as it isn't published",
src.as_ref().display()
);
return Ok(None);
}
// format the summary as markdown
let mut front = front.unwrap();
{
let markdown::FormatResponse { output, .. } =
markdown::format_markdown(&front.summary)?;
front.summary = output;
}
let url = format!("/posts/{}/", front.slug);
Ok(Some(Post {
front,
contents,
source: src.as_ref().to_owned(),
url,
}))
}
pub fn render(&self) -> Result<String, Box<dyn std::error::Error>> {
let markdown::FormatResponse {
output,
include_katex_css,
} = markdown::format_markdown(&self.contents)?;
let mut context = tera::Context::new();
context.insert("title", &self.front.title);
context.insert("front", &self.front);
context.insert("content", &output);
context.insert("include_katex_css", &include_katex_css);
let rendered = TEMPLATES.render("post.html", &context)?;
let minified = html_minifier::HTMLMinifier::minify(rendered)?;
Ok(minified)
}
}