summaryrefslogtreecommitdiff
path: root/src/generate_html.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/generate_html.rs')
-rw-r--r--src/generate_html.rs105
1 files changed, 81 insertions, 24 deletions
diff --git a/src/generate_html.rs b/src/generate_html.rs
index 84c3bdb..dd08885 100644
--- a/src/generate_html.rs
+++ b/src/generate_html.rs
@@ -4,44 +4,94 @@ use markdown::*;
pub fn generate_html(document: &MarkdownDocument, page: &Page, website: &Website) -> String {
+ let root = page.root();
+ let page_name = &page.name;
+ let site_name = &website.name;
+ let mut parent_url = String::new();
+ for segment in &page.parents {
+ parent_url.push_str(&make_url_safe(segment)); parent_url.push('/');
+ }
+ parent_url.pop();
+ let parent_name = match page.parents.get(page.parents.len()-1) {
+ Some(parent) => parent.to_string(),
+ None => String::new(),
+ };
+
+ let head = get_html_head(document, page); let head = head.trim();
+ let mut home = format!("<a id='home' href='{root}index.html'>{site_name}</a>");
+ let mut parent = format!("<a id='parent' href='../{parent_url}.html'>{parent_name}</a>");
+ let mut title = format!("<h1 id='title'>{page_name}</h1>");
+ let mut toc = get_table_of_contents(page);
+ let main = document_to_html(document, page, website); let main = main.trim();
+
+ if page.parents.is_empty() {
+ parent.clear();
+ if page.name_url == "index" {
+ home.clear();
+ title.clear();
+ toc.clear();
+ }
+ }
+
format!("\
<!DOCTYPE html>
<head>
-<title>{} &mdash; {}</title>
+<title>{page_name} &mdash; {site_name}</title>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
-{}
+{head}
</head>
<body>
+<header>
+<nav id='up'>
+{home}
+{parent}
+</nav>
+{title}
+{toc}
+</header>
<main>
-{}
+{main}
</main>
</body>
-</html> \
-",
- page.name, website.name,
- get_html_head(document, page).trim(),
- document_to_html(document, page, website).trim()
- )
+</html>")
}
-
pub fn get_html_head(document: &MarkdownDocument, page: &Page) -> String {
if let Some(Block::Fragment { language, content }) = document.blocks.first() {
if language == "embed-html-head" {
return content.to_string();
}
}
- let back = page.back_string();
+ let root = page.root();
format!("\
-<link rel='stylesheet' type='text/css' media='screen' href='{back}static/screen.css'>
-<link rel='stylesheet' type='text/css' media='print' href='{back}static/print.css'>
-<script src='{back}static/render_math.js' defer></script> \
+<link rel='stylesheet' type='text/css' media='screen' href='{root}static/screen.css'>
+<link rel='stylesheet' type='text/css' media='print' href='{root}static/print.css'>
+<script src='{root}static/render_math.js' defer></script> \
")
}
+pub fn get_table_of_contents(page: &Page) -> String {
+ if page.headings.len() < 3 {
+ return String::new();
+ }
+ let mut toc = String::from("<nav id='toc'><details><summary></summary><ul>\n");
+ for heading in &page.headings {
+ let name = &heading.name;
+ let url = &heading.url;
+ let class = match heading.level {
+ Level::Heading1 => "l1",
+ Level::Heading2 => "l2",
+ Level::Heading3 => "l3",
+ };
+ toc.push_str(&format!("<li><a href='#{url}' class='{class}'>{name}</a></li>\n"));
+ }
+ toc.push_str("</ul></details></nav>\n");
+ return toc;
+}
+
pub fn document_to_html(document: &MarkdownDocument, page: &Page, website: &Website) -> String {
let mut html = String::new();
@@ -54,9 +104,10 @@ pub fn document_to_html(document: &MarkdownDocument, page: &Page, website: &Webs
($t:expr,$l:expr,$c:expr) => { html!("<{} {}>{}</{}>", $t, $c, line_to_html!($l), $t) };
($t:expr,$l:expr) => { html!("<{}>{}</{}>", $t, line_to_html!($l), $t) }; }
macro_rules! wrap {
- ($t:expr,$f:expr) => {{ html!("<{}>", $t); $f; html!("</{}>", $t); }};
- }
+ ($t:expr,$c:expr,$f:expr) => {{ html!("<{} {}>", $t, $c); $f; html!("</{}>", $t); }};
+ ($t:expr,$f:expr) => {{ html!("<{}>", $t); $f; html!("</{}>", $t); }}; }
+ let root = page.root();
for block in &document.blocks {
match block {
Block::Heading { level, line } => match level {
@@ -96,11 +147,17 @@ pub fn document_to_html(document: &MarkdownDocument, page: &Page, website: &Webs
}),
Block::Note(lines) => wrap!("aside", for line in lines { tag!("p", line) }),
Block::Embedded { label, path } => match path.rsplit_once('.') {
- Some((_, extension)) => match extension.to_lowercase().as_str() {
- "jpg"|"jpeg"|"png"|"webp"|"gif"|"tiff" => html!(
- "<figure><a href='{path}'><img src='{path}' alt='{label}' title='{label}'></a></figure>"),
- "mp3"|"wav"|"m4a" => html!("<audio src='{path}' controls>"),
- ext @ _ => warn!("Unrecognised extension for embedded file {path:?} with extension {ext:?} in page {:?}", page.name),
+ Some((_, extension)) => {
+ let path = match path.strip_prefix('/') {
+ Some(stripped) => format!("{root}{stripped}"),
+ None => path.to_string(),
+ };
+ match extension.to_lowercase().as_str() {
+ "jpg"|"jpeg"|"png"|"webp"|"gif"|"tiff" => html!(
+ "<figure><a href='{path}'><img src='{path}' alt='{label}' title='{label}'></a></figure>"),
+ "mp3"|"wav"|"m4a" => html!("<audio src='{path}' controls>"),
+ ext @ _ => warn!("Unrecognised extension for embedded file {path:?} with extension {ext:?} in page {:?}", page.name),
+ }
}
_ => warn!("Cannot embed file {path:?} with no file extension in page {:?}", page.name),
}
@@ -119,7 +176,7 @@ pub fn document_to_html(document: &MarkdownDocument, page: &Page, website: &Webs
}
}
Block::Break => html!("<hr>"),
- Block::Table(table) => wrap!("table", {
+ Block::Table(table) => wrap!("div", "class='table'", wrap!("table", {
wrap!("thead",
wrap!("tr", for column in &table.columns {
tag!("th", column.name);
@@ -150,7 +207,7 @@ pub fn document_to_html(document: &MarkdownDocument, page: &Page, website: &Webs
})
})
};
- })
+ }))
}
}
return html;
@@ -194,7 +251,7 @@ fn line_to_html(line: &Line, page: &Page, website: &Website) -> String {
// Check that the heading exists.
if class == "heading" {
let heading = path.strip_prefix('#').unwrap().to_string();
- if !page.headings.contains(&heading) {
+ if !page.headings.iter().any(|h| h.url == heading) {
warn!("Page {:?} contains link to nonexistent internal heading {heading:?}", page.name);
}
}