switched to askama, tightened up styles, added navigation

master
Kenton Hamaluik 5 years ago
parent 3ef3ba493b
commit 7125ba3098

106
Cargo.lock generated

@ -21,6 +21,46 @@ dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "askama"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"askama_derive 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"askama_escape 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"askama_shared 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "askama_derive"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"askama_shared 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "askama_escape"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "askama_shared"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"askama_escape 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"humansize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "atty"
version = "0.2.13"
@ -270,29 +310,6 @@ name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "handlebars"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"hashbrown 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)",
"walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hashbrown"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hermit-abi"
version = "0.1.3"
@ -301,6 +318,11 @@ dependencies = [
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "humansize"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "humantime"
version = "1.3.0"
@ -381,9 +403,9 @@ dependencies = [
name = "mkbook"
version = "0.1.0"
dependencies = [
"askama 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"comrak 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"handlebars 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"sass-rs 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
"syntect 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -399,6 +421,14 @@ dependencies = [
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-traits"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num_cpus"
version = "1.11.1"
@ -641,6 +671,16 @@ name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "0.15.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
version = "1.0.8"
@ -696,6 +736,14 @@ dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "toml"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "toml"
version = "0.5.5"
@ -835,6 +883,10 @@ dependencies = [
"checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2"
"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum askama 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3dc2a4b6d7f812d2b13d251ae792caecebd635d6401761162d4b71d5ebe1a010"
"checksum askama_derive 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ee2fff0f22ad5d215cace1227cd036c28e81e26206763bb837b6d0e766c87d"
"checksum askama_escape 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b0de942230b5beedaa9e1d64df5b76fa1c97002e4c7982897be899cccf40621d"
"checksum askama_shared 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a6dfa6b6d254fd066a8bbed9a8f913123e3f701db89216ad4f0aff04ad87718c"
"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
"checksum backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea"
@ -864,9 +916,8 @@ dependencies = [
"checksum fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
"checksum handlebars 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "91ef1ac30f2eaaa2b835fce73c57091cb6b9fc62b7eef285efbf980b0f20001b"
"checksum hashbrown 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1de41fb8dba9714efd92241565cdff73f78508c95697dd56787d3cba27e2353"
"checksum hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "307c3c9f937f38e3534b1d6447ecf090cafcc9744e4a6360e8b037b2cf5af120"
"checksum humansize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6cab2627acfc432780848602f3f558f7e9dd427352224b0d9324025796d2a5e"
"checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
@ -880,6 +931,7 @@ dependencies = [
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
"checksum miniz_oxide 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6f3f74f726ae935c3f514300cc6773a0c9492abc5e972d42ba0c0ebb88757625"
"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
"checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4"
"checksum num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76dac5ed2a876980778b8b85f75a71b6cbf0db0b1232ee12f826bccb00d09d72"
"checksum onig 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e4e723fc996fff1aeab8f62205f3e8528bf498bdd5eadb2784d2d31f30077947"
"checksum onig_sys 69.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0a8d4efbf5f59cece01f539305191485b651acb3785b9d5eef05749f0496514e"
@ -910,11 +962,13 @@ dependencies = [
"checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68"
"checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
"checksum syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "661641ea2aa15845cddeb97dad000d22070bb5c1fb456b96c1cba883ec691e92"
"checksum syntect 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "955e9da2455eea5635f7032fc3a229908e6af18c39600313866095e07db0d8b8"
"checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
"checksum toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "01d1404644c8b12b16bfcffa4322403a91a451584daaaa7c28d3152e6cbc98cf"
"checksum twoway 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6b40075910de3a912adbd80b5d8bad6ad10a23eeb1f5bf9d4006839e899ba5bc"
"checksum typed-arena 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9b2228007eba4120145f785df0f6c92ea538f5a3635a612ecf4e334c8c1446d"

@ -9,7 +9,7 @@ build = "build.rs"
syntect = "3.3"
comrak = "0.6"
clap = "2.33"
handlebars = "2.0"
askama = "0.8"
serde = { version = "1.0", features = ["derive"] }
toml = "0.5"

@ -0,0 +1,12 @@
<svg aria-hidden="true" style="position: absolute; width: 0; height: 0; overflow: hidden;" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<symbol id="icon-arrow-left" viewBox="0 0 448 512">
<title>arrow-left</title>
<path fill="currentColor" d="M257.5 445.1l-22.2 22.2c-9.4 9.4-24.6 9.4-33.9 0L7 273c-9.4-9.4-9.4-24.6 0-33.9L201.4 44.7c9.4-9.4 24.6-9.4 33.9 0l22.2 22.2c9.5 9.5 9.3 25-.4 34.3L136.6 216H424c13.3 0 24 10.7 24 24v32c0 13.3-10.7 24-24 24H136.6l120.5 114.8c9.8 9.3 10 24.8.4 34.3z"></path>
</symbol>
<symbol id="icon-arrow-right" viewBox="0 0 448 512">
<title>arrow-right</title>
<path fill="currentColor" d="M190.5 66.9l22.2-22.2c9.4-9.4 24.6-9.4 33.9 0L441 239c9.4 9.4 9.4 24.6 0 33.9L246.6 467.3c-9.4 9.4-24.6 9.4-33.9 0l-22.2-22.2c-9.5-9.5-9.3-25 .4-34.3L311.4 296H24c-13.3 0-24-10.7-24-24v-32c0-13.3 10.7-24 24-24h287.4L190.9 101.2c-9.8-9.3-10-24.8-.4-34.3z"></path>
</symbol>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 963 B

@ -0,0 +1,4 @@
---
title = "Introduction"
---

@ -0,0 +1,4 @@
---
title = "Markdown"
---

@ -0,0 +1,28 @@
---
title = "Front Matter"
---
# Front Matter
Each `.md` file can optionally contain a header with metadata describing the document. If the header isn't present, default values will be used which may look ugly.
To insert a header into a `.md` file, insert three dashes (`---`), followed by a new-line, followed by the front matter contents, followed by a newline, then another three dashes and a new-line. The metadata is in the [TOML](https://github.com/toml-lang/toml) format, so for example the front-matter (and first line) for this file looks like:
```md
---
title = "Front Matter"
---
# Front Matter
Each `.md` file can optionally contain a header with metadata describing the document. If the header isn't present, default values will be used which may look ugly.
```
## Supported Keys
The list of supported keys is subject to change, but for now it is as follows:
title
: A human-readable title for the document

@ -0,0 +1,4 @@
---
title = "Structure"
---

@ -1,72 +0,0 @@
---
title = "Sparte plura"
---
# Sparte plura
## Sine flumina
Lorem markdownum procubuit animasse solos talia [flammis me
quia](http://www.dedit.io/fessa-nec), attollite. Tu dulcedine tanta mitescere
manebit movit qualia aequorei victa illo, ipsi collum, suum disque. Sperantemque
triumphos praecipitatur potentia mea? Vox aere vestibus coepit utque deinde, heu
infans non amor somnusque Peleus.
1. Carminibus colle artibus ferunt fulgorem
2. Sonantia eripui
3. Divamque fixumque inter
4. Ducar mihi vertigine in spoliis
5. Fuit pendens talibus recentibus
6. Utrumque praesens adit maduere detorquet siqua
Tibi ego proelia mihi, hic precor, nil alba sitientes decusque linquendus.
Coeptis dixi longa; *me avido*, et queat humi Achillem, ora bina.
```c++
webmaster(whitelist(processor, 3, deviceDos), columnMinisite * 5, 2);
if (newlineWidgetTask) {
toslink.tape = throughput;
file_touchscreen_carrier.risc(point);
checksumWais -= ringMegapixelSoftware;
} else {
solid_sdsl(standaloneEbookBasic, 2);
syntax_drive_dynamic.gif_storage(losslessInsertion);
drop_controller_net = dashboard;
}
postSocial = dac.led(streamingScrollQuicktime + logVlbLeaderboard(memory,
terminal_grep), folderMemory);
```
## Functo mei ore terra liquidas praesepibus sopore
Iovis retorserunt tamen cumque **intus** equorum lacrimas inritat pluviaque
aliquas habebit videt: ignorat. Nec senex me **navigat mollit rogavi**
meditataque erat furibunda iter ales. Ciconia sibi mitra. Tamen postquam possunt
pariter contigerant **atris** expellitque odit; illam arva quae, partem medias?
```haxe
var icio = ugc(linkedin(310547, systrayPrimary, gatewayWindows + domain));
excelErgonomics -= userUrlKey(prebindingRefreshLaser);
if (fileCycleNumber(cmos(data / -2))) {
safeUrlMarkup(source(hard_io), checksum_youtube_file,
text_adc_myspace.eup(bankRup, -2, smb));
dimmSafeKeystroke.ntfs += parity_thunderbolt;
}
```
Ceris securum: cuius Amoris feliciter longe, *de esset plura*. Saevam vis seque,
viderit tantosque feritate oriens quaesitisque aevi. Parvis quisquis qui: hausit
est, pedum in fessa tutaque: leto sub fugias *non*, Halcyoneus. Globos te umeri
struxerit Iuppiter vitae; est arator ecce, [sit
et](http://www.tingui-ausus.io/), Aeneae per digestum rudis.
- Cedere nudae profitemur nec aurato adspice non
- Tibi artisque cornua nondum miratur ceperit
- Metuunt uva utque nondum potes super vocatus
- Velleris pecudes labens deseruitque regna
- Erant lacteus dubitati iuvenco
Lyraeque carpere luxque quas virgineos se deus reddidit colonos adflavit.
Tepidos [superba](http://notavi-sibi.com/de-saucius.aspx), iubent omnia quereris
tumidam est petentes, lumine.

@ -1,17 +1,12 @@
use std::path::PathBuf;
use std::{fs, io};
use serde::Deserialize;
pub const STYLESHEET: &'static str = include_str!(concat!(env!("OUT_DIR"), "/style.css"));
pub const TEMPLATE_PAGE: &'static str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/page.hbs"));
pub const ASSET_FAVICON: &'static [u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/favicon.ico"));
pub const ASSET_ICONS: &'static [u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/icons.svg"));
mod cli;
#[derive(Deserialize, Default)]
struct Metadata {
title: Option<String>,
}
mod models;
fn format_code(lang: &str, src: &str) -> Result<String, Box<dyn std::error::Error>> {
use syntect::parsing::{SyntaxSet, SyntaxReference};
@ -39,7 +34,7 @@ fn format_code(lang: &str, src: &str) -> Result<String, Box<dyn std::error::Erro
Ok(html)
}
fn extract_metadata(src: &str) -> Result<(Option<Metadata>, String), Box<dyn std::error::Error>> {
fn extract_frontmatter(src: &str) -> Result<(Option<models::frontmatter::ParsedFrontMatter>, String), Box<dyn std::error::Error>> {
if src.starts_with("---\n") {
let slice = &src[4..];
let end = slice.find("---\n");
@ -48,10 +43,10 @@ fn extract_metadata(src: &str) -> Result<(Option<Metadata>, String), Box<dyn std
}
let end = end.unwrap();
let metadata = &slice[..end];
let front = &slice[..end];
let contents = &slice[end+4..];
let metadata: Metadata = toml::from_str(metadata)?;
Ok((Some(metadata), contents.to_owned()))
let front: models::frontmatter::ParsedFrontMatter = toml::from_str(front)?;
Ok((Some(front), contents.to_owned()))
}
else if src.starts_with("---\r\n") {
let slice = &src[5..];
@ -61,10 +56,10 @@ fn extract_metadata(src: &str) -> Result<(Option<Metadata>, String), Box<dyn std
}
let end = end.unwrap();
let metadata = &slice[..end];
let front = &slice[..end];
let contents = &slice[end+5..];
let metadata: Metadata = toml::from_str(metadata)?;
Ok((Some(metadata), contents.to_owned()))
let front: models::frontmatter::ParsedFrontMatter = toml::from_str(front)?;
Ok((Some(front), contents.to_owned()))
}
else {
Ok((None, src.to_owned()))
@ -79,7 +74,7 @@ fn format_markdown(src: &str) -> Result<String, Box<dyn std::error::Error>> {
hardbreaks: false,
smart: true,
github_pre_lang: true,
default_info_string: Some("none".to_owned()),
default_info_string: None,
unsafe_: true,
ext_strikethrough: true,
ext_tagfilter: false,
@ -131,26 +126,46 @@ fn format_markdown(src: &str) -> Result<String, Box<dyn std::error::Error>> {
Ok(output)
}
fn format_page<W: io::Write>(metadata: Option<Metadata>, content: &str, output: W) -> Result<(), Box<dyn std::error::Error>> {
use handlebars::Handlebars;
// create the handlebars registry
let mut handlebars = Handlebars::new();
// register the template. The template string will be verified and compiled.
handlebars.register_template_string("page", TEMPLATE_PAGE).expect("page is valid template");
fn format_page<W: io::Write>(frontmatter: models::frontmatter::FrontMatter, chapters: &Vec<models::chapter::Chapter>, url: &str, content: &str, mut output: W) -> Result<(), Box<dyn std::error::Error>> {
use askama::Template;
#[derive(Template)]
#[template(path = "page.html")]
struct PageTemplate<'a, 'b, 'c, 'd, 'e, 'f> {
title: &'a str,
content: &'b str,
url: &'f str,
chapters: &'c Vec<models::chapter::Chapter>,
prev_chapter: Option<&'d models::chapter::Chapter>,
next_chapter: Option<&'e models::chapter::Chapter>,
}
// generate our context
use std::collections::BTreeMap;
let mut data = BTreeMap::new();
data.insert("content", content);
let this_index = chapters.iter().enumerate().find(|(_, chap)| chap.url == url).map(|(i, _)| i).expect("chapter exists");
let prev_chapter = if this_index > 0 {
Some(chapters.iter().nth(this_index - 1).expect("chapter n-1 exists"))
}
else {
None
};
let next_chapter = if this_index < chapters.len() - 1 {
Some(chapters.iter().nth(this_index + 1).expect("chapter n+1 exists"))
}
else {
None
};
let metadata = metadata.unwrap_or_default();
let title = metadata.title.unwrap_or_default();
data.insert("title", &title);
// fill out our template
let template = PageTemplate {
title: &frontmatter.title,
content,
url,
chapters,
prev_chapter,
next_chapter,
};
// and render
handlebars.render_to_write("page", &data, output)?;
// and render!
let s = template.render()?;
output.write_all(s.as_bytes())?;
Ok(())
}
@ -173,6 +188,27 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let dest = prefix.join("book");
std::fs::create_dir_all(&dest)?;
// load all our chapters
let mut chapters: Vec<models::chapter::Chapter> = Vec::default();
for entry in src.read_dir()? {
let entry = entry?;
let path = entry.path();
if let Some("md") = path.extension().map(std::ffi::OsStr::to_str).flatten() {
let name = path.file_stem().map(std::ffi::OsStr::to_str).flatten();
if name.is_none() { continue; }
let name = name.unwrap();
let contents = fs::read_to_string(&path)?;
let (front, _) = extract_frontmatter(&contents)?;
let front = front.unwrap_or_default().into_front(name);
chapters.push(models::chapter::Chapter {
url: format!("/{}.html", name),
title: front.title,
});
}
}
chapters.sort_by(|a, b| a.url.cmp(&b.url));
// compile markdown
for entry in src.read_dir()? {
let entry = entry?;
@ -187,9 +223,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let outfile = io::BufWriter::new(outfile);
let contents = fs::read_to_string(&path)?;
let (meta, contents) = extract_metadata(&contents)?;
let (front, contents) = extract_frontmatter(&contents)?;
let front = front.unwrap_or_default().into_front(name);
let contents = format_markdown(&contents)?;
format_page(meta, &contents, outfile)?;
format_page(front, &chapters, &format!("/{}.html", name), &contents, outfile)?;
println!("Rendered `{}` into `{}`", path.display(), out.display());
}
@ -200,6 +237,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Wrote {}", dest.join("style.css").display());
fs::write(dest.join("favicon.ico"), ASSET_FAVICON)?;
println!("Wrote {}", dest.join("favicon.ico").display());
fs::write(dest.join("icons.svg"), ASSET_ICONS)?;
println!("Wrote {}", dest.join("icons.svg").display());
println!("Done!");
Ok(())

@ -0,0 +1,2 @@
pub mod chapter;
pub mod frontmatter;

@ -0,0 +1,4 @@
pub struct Chapter {
pub url: String,
pub title: String,
}

@ -0,0 +1,21 @@
use serde::Deserialize;
#[derive(Deserialize, Default)]
pub struct ParsedFrontMatter {
pub title: Option<String>,
}
pub struct FrontMatter {
pub title: String,
}
impl ParsedFrontMatter {
pub fn into_front(&self, file_name: &str) -> FrontMatter {
FrontMatter {
title: match &self.title {
Some(title) => title.clone(),
None => file_name.to_owned(),
},
}
}
}

@ -1,19 +1,19 @@
body {
margin: 2em auto;
max-width: 36em;
line-height: 1.6;
margin: 0;
line-height: 1.5;
font-size: 14pt;
color: #222222;
background: #eeeeee;
padding: 0 0.5em;
font-family: "Georgia", Georgia, "Times New Roman", Times, serif;
padding: 0;
font-family: $font-serif;
}
h1,
h2,
h3 {
margin-top: 0;
line-height: 1.2;
font-family: "Franklin Gothic Medium", "Arial Narrow", Arial, sans-serif;
font-family: $font-sansserif;
}
a {
@ -45,14 +45,39 @@ figure {
code {
margin: 0 2px;
padding: 0 5px;
padding: 0 2px;
border: 1px solid #4c566a;
border-radius: 3px;
word-break: break-all;
font-family: $font-mono;
font-size: 0.75em;
}
pre {
overflow-x: auto;
font-family: "Courier New", Courier, monospace;
padding: 0.25em 0.5em;
font-family: $font-mono;
padding: 0;
}
dl {
display: grid;
grid-template-columns: auto 1fr;
dt {
font-weight: 700;
margin: 0;
padding: 0.5em;
border-right: 1px solid #dddddd;
}
dd {
margin: 0;
padding: 0.5em;
}
dt, dd {
p {
margin: 0;
}
}
}

@ -12,6 +12,10 @@
}
}
nav {
background: #18181d;
}
img {
filter: grayscale(30%);
}

@ -0,0 +1,22 @@
span.icon {
display: flex;
align-items: center;
}
[class^="icon-"],
[class*=" icon-"] {
display: inline-block;
width: 1em;
height: 1em;
stroke-width: 0;
stroke: currentColor;
fill: currentColor;
}
.icon-arrow-left {
width: 0.875em;
}
.icon-arrow-right {
width: 0.875em;
}

@ -0,0 +1,105 @@
html, body {
width: 100%;
min-height: 100vh;
}
body {
display: grid;
grid-template-columns: auto 1fr;
grid-template-rows: 1fr;
justify-items: stretch;
align-items: stretch;
nav.big {
background: #2c2c38;
padding: 1em;
display: flex;
flex-direction: column;
a {
width: 100%;
font-size: 1.5em;
text-decoration: none;
font-family: $font-sansserif;
margin: 0 0 0.25em 0;
color: #5babd1;
&:hover {
color: #cf5ccd;
}
&.current {
color: #cf5ccd;
&:hover {
color: #fefefe;
}
}
}
}
nav.small {
display: none;
width: 100%;
align-items: center;
justify-content: space-between;
background: #2c2c38;
padding: 0;
>* {
margin: 0.5em;
}
a {
text-decoration: none;
font-family: $font-sansserif;
color: #5babd1;
&:hover {
color: #cf5ccd;
}
}
span.title {
text-decoration: none;
font-family: $font-sansserif;
color: #fefefe;
}
span.placeholder {
width: 1em;
height: 1em;
}
}
article {
padding: 1em 2em 0 2em;
max-width: 38em;
min-width: 0;
min-height: 0;
>* {
max-width: 100%;
}
}
}
@media screen and (max-width: 768px) {
body {
grid-template-columns: 1fr;
grid-template-rows: auto 1fr;
nav.big {
display: none;
}
nav.small {
display: flex;
}
article {
padding: 1em 0.5em 0 0.5em;
}
}
}

@ -26,4 +26,12 @@
* {
overflow: hidden;
}
nav {
display: none;
}
body {
display: block;
}
}

@ -1,3 +1,6 @@
@import 'variables';
@import 'base';
@import 'layout';
@import 'icons';
@import 'darktheme';
@import 'print';

@ -0,0 +1,5 @@
@import url('https://fonts.googleapis.com/css?family=Crimson+Pro|Poppins:700|Source+Code+Pro&display=swap');
$font-serif: 'Crimson Pro', "Georgia", Georgia, "Times New Roman", Times, serif !default;
$font-sansserif: 'Poppins', "Franklin Gothic Medium", "Arial Narrow", Arial, sans-serif !default;
$font-mono: 'Source Code Pro', "Courier New", Courier, monospace !default;

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ title }}</title>
<link rel="stylesheet" href="/style.css" type="text/css" media="all" />
<link rel="shortcut icon" href="/favicon.ico" />
</head>
<body>
{{{content}}}
</body>
</html>

@ -0,0 +1,45 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ title }}</title>
<link rel="stylesheet" href="/style.css" type="text/css" media="all" />
<link rel="shortcut icon" href="/favicon.ico" />
</head>
<body>
<nav class="big">
{% for chapter in chapters %}
<a href="{{ chapter.url|safe }}"{% if url == chapter.url %} class = "current"{% endif %}>{{ chapter.title }}</a>
{% endfor %}
</nav>
<nav class="small">
{% match prev_chapter %}
{% when Some with (chapter) %}
<a href="{{ chapter.url }}">
<span class="icon">
<svg class="icon-arrow-left">
<use xlink:href="icons.svg#icon-arrow-left">
</svg>
</span>
</a>
{% when None %}
<span class="placeholder"></span>
{% endmatch %}
<span class="title">{{ title }}</span>
{% match next_chapter %}
{% when Some with (chapter) %}
<a href="{{ chapter.url }}">
<span class="icon">
<svg class="icon-arrow-right">
<use xlink:href="icons.svg#icon-arrow-right">
</svg>
</span>
</a>
{% when None %}
<span class="placeholder"></span>
{% endmatch %}
</nav>
<article>{{ content|safe }}</article>
</body>
</html>
Loading…
Cancel
Save