diff --git a/.gitignore b/.gitignore index f9c3472..423dde2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,21 @@ target .idea/ test_site/public +test_site_i18n/public docs/public small-blog medium-blog big-blog huge-blog +extra-huge-blog small-kb medium-kb huge-kb current.bench now.bench +*.zst # snapcraft artifacts snap/.snapcraft diff --git a/.travis.yml b/.travis.yml index 7541658..e8076d1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ matrix: # The earliest stable Rust version that works - env: TARGET=x86_64-unknown-linux-gnu - rust: 1.30.0 + rust: 1.31.0 before_install: set -e diff --git a/CHANGELOG.md b/CHANGELOG.md index ff0b634..1908ed2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## 0.6.0 (unreleased) + +### Breaking +- `earlier/later` and `lighter/heavier` are not set anymore on pages when rendering +a section +- The table of content for a page/section is now only available as the `toc` variable when +rendering it and not anymore on the `page`/`section` variable +- Default directory for `load_data` is now the root of the site instead of the `content` directory +- Change variable sent to the sitemap template, see documentation for details + +### Other +- Add support for content in multiple languages +- Lower latency on serve before rebuilding from 2 to 1 second +- Allow processing PNG and produced images are less blurry +- Add an id (`zola-continue-reading`) to the paragraph generated after a summary +- Add Dracula syntax highlighting theme +- Fix using inline styles in headers +- Fix sections with render=false being shown in sitemap +- Sitemap is now split when there are more than 30 000 links in it +- Add link to sitemap in robots.txt +- Markdown rendering is now fully CommonMark compliant +- `load_data` now defaults to loading file as plain text, unless `format` is passed +or the extension matches csv/toml/json +- Sitemap entries get an additional `extra` field for pages only +- Add a `base-path` command line option to `build` and `serve` + + ## 0.5.1 (2018-12-14) - Fix deleting markdown file in `zola serve` diff --git a/Cargo.lock b/Cargo.lock index e5b42b5..31ce1ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,3 +1,13 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "MacTypes-sys" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "actix" version = "0.7.9" @@ -5,100 +15,99 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "actix_derive 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-channel 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-signal 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "trust-dns-proto 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "trust-dns-resolver 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "trust-dns-resolver 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "actix-net" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "actix 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-current-thread 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "tower-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "trust-dns-proto 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "trust-dns-resolver 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "trust-dns-resolver 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "actix-web" -version = "0.7.16" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "actix 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", - "actix-net 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "askama_escape 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "actix-net 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "cookie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "h2 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "h2 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-current-thread 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "v_htmlescape 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -107,9 +116,9 @@ name = "actix_derive" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -119,19 +128,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "aho-corasick" -version = "0.6.9" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ammonia" -version = "1.2.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "html5ever 0.22.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "tendril 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -148,7 +157,7 @@ dependencies = [ [[package]] name = "arc-swap" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -158,72 +167,65 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "arrayvec" -version = "0.4.8" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "askama_escape" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "atty" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "backtrace" -version = "0.3.12" +name = "autocfg" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] -name = "backtrace-sys" -version = "0.1.24" +name = "backtrace" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "base64" -version = "0.9.3" +name = "backtrace-sys" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "base64" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bincode" -version = "1.0.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -257,26 +259,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "byteorder" -version = "1.2.7" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bytes" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cc" -version = "1.0.26" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cfg-if" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -286,8 +288,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -324,11 +326,12 @@ dependencies = [ "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "errors 0.1.0", "globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "syntect 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "syntect 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "utils 0.1.0", ] [[package]] @@ -336,7 +339,7 @@ name = "cookie" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -346,7 +349,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -354,7 +357,7 @@ name = "core-foundation-sys" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -367,22 +370,19 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.1.2" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-channel" -version = "0.3.3" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-epoch 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -396,11 +396,11 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-epoch 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -408,10 +408,10 @@ name = "crossbeam-epoch" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -419,48 +419,57 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.6.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossbeam-queue" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crossbeam-utils" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-utils" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "csv" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "csv-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", + "csv-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "csv-core" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -478,7 +487,7 @@ version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -501,7 +510,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "either" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -509,12 +518,12 @@ name = "elasticlunr-rs" version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rust-stemmers 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-stemmers 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "strum 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "strum_macros 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -578,10 +587,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "encoding_rs" -version = "0.8.13" +version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -589,45 +598,36 @@ name = "error-chain" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "error-chain" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "errors" version = "0.1.0" dependencies = [ - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "image 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)", - "syntect 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tera 0.11.20 (registry+https://github.com/rust-lang/crates.io-index)", + "image 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syntect 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tera 1.0.0-beta.3 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "failure" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "failure_derive" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -641,20 +641,20 @@ name = "filetime" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "flate2" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crc32fast 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "miniz-sys 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide_c_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide_c_api 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -681,11 +681,11 @@ version = "0.1.0" dependencies = [ "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "errors 0.1.0", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "tera 0.11.20 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "tera 1.0.0-beta.3 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "utils 0.1.0", ] @@ -702,7 +702,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "fsevent-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -710,9 +710,14 @@ name = "fsevent-sys" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -733,7 +738,7 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "new_debug_unreachable 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -747,7 +752,7 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -785,33 +790,42 @@ name = "globset" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "globwalk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ignore 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "h2" -version = "0.1.14" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "string 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "heck" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -822,7 +836,7 @@ name = "hostname" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -834,17 +848,17 @@ dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "markup5ever 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "http" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -869,40 +883,41 @@ dependencies = [ [[package]] name = "hyper" -version = "0.12.18" +version = "0.12.25" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "h2 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "h2 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "hyper-tls" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.18 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.25 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -912,24 +927,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ignore" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "image" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "gif 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "jpeg-decoder 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", "num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "png 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "png 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", + "safe-transmute 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tiff 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tiff 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -937,11 +970,11 @@ name = "imageproc" version = "0.1.0" dependencies = [ "errors 0.1.0", - "image 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "image 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tera 0.11.20 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tera 1.0.0-beta.3 (registry+https://github.com/rust-lang/crates.io-index)", "utils 0.1.0", ] @@ -952,7 +985,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "inflate" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -964,12 +997,8 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -977,7 +1006,7 @@ name = "inotify-sys" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -985,7 +1014,7 @@ name = "iovec" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1011,7 +1040,7 @@ name = "jpeg-decoder" version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1031,7 +1060,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "lazy_static" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1041,18 +1070,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.45" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "libflate" -version = "0.1.19" +version = "0.2.50" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "crc32fast 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "library" @@ -1063,36 +1082,39 @@ dependencies = [ "errors 0.1.0", "front_matter 0.1.0", "globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "rendering 0.1.0", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "slotmap 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "slug 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tera 0.11.20 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tera 1.0.0-beta.3 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "utils 0.1.0", ] [[package]] -name = "link_checker" -version = "0.1.0" +name = "line-wrap" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "reqwest 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", + "safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "linked-hash-map" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" +name = "link_checker" +version = "0.1.0" +dependencies = [ + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "linked-hash-map" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1109,15 +1131,15 @@ name = "log" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "lru-cache" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "linked-hash-map 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1140,11 +1162,11 @@ name = "markup5ever" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "phf 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", - "phf_codegen 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", + "phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "tendril 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1157,12 +1179,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "memchr" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1172,10 +1192,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "mime" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1183,9 +1203,9 @@ name = "mime_guess" version = "2.0.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "mime 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", - "phf 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", - "phf_codegen 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", + "phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1194,13 +1214,13 @@ name = "miniz-sys" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "miniz_oxide" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1208,13 +1228,13 @@ dependencies = [ [[package]] name = "miniz_oxide_c_api" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1227,11 +1247,11 @@ dependencies = [ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1243,7 +1263,7 @@ dependencies = [ "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1252,7 +1272,7 @@ version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1272,16 +1292,16 @@ name = "native-tls" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.10.15 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.20 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.39 (registry+https://github.com/rust-lang/crates.io-index)", - "schannel 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.43 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1289,18 +1309,15 @@ name = "net2" version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "new_debug_unreachable" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "nix" @@ -1308,9 +1325,9 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1319,9 +1336,18 @@ name = "nodrop" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "nom" +version = "4.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "notify" -version = "4.0.6" +version = "4.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1330,7 +1356,7 @@ dependencies = [ "fsevent-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", "mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1339,13 +1365,12 @@ dependencies = [ [[package]] name = "num-derive" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1381,44 +1406,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "num_cpus" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "onig" -version = "4.2.1" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "onig_sys 69.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "onig_sys 69.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "onig_sys" -version = "69.0.0" +version = "69.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "openssl" -version = "0.10.15" +version = "0.10.20" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.39 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.43 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1428,12 +1452,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl-sys" -version = "0.9.39" +version = "0.9.43" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1447,43 +1472,22 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "parking_lot_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "parking_lot_core" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1494,7 +1498,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "pest" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ucd-trie 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1502,64 +1506,64 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "pest_generator 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_generator 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pest_generator" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "pest_meta 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)", + "pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_meta 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pest_meta" -version = "2.0.3" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "sha-1 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "phf" -version = "0.7.23" +version = "0.7.24" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "phf_codegen" -version = "0.7.23" +version = "0.7.24" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "phf_generator 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", - "phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "phf_generator" -version = "0.7.23" +version = "0.7.24" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "phf_shared" -version = "0.7.23" +version = "0.7.24" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1573,24 +1577,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "plist" -version = "0.3.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "line-wrap 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "png" -version = "0.12.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "deflate 0.7.19 (registry+https://github.com/rust-lang/crates.io-index)", - "inflate 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1601,7 +1606,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "proc-macro2" -version = "0.4.24" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1616,6 +1621,17 @@ dependencies = [ "getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pulldown-cmark" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "quick-error" version = "1.2.2" @@ -1623,72 +1639,74 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "quote" -version = "0.6.10" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand" -version = "0.4.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand" -version = "0.6.1" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_jitter 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand_chacha" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand_core" -version = "0.2.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand_core" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1696,7 +1714,7 @@ name = "rand_hc" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1704,24 +1722,47 @@ name = "rand_isaac" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_jitter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand_pcg" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand_xorshift" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1730,7 +1771,7 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1740,9 +1781,17 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1754,12 +1803,12 @@ dependencies = [ "fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "library 0.1.0", "site 0.1.0", - "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "redox_syscall" -version = "0.1.44" +version = "0.1.51" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1767,24 +1816,24 @@ name = "redox_termios" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex-syntax" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1805,50 +1854,53 @@ dependencies = [ "config 0.1.0", "errors 0.1.0", "front_matter 0.1.0", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "link_checker 0.1.0", - "pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "pest_derive 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pulldown-cmark 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", + "pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pulldown-cmark 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "slug 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "syntect 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syntect 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "templates 0.1.0", - "tera 0.11.20 (registry+https://github.com/rust-lang/crates.io-index)", + "tera 1.0.0-beta.3 (registry+https://github.com/rust-lang/crates.io-index)", "utils 0.1.0", ] [[package]] name = "reqwest" -version = "0.9.5" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "encoding_rs 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding_rs 0.8.17 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.18 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libflate 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.25 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "resolv-conf" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1857,16 +1909,16 @@ dependencies = [ [[package]] name = "rust-stemmers" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rustc-demangle" -version = "0.1.9" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1882,6 +1934,11 @@ name = "ryu" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "safe-transmute" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "safemem" version = "0.3.0" @@ -1892,7 +1949,7 @@ name = "same-file" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1900,26 +1957,27 @@ name = "sass-rs" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "sass-sys 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "sass-sys 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "sass-sys" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "schannel" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1937,31 +1995,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "search" version = "0.1.0" dependencies = [ - "ammonia 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ammonia 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "elasticlunr-rs 2.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "errors 0.1.0", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "library 0.1.0", ] [[package]] name = "security-framework" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "security-framework-sys" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "MacTypes-sys 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1979,28 +2038,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.82" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde_derive" -version = "1.0.82" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.33" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2010,7 +2069,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2032,11 +2091,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "signal-hook" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arc-swap 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "arc-swap 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2057,17 +2116,17 @@ dependencies = [ "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "sass-rs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "search 0.1.0", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "templates 0.1.0", - "tera 0.11.20 (registry+https://github.com/rust-lang/crates.io-index)", + "tera 1.0.0-beta.3 (registry+https://github.com/rust-lang/crates.io-index)", "utils 0.1.0", ] [[package]] name = "slab" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2085,20 +2144,17 @@ dependencies = [ [[package]] name = "smallvec" -version = "0.6.7" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "socket2" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2109,7 +2165,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "string" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2117,11 +2173,11 @@ name = "string_cache" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "new_debug_unreachable 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", "precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2131,10 +2187,10 @@ name = "string_cache_codegen" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "phf_generator 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", - "phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2158,29 +2214,19 @@ name = "strum_macros" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)", + "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "syn" -version = "0.14.9" +version = "0.15.29" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.10 (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 = "0.15.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2189,42 +2235,42 @@ name = "synstructure" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (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 = "syntect" -version = "3.0.2" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bincode 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bincode 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "flate2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "onig 4.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "plist 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", + "onig 4.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "plist 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "yaml-rust 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tempfile" -version = "3.0.5" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2233,18 +2279,17 @@ dependencies = [ name = "templates" version = "0.1.0" dependencies = [ - "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "config 0.1.0", - "csv 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "csv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "errors 0.1.0", "imageproc 0.1.0", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "library 0.1.0", "pulldown-cmark 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "reqwest 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", - "tera 0.11.20 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "tera 1.0.0-beta.3 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "utils 0.1.0", @@ -2257,27 +2302,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futf 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "utf-8 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", + "utf-8 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tera" -version = "0.11.20" +version = "1.0.0-beta.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "globwalk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "humansize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "pest_derive 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "slug 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unic-segment 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-segment 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "v_htmlescape 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2293,8 +2338,8 @@ name = "termion" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2311,50 +2356,52 @@ name = "thread_local" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tiff" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-derive 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "num-derive 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "time" -version = "0.1.41" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio" -version = "0.1.13" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-current-thread 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-fs 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-sync 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-trace-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-uds 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2362,63 +2409,65 @@ name = "tokio-codec" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-current-thread" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-executor" -version = "0.1.5" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-fs" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-io" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-reactor" -version = "0.1.7" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-sync 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2427,52 +2476,71 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "signal-hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "signal-hook 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tokio-sync" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "tokio-tcp" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-threadpool" -version = "0.1.9" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-deque 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-timer" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-trace-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2480,30 +2548,30 @@ name = "tokio-udp" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-uds" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2511,7 +2579,7 @@ name = "toml" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2527,40 +2595,63 @@ name = "trust-dns-proto" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", "socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "trust-dns-proto" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "trust-dns-resolver" -version = "0.10.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "ipconfig 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "resolv-conf 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "trust-dns-proto 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "resolv-conf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "trust-dns-proto 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2585,46 +2676,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "unic-char-property" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "unic-char-range 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-char-range 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "unic-char-range" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "unic-common" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "unic-segment" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "unic-ucd-segment 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-ucd-segment 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "unic-ucd-segment" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "unic-char-property 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unic-char-range 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unic-ucd-version 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-char-property 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-char-range 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-ucd-version 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "unic-ucd-version" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "unic-common 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-common 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2637,7 +2728,7 @@ dependencies = [ [[package]] name = "unicase" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2653,8 +2744,11 @@ dependencies = [ [[package]] name = "unicode-normalization" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "unicode-segmentation" @@ -2671,14 +2765,6 @@ name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "unreachable" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "url" version = "1.7.2" @@ -2692,7 +2778,7 @@ dependencies = [ [[package]] name = "utf-8" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2705,9 +2791,9 @@ name = "utils" version = "0.1.0" dependencies = [ "errors 0.1.0", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tera 0.11.20 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tera 1.0.0-beta.3 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2715,10 +2801,69 @@ dependencies = [ [[package]] name = "uuid" -version = "0.7.1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "v_escape" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "v_escape_derive 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "v_escape" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "v_escape_derive 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "v_escape_derive" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "v_escape_derive" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand 0.5.5 (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.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "v_htmlescape" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "v_escape 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "v_htmlescape" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "v_escape 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2748,7 +2893,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2792,7 +2937,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi-util" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2809,7 +2954,7 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2833,15 +2978,15 @@ name = "ws" version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", "mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2856,33 +3001,30 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "yaml-rust" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "zola" -version = "0.5.1" +version = "0.6.0" dependencies = [ - "actix-web 0.7.16 (registry+https://github.com/rust-lang/crates.io-index)", + "actix-web 0.7.18 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "errors 0.1.0", "front_matter 0.1.0", - "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "notify 4.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "notify 4.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "rebuild 0.1.0", "site 0.1.0", "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2893,33 +3035,33 @@ dependencies = [ ] [metadata] +"checksum MacTypes-sys 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eaf9f0d0b1cc33a4d2aee14fb4b2eac03462ef4db29c8ac4057327d8a71ad86f" "checksum actix 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6c616db5fa4b0c40702fb75201c2af7f8aa8f3a2e2c1dda3b0655772aa949666" -"checksum actix-net 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "12feb297c0a8b1ad2418d55227c61783111d40fbd49f927a09f8c67683471b8c" -"checksum actix-web 0.7.16 (registry+https://github.com/rust-lang/crates.io-index)" = "9c1ae55616ff06c1d011c4e7f16f443b825df72aaf1c75e97cdc43a4ab83a602" +"checksum actix-net 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8bebfbe6629e0131730746718c9e032b58f02c6ce06ed7c982b9fef6c8545acd" +"checksum actix-web 0.7.18 (registry+https://github.com/rust-lang/crates.io-index)" = "e9f33c941e5e69a58a6bfef33853228042ed3799fc4b5a4923a36a85776fb690" "checksum actix_derive 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4300e9431455322ae393d43a2ba1ef96b8080573c0fc23b196219efedfb6ba69" "checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" -"checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e" -"checksum ammonia 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8b93ecb80665873703bf3b0a77f369c96b183d8e0afaf30a3ff5ff07dfc6409" +"checksum aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5" +"checksum ammonia 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c8cd3dff93e4471fff384645c5625cb8e4349000d8a730b9685bdbb19cbacb4" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -"checksum arc-swap 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "5c5ed110e2537bdd3f5b9091707a8a5556a72ac49bbd7302ae0b28fdccb3246c" +"checksum arc-swap 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1025aeae2b664ca0ea726a89d574fe8f4e77dd712d443236ad1de00379450cf6" "checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" -"checksum arrayvec 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "f405cc4c21cd8b784f6c8fc2adf9bc00f59558f0049b5ec21517f875963040cc" -"checksum askama_escape 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "719b48039ffac1564f67d70162109ba9341125cee0096a540e478355b3c724a7" +"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" -"checksum backtrace 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "a2eff3830839471718ef8522b9025b399bfb713e25bc220da721364efb660d7d" -"checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0" -"checksum base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "621fc7ecb8008f86d7fb9b95356cd692ce9514b80a86d85b397f32a22da7b9e2" -"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" -"checksum bincode 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9f2fb9e29e72fd6bc12071533d5dc7664cb01480c59406f656d7ac25c7bd8ff7" +"checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" +"checksum backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "cd5a90e2b463010cd0e0ce9a11d4a9d5d58d9f41d4a6ba3dcaf9e68b466e88b4" +"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" +"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" +"checksum bincode 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3efe0b4c8eaeed8600549c29f538a6a11bf422858d0ed435b1d70ec4ab101190" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" "checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" "checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" -"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d" -"checksum bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "40ade3d27603c2cb345eb0912aec461a6dec7e06a4ae48589904e808335c7afa" -"checksum cc 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "389803e36973d242e7fecb092b2de44a3d35ac62524b3b9339e51d577d668e02" -"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" +"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" +"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +"checksum cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "c9ce8bb087aacff865633f0bd5aeaed910fe2fe55b55f4739527f2e023a2e53d" +"checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4" "checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" @@ -2928,22 +3070,23 @@ dependencies = [ "checksum core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "286e0b41c3a20da26536c6000a280585d519fd07b3956b43aed8a79e9edce980" "checksum core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "716c271e8613ace48344f723b60b900a93150271e5be206212d052bbc0883efa" "checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" -"checksum crc32fast 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e91d5240c6975ef33aeb5f148f35275c25eda8e8a5f95abe421978b05b8bf192" -"checksum crossbeam-channel 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8b7d034925ce9668a9a19539a82a2ae75660fa65c1a3a5ddbfce333aafcceb55" +"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" +"checksum crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f0ed1a4de2235cabda8558ff5840bffb97fcb64c97827f354a451307df5f72b" "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" -"checksum crossbeam-deque 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4fe1b6f945f824c7a25afe44f62e25d714c0cc523f8e99d8db5cd1026e1269d3" +"checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" -"checksum crossbeam-epoch 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2449aaa4ec7ef96e5fb24db16024b935df718e9ae1cec0a1e68feeca2efca7b8" +"checksum crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "04c9e3102cc2d69cd681412141b390abd55a362afc1540965dad0ad4d34280b4" +"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" -"checksum crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "41ee4864f4797060e52044376f7d107429ce1fb43460021b126424b7180ee21a" -"checksum csv 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d54f6b0fd69128a2894b1a3e57af5849a0963c1cc77b165d30b896e40296452" -"checksum csv-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4dd8e6d86f7ba48b4276ef1317edc8cc36167546d8972feb4a2b5fec0b374105" +"checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" +"checksum csv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9fd1c44c58078cfbeaf11fbb3eac9ae5534c23004ed770cc4bfb48e658ae4f04" +"checksum csv-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa5cdef62f37e6ffe7d1f07a381bc0db32b7a3ff1cac0de56cb0d81e71f53d65" "checksum ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "630391922b1b893692c6334369ff528dcc3a9d8061ccf4c803aa8f83cb13db5e" "checksum deflate 0.7.19 (registry+https://github.com/rust-lang/crates.io-index)" = "8a6abb26e16e8d419b5c78662aa9f82857c2386a073da266840e474d5055ec86" "checksum deunicode 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "850878694b7933ca4c9569d30a34b55031b9b139ee1fc7b94a527c4ef960d690" "checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" "checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd" -"checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" +"checksum either 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c67353c641dc847124ea1902d69bd753dee9bb3beff9aa3662ecf86c971d1fac" "checksum elasticlunr-rs 2.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a99a310cd1f9770e7bf8e48810c7bcbb0e078c8fb23a8c7bcf0da4c2bf61a455" "checksum encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" "checksum encoding-index-japanese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" @@ -2952,20 +3095,20 @@ dependencies = [ "checksum encoding-index-singlebyte 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" "checksum encoding-index-tradchinese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" "checksum encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" -"checksum encoding_rs 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1a8fa54e6689eb2549c4efed8d00d7f3b2b994a064555b0e8df4ae3764bcc4be" -"checksum error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07e791d3be96241c77c43846b665ef1384606da2cd2a48730abe606a12906e02" +"checksum encoding_rs 0.8.17 (registry+https://github.com/rust-lang/crates.io-index)" = "4155785c79f2f6701f185eb2e6b4caf0555ec03477cb4c70db67b465311620ed" "checksum error-chain 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6930e04918388a9a2e41d518c25cf679ccafe26733fb4127dbf21993f2575d46" -"checksum failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6dd377bcc1b1b7ce911967e3ec24fa19c3224394ec05b54aa7b083d498341ac7" -"checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596" +"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" +"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum filetime 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a2df5c1a8c4be27e7707789dc42ae65976e60b394afd293d1419ab915833e646" -"checksum flate2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2291c165c8e703ee54ef3055ad6188e3d51108e2ded18e9f2476e774fc5ad3d4" +"checksum flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f87e68aa82b2de08a6e037f1385455759df6e445a8df5e005b4297191dbf18aa" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" "checksum fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5f2a4a2034423744d2cc7ca2068453168dcdb82c438419e639a26bd87839c674" "checksum fsevent 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "c4bbbf71584aeed076100b5665ac14e3d85eeb31fdbb45fbd41ef9a682b5ec05" "checksum fsevent-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1a772d36c338d07a032d5375a36f15f9a7043bf0cb8ce7cee658e037c6032874" +"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum futf 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7c9c1ce3fa9336301af935ab852c437817d14cd33690446569392e65170aac3b" @@ -2976,20 +3119,22 @@ dependencies = [ "checksum gif 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4bca55ac1f213920ce3527ccd62386f1f15fa3f1714aeee1cf93f2c416903f" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4743617a7464bbda3c8aec8558ff2f9429047e025771037df561d383337ff865" -"checksum h2 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "1ac030ae20dee464c5d0f36544d8b914a6bc606da44a57e052d2b0f5dae129e0" -"checksum heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea04fa3ead4e05e51a7c806fc07271fdbde4e246a6c6d1efd52e72230b771b82" +"checksum globwalk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c7ee1ce235d766a01b481e593804b9356768d1dbd68fc0c063d04b407bee71a" +"checksum h2 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "910a5e7be6283a9c91b3982fa5188368c8719cce2a3cf3b86048673bf9d9c36b" +"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "21ceb46a83a85e824ef93669c8b390009623863b5c195d1ba747292c0c72f94e" "checksum html5ever 0.22.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c213fa6a618dc1da552f54f85cba74b05d8e883c92ec4e89067736938084c26e" -"checksum http 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "02096a6d2c55e63f7fcb800690e4f889a25f6ec342e3adb4594e293b625215ab" +"checksum http 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "fe67e3678f2827030e89cc4b9e7ecd16d52f132c0b940ab5005f88e821500f6a" "checksum httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83" "checksum humansize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6cab2627acfc432780848602f3f558f7e9dd427352224b0d9324025796d2a5e" "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" -"checksum hyper 0.12.18 (registry+https://github.com/rust-lang/crates.io-index)" = "8dd7729fc83d88353415f6816fd4bb00897aa47c7f1506b69060e74e6e3d8e8b" -"checksum hyper-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "32cd73f14ad370d3b4d4b7dce08f69b81536c82e39fcc89731930fe5788cd661" +"checksum hyper 0.12.25 (registry+https://github.com/rust-lang/crates.io-index)" = "7d5b6658b016965ae301fa995306db965c93677880ea70765a84235a96eae896" +"checksum hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3a800d6aa50af4b5850b2b0f659625ce9504df908e9733b635720483be26174f" "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" -"checksum image 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44665b4395d1844c96e7dc8ed5754782a1cdfd9ef458a80bbe45702681450504" +"checksum ignore 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ad03ca67dc12474ecd91fdb94d758cbd20cb4e7a78ebe831df26a9b7511e1162" +"checksum image 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "52fb0666a1273dac46f9725aa4859bcd5595fc3554cf3495051b4de8db745e7d" "checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" -"checksum inflate 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "84c683bde2d8413b8f1be3e459c30e4817672b6e7a31d9212b0323154e76eba7" +"checksum inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" "checksum inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40b54539f3910d6f84fbf9a643efd6e3aa6e4f001426c0329576128255994718" "checksum inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" @@ -2998,177 +3143,187 @@ dependencies = [ "checksum jpeg-decoder 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c8b7d43206b34b3f94ea9445174bda196e772049b9bddbc620c9d29b2d20110d" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" -"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" +"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" -"checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74" -"checksum libflate 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)" = "bff3ac7d6f23730d3b533c35ed75eef638167634476a499feef16c428d74b57b" -"checksum linked-hash-map 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7860ec297f7008ff7a1e3382d7f7e1dcd69efc94751a2284bafc3d013c2aa939" -"checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e" +"checksum libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "aab692d7759f5cd8c859e169db98ae5b52c924add2af5fbbca11d12fefb567c1" +"checksum line-wrap 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" +"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" -"checksum lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4d06ff7ff06f729ce5f4e227876cb88d10bc59cd4ae1e09fbb2bde15c850dc21" +"checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" "checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" "checksum mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" "checksum maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08cbb6b4fef96b6d77bfc40ec491b1690c779e77b05cd9f07f787ed376fd4c43" "checksum markup5ever 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "897636f9850c3eef4905a5540683ed53dc9393860f0846cab2c2ddf9939862ff" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -"checksum memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db4c41318937f6e76648f42826b1d9ade5c09cafb5aef7e351240a70f39206e9" +"checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" -"checksum mime 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "0a907b83e7b9e987032439a387e187119cddafc92d5c2aaeb1d92580a793f630" +"checksum mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "3e27ca21f40a310bd06d9031785f4801710d566c184a6e15bad4f1d9b65f9425" "checksum mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "30de2e4613efcba1ec63d8133f344076952090c122992a903359be5a4f99c3ed" "checksum miniz-sys 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0300eafb20369952951699b68243ab4334f4b10a88f411c221d444b36c40e649" -"checksum miniz_oxide 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5ad30a47319c16cde58d0314f5d98202a80c9083b5f61178457403dfb14e509c" -"checksum miniz_oxide_c_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "28edaef377517fd9fe3e085c37d892ce7acd1fbeab9239c5a36eec352d8a8b7e" +"checksum miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c468f2369f07d651a5d0bb2c9079f8488a66d5466efe42d0c5c6466edcb7f71e" +"checksum miniz_oxide_c_api 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7fe927a42e3807ef71defb191dc87d4e24479b221e67015fe38ae2b7b447bab" "checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432" "checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40" "checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" "checksum native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff8e08de0070bbf4c31f452ea2a70db092f36f6f2e4d897adf5674477d488fb2" "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" -"checksum new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0cdc457076c78ab54d5e0d6fa7c47981757f1e34dc39ff92787f217dede586c4" +"checksum new_debug_unreachable 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f40f005c60db6e03bae699e414c58bf9aa7ea02a2d0b9bfbcf19286cc4c82b30" "checksum nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d37e713a259ff641624b6cb20e3b12b2952313ba36b6823c0f16e6cfd9e5de17" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" -"checksum notify 4.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "873ecfd8c174964ae30f401329d140142312c8e5590719cf1199d5f1717d8078" -"checksum num-derive 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8af1847c907c2f04d7bfd572fb25bbb4385c637fe5be163cf2f8c5d778fe1e7d" +"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" +"checksum notify 4.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "abb1581693e44d8a0ec347ef12289625063f52a1dddc3f3c9befd5fc59e88943" +"checksum num-derive 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d9fe8fcafd1b86a37ce8a1cfa15ae504817e0c8c2e7ad42767371461ac1d316d" "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" "checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" "checksum num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e96f040177bb3da242b5b1ecf3f54b5d5af3efbbfb18608977a5d2767b22f10" "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" -"checksum num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5a69d464bdc213aaaff628444e99578ede64e9c854025aa43b9796530afa9238" -"checksum onig 4.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3febe8cb22362af9e662c9c35e4d8a675de50b1b119823aa556892ac967fb776" -"checksum onig_sys 69.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "78c04019a39ebac42dfd8c7822af0a009043720845a812ddbb95e403298b0183" -"checksum openssl 0.10.15 (registry+https://github.com/rust-lang/crates.io-index)" = "5e1309181cdcbdb51bc3b6bedb33dfac2a83b3d585033d3f6d9e22e8c1928613" +"checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba" +"checksum onig 4.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a646989adad8a19f49be2090374712931c3a59835cb5277b4530f48b417f26e7" +"checksum onig_sys 69.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388410bf5fa341f10e58e6db3975f4bea1ac30247dd79d37a9e5ced3cb4cc3b0" +"checksum openssl 0.10.20 (registry+https://github.com/rust-lang/crates.io-index)" = "5a0d6b781aac4ac1bd6cafe2a2f0ad8c16ae8e1dd5184822a16c50139f8838d9" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" -"checksum openssl-sys 0.9.39 (registry+https://github.com/rust-lang/crates.io-index)" = "278c1ad40a89aa1e741a1eed089a2f60b18fab8089c3139b542140fc7d674106" +"checksum openssl-sys 0.9.43 (registry+https://github.com/rust-lang/crates.io-index)" = "33c86834957dd5b915623e94f2f4ab2c70dd8f6b70679824155d5ae21dbd495d" "checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" -"checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" -"checksum parking_lot 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9723236a9525c757d9725b993511e3fc941e33f27751942232f0058298297edf" -"checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" +"checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" "checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" -"checksum pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a677051ad923732bb5c70f2d45f8985a96e3eee2e2bff86697e3b11b0c3fcfde" -"checksum pest_derive 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b76f477146419bc539a63f4ef40e902166cb43b3e51cecc71d9136fd12c567e7" -"checksum pest_generator 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ebee4e9680be4fd162e6f3394ae4192a6b60b1e4d17d845e631f0c68d1a3386" -"checksum pest_meta 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1f6d5f6f0e6082578c86af197d780dc38328e3f768cec06aac9bc46d714e8221" -"checksum phf 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "cec29da322b242f4c3098852c77a0ca261c9c01b806cae85a5572a1eb94db9a6" -"checksum phf_codegen 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "7d187f00cd98d5afbcd8898f6cf181743a449162aeb329dcd2f3849009e605ad" -"checksum phf_generator 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "03dc191feb9b08b0dc1330d6549b795b9d81aec19efe6b4a45aec8d4caee0c4b" -"checksum phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "b539898d22d4273ded07f64a05737649dc69095d92cb87c7097ec68e3f150b93" +"checksum pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "54f0c72a98d8ab3c99560bfd16df8059cc10e1f9a8e83e6e3b97718dd766e9c3" +"checksum pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +"checksum pest_generator 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "63120576c4efd69615b5537d3d052257328a4ca82876771d6944424ccfd9f646" +"checksum pest_meta 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5a3492a4ed208ffc247adcdcc7ba2a95be3104f58877d0d02f0df39bf3efb5e" +"checksum phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" +"checksum phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" +"checksum phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" +"checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" "checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" -"checksum plist 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0c7316832d9ac5da02786bdc89a3faf0ca07070212b388766e969078fd593edc" -"checksum png 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f54b9600d584d3b8a739e1662a595fab051329eff43f20e7d8cc22872962145b" +"checksum plist 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f4739851c08dd9a62a78beff2edf1a438517268b2c563c42fc6d9d3139e42d2a" +"checksum png 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9adebf7fb91ccf5eac9da1a8e00e83cb8ae882c3e8d8e4ad59da73cb8c82a2c9" "checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" -"checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09" +"checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915" "checksum pulldown-cmark 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eef52fac62d0ea7b9b4dc7da092aa64ea7ec3d90af6679422d3d7e0e14b6ee15" +"checksum pulldown-cmark 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa4987312f985c300f4d68d208a9e4b646268140b6dbe83388c09652cc19ed3f" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" -"checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c" -"checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd" -"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" -"checksum rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae9d223d52ae411a33cf7e54ec6034ec165df296ccd23533d671a28252b6f66a" -"checksum rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "771b009e3a508cb67e8823dda454aaa5368c7bc1c16829fb77d3e980440dd34a" -"checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372" -"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db" +"checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1" +"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" +"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" "checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" "checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -"checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05" -"checksum rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effa3fcaa47e18db002bdde6060944b6d2f9cfd8db471c30e873448ad9187be3" +"checksum rand_jitter 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b9ea758282efe12823e0d952ddb269d2e1897227e464919a554f2a03ef1b832" +"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" "checksum rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "373814f27745b2686b350dd261bfd24576a6fb0e2c5919b3a2b6005f820b0473" "checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356" -"checksum redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "a84bcd297b87a545980a2d25a0beb72a1f490c31f0a9fde52fca35bfbb1ceb70" +"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +"checksum redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)" = "423e376fffca3dfa06c9e9790a9ccd282fafb3cc6e6397d01dbf64f9bacc6b85" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" -"checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f" -"checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1" +"checksum regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53ee8cfdddb2e0291adfb9f13d31d3bbe0a03c9a402c01b1e24188d86c35b24f" +"checksum regex-syntax 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8c2f35eedad5295fdf00a63d7d4b238135723f92b434ec06774dad15c7ab0861" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" -"checksum reqwest 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ab52e462d1e15891441aeefadff68bdea005174328ce3da0a314f2ad313ec837" -"checksum resolv-conf 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c62bd95a41841efdf7fca2ae9951e64a8d8eae7e5da196d8ce489a2241491a92" -"checksum rust-stemmers 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fbf06149ec391025664a5634200ced1afb489f0f3f8a140d515ebc0eb04b4bc0" -"checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" +"checksum reqwest 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)" = "962fa64e670e70b9d3a81c3688832eb59293ef490e0af5ad169763f62016ac5e" +"checksum resolv-conf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b263b4aa1b5de9ffc0054a2386f96992058bb6870aab516f8cdeb8a667d56dcb" +"checksum rust-stemmers 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05928c187b85b38f6b98db43057a24f0245163635a5ce6325a4f77a833d646aa" +"checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" +"checksum safe-transmute 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9604873ffe1980bc1f179103704a65c8aca141c248d9e52b7af95ff10578166e" "checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9" "checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" "checksum sass-rs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90f8cf6e645aa843ffffcbdc1e8752b1f221dfa314c81895aeb229a77aea7e05" -"checksum sass-sys 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "173ac202b4585ecfb1521159491175a787584fcc346457d53a099b240c69cd41" -"checksum schannel 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "0e1a231dc10abf6749cfa5d7767f25888d484201accbd919b66ab5413c502d56" +"checksum sass-sys 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "f88301b9780e715f1ef96b16d33a4d7d917c61ec1caccf26215ebc4bebca58dd" +"checksum schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f6abf258d99c3c1c5c2131d99d064e94b7b3dd5f416483057f308fea253339" "checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" -"checksum security-framework 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "697d3f3c23a618272ead9e1fb259c1411102b31c6af8b93f1d64cca9c3b0e8e0" -"checksum security-framework-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab01dfbe5756785b5b4d46e0289e5a18071dfa9a7c2b24213ea00b9ef9b665bf" +"checksum security-framework 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfab8dda0e7a327c696d893df9ffa19cadc4bd195797997f5223cf5831beaf05" +"checksum security-framework-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3d6696852716b589dff9e886ff83778bb635150168e83afa8ac6b8a78cb82abc" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)" = "6fa52f19aee12441d5ad11c9a00459122bd8f98707cadf9778c540674f1935b6" -"checksum serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)" = "96a7f9496ac65a2db5929afa087b54f8fc5008dcfbe48a8874ed20049b0d6154" -"checksum serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)" = "c37ccd6be3ed1fdf419ee848f7c758eb31b054d7cd3ae3600e3bae0adf569811" +"checksum serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "92514fb95f900c9b5126e32d020f5c6d40564c27a5ea6d1d7d9f157a96623560" +"checksum serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6eabf4b5914e88e24eea240bb7c9f9a2cbc1bbbe8d961d381975ec3c6b806c" +"checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d" "checksum serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d48f9f99cd749a2de71d29da5f948de7f2764cc5a9d7f3c97e3514d4ee6eabf2" "checksum sha-1 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9d1f3b5de8a167ab06834a7c883bd197f2191e1dda1a22d9ccfeedbf9aded" "checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" -"checksum signal-hook 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8941ae94fa73d0f73b422774b3a40a7195cecd88d1c090f4b37ade7dc795ab66" +"checksum signal-hook 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "97a47ae722318beceb0294e6f3d601205a1e6abaa4437d9d33e3a212233e3021" "checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" -"checksum slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5f9776d6b986f77b35c6cf846c11ad986ff128fe0b2b63a3628e3755e8d3102d" +"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum slotmap 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4ed041f7f2ff35f2bf7d688bf30686976512f8300e37433c2c73ea9f4cf14b" "checksum slug 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b3bc762e6a4b6c6fcaade73e77f9ebc6991b676f88bb2358bddb56560f073373" -"checksum smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b73ea3738b47563803ef814925e69be00799a8c07420be8b996f8e98fb2336db" +"checksum smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c4488ae950c49d403731982257768f48fada354a5203fe81f9bb6f43ca9002be" "checksum socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c4d11a52082057d87cb5caa31ad812f4504b97ab44732cd8359df2e9ff9f48e7" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" -"checksum string 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98998cced76115b1da46f63388b909d118a37ae0be0f82ad35773d4a4bc9d18d" +"checksum string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b639411d0b9c738748b5397d5ceba08e648f4f1992231aa859af1a017f31f60b" "checksum string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25d70109977172b127fe834e5449e5ab1740b9ba49fa18a2020f509174f25423" "checksum string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eea1eee654ef80933142157fdad9dd8bc43cf7c74e999e369263496f04ff4da" "checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum strum 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f6c3a2071519ab6a48f465808c4c1ffdd00dfc8e93111d02b4fc5abab177676e" "checksum strum_macros 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8baacebd7b7c9b864d83a6ba7a246232983e277b86fa5cdec77f565715a4b136" -"checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741" -"checksum syn 0.15.22 (registry+https://github.com/rust-lang/crates.io-index)" = "ae8b29eb5210bc5cf63ed6149cbf9adfc82ac0be023d8735c176ee74a2db4da7" +"checksum syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1825685f977249735d510a242a6727b46efe914bb67e38d30c071b1b72b1d5c2" "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" -"checksum syntect 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e02dd9df97a68a2d005ace28ff24c610abfc3ce17afcfdb22a077645dabb599a" -"checksum tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7e91405c14320e5c79b3d148e1c86f40749a36e490642202a31689cb1a3452b2" +"checksum syntect 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e80b8831c5a543192ffc3727f01cf0e57579c6ac15558e3048bfb5708892167b" +"checksum tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b86c784c88d98c801132806dadd3819ed29d8600836c4088e855cdf3e178ed8a" "checksum tendril 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "707feda9f2582d5d680d733e38755547a3e8fb471e7ba11452ecfd9ce93a5d3b" -"checksum tera 0.11.20 (registry+https://github.com/rust-lang/crates.io-index)" = "4b505279e19d8f7d24b1a9dc58327c9c36174b1a2c7ebdeac70792d017cb64f3" +"checksum tera 1.0.0-beta.3 (registry+https://github.com/rust-lang/crates.io-index)" = "dc35f9f4098f84b32579af1fd3d9c850d5c713ea2490c81e1f792a5fbb22e278" "checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" -"checksum tiff 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a2cc6c4fd13cb1cfd20abdb196e794ceccb29371855b7e7f575945f920a5b3c2" -"checksum time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "847da467bf0db05882a9e2375934a8a55cffdc9db0d128af1518200260ba1f6c" -"checksum tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "a7817d4c98cc5be21360b3b37d6036fe9b7aefa5b7a201b7b16ff33423822f7d" +"checksum tiff 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1e4834f28a0330cb9f3f2c87d2649dca723cb33802e2bdcf18da32759fbec7ce" +"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" +"checksum tokio 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "65641e515a437b308ab131a82ce3042ff9795bef5d6c5a9be4eb24195c417fd9" "checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" -"checksum tokio-current-thread 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "331c8acc267855ec06eb0c94618dcbbfea45bed2d20b77252940095273fb58f6" -"checksum tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c117b6cf86bb730aab4834f10df96e4dd586eff2c3c27d3781348da49e255bde" -"checksum tokio-fs 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "60ae25f6b17d25116d2cba342083abe5255d3c2c79cb21ea11aa049c53bf7c75" -"checksum tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "7392fe0a70d5ce0c882c4778116c519bd5dbaa8a7c3ae3d04578b3afafdcda21" -"checksum tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "502b625acb4ee13cbb3b90b8ca80e0addd263ddacf6931666ef751e610b07fb5" +"checksum tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d16217cad7f1b840c5a97dfb3c43b0c871fef423a6e8d2118c604e843662a443" +"checksum tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "83ea44c6c0773cc034771693711c35c677b4b5a4b21b9e7071704c54de7d555e" +"checksum tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe6dc22b08d6993916647d108a1a7d15b9cd29c4f4496c62b92c45b5041b7af" +"checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" +"checksum tokio-reactor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6af16bfac7e112bea8b0442542161bfc41cbfa4466b580bdda7d18cb88b911ce" "checksum tokio-signal 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "dd6dc5276ea05ce379a16de90083ec80836440d5ef8a6a39545a3207373b8296" -"checksum tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7ad235e9dadd126b2d47f6736f65aa1fdcd6420e66ca63f44177bc78df89f912" -"checksum tokio-threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "56c5556262383032878afad66943926a1d1f0967f17e94bd7764ceceb3b70e7f" -"checksum tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4f37f0111d76cc5da132fe9bc0590b9b9cfd079bc7e75ac3846278430a299ff8" +"checksum tokio-sync 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fda385df506bf7546e70872767f71e81640f1f251bdf2fd8eb81a0eaec5fe022" +"checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119" +"checksum tokio-threadpool 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "ec5759cf26cf9659555f36c431b515e3d05f66831741c85b4b5d5dfb9cf1323c" +"checksum tokio-timer 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2910970404ba6fa78c5539126a9ae2045d62e3713041e447f695f41405a120c6" +"checksum tokio-trace-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "350c9edade9830dc185ae48ba45667a445ab59f6167ef6d0254ec9d2430d9dd3" "checksum tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "66268575b80f4a4a710ef83d087fdfeeabdce9b74c797535fbac18a2cb906e92" -"checksum tokio-uds 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "99ce87382f6c1a24b513a72c048b2c8efe66cb5161c9061d00bee510f08dc168" +"checksum tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "037ffc3ba0e12a0ab4aca92e5234e0dedeb48fddf6ccd260f1f150a36a9f2445" "checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" "checksum tower-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b32f72af77f1bfe3d3d4da8516a238ebe7039b51dd8637a09841ac7f16d2c987" "checksum trust-dns-proto 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0838272e89f1c693b4df38dc353412e389cf548ceed6f9fd1af5a8d6e0e7cf74" -"checksum trust-dns-resolver 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4e913a5df94658858e548cc95a3212797ee524e487ede091c32f27ca26e11620" +"checksum trust-dns-proto 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "09144f0992b0870fa8d2972cc069cbf1e3c0fda64d1f3d45c4d68d0e0b52ad4e" +"checksum trust-dns-resolver 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8a9f877f7a1ad821ab350505e1f1b146a4960402991787191d6d8cab2ce2de2c" "checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum ucd-trie 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "71a9c5b1fe77426cf144cc30e49e955270f5086e31a6441dfa8b32efc09b9d77" "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" -"checksum unic-char-property 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce36d3f7ce754afdbccccf8ff0dd0134e50fb44aaae579f96218856e9e5dbd1e" -"checksum unic-char-range 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9ab85fab42ad1b26cafc03bf891f69cb4d6e15f491030e89a0122197baa8ae8" -"checksum unic-common 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff8d4a7ade929ef7d971e16ced21a8cd56a63869aa6032dfb8cb083cf7d077bf" -"checksum unic-segment 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c9ca47cbb09fb5fcd066b5867d11dc528302fa465277882797d6a836e1ee6f9e" -"checksum unic-ucd-segment 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "48f1a08ce0409a9e391b88d1930118eec48af12742fc538bcec55f775865776e" -"checksum unic-ucd-version 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1f5e6c6c53c2d0ece4a5964bc55fcff8602153063cb4fab20958ff32998ff6" +"checksum unic-char-property 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +"checksum unic-char-range 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" +"checksum unic-common 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" +"checksum unic-segment 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23" +"checksum unic-ucd-segment 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700" +"checksum unic-ucd-version 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" -"checksum unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d3218ea14b4edcaccfa0df0a64a3792a2c32cc706f1b336e48867f9d3147f90" +"checksum unicase 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41d17211f887da8e4a70a45b9536f26fc5de166b81e2d5d80de4a17fd22553bd" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" +"checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426" "checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" "checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" -"checksum utf-8 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bab35f71693630bb1953dce0f2bcd780e7cde025027124a202ac08a45ba25141" +"checksum utf-8 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" -"checksum uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dab5c5526c5caa3d106653401a267fed923e7046f35895ffcb5ca42db64942e6" +"checksum uuid 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "600ef8213e9f8a0ac1f876e470e90780ae0478eabce7f76aff41b0f4ef0fd5c0" +"checksum v_escape 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c8b50688edb86f4c092a1a9fe8bda004b0faa3197100897653809e97e09a2814" +"checksum v_escape 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8865501b78eef9193c1b45486acf18ba889e5662eba98854d6fc59d8ecf3542d" +"checksum v_escape_derive 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7cd994c63b487fef7aad31e5394ec04b9e24de7b32ea5251c9fb499cd2cbf44c" +"checksum v_escape_derive 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "306896ff4b75998501263a1dc000456de442e21d68fe8c8bdf75c66a33a58e23" +"checksum v_htmlescape 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "020cae817dc82693aa523f01087b291b1c7a9ac8cea5c12297963f21769fb27f" +"checksum v_htmlescape 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7fbbe0fa88dd36f9c8cf61a218d4b953ba669de4d0785832f33cc72bd081e1be" "checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" @@ -3180,12 +3335,12 @@ dependencies = [ "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "afc5508759c5bf4285e61feb862b6083c8480aec864fa17a81fdec6f69b461ab" +"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" "checksum winreg 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a27a759395c1195c4cc5cda607ef6f8f6498f64e78f7900f5de0a127a424704a" "checksum winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7daf138b6b14196e3830a588acf1e86966c694d3e8fb026fb105b8b5dca07e6e" "checksum ws 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)" = "329d3e6dd450a9c5c73024e1047f0be7e24121a68484eb0b5368977bee3cf8c3" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -"checksum xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c1cb601d29fe2c2ac60a2b2e5e293994d87a1f6fa9687a31a15270f909be9c2" -"checksum yaml-rust 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "95acf0db5515d07da9965ec0e0ba6cc2d825e2caeb7303b66ca441729801254e" +"checksum xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "541b12c998c5b56aa2b4e6f18f03664eef9a4fd0a246a55594efae6cc2d964b5" +"checksum yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d" diff --git a/Cargo.toml b/Cargo.toml index f1806f0..9897ba3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zola" -version = "0.5.1" +version = "0.6.0" authors = ["Vincent Prouillet "] license = "MIT" readme = "README.md" diff --git a/EXAMPLES.md b/EXAMPLES.md index e83a972..60c9589 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -19,5 +19,7 @@ | [Daniel Sockwell's codesections.com](https://www.codesections.com) | https://gitlab.com/codesections/codesections-website | | [Jens Getreu's blog](https://blog.getreu.net) | | | [Matthias Endler](https://matthias-endler.de) | https://github.com/mre/mre.github.io | +| [Michael Plotke](https://michael.plotke.me) | https://gitlab.com/bdjnk/michael | +| [shaleenjain.com](https://shaleenjain.com) | https://github.com/shalzz/shalzz.github.io | | [Hello, Rust!](https://hello-rust.show) | https://github.com/hello-rust/hello-rust.github.io | | [maxdeviant.com](https://maxdeviant.com/) | | diff --git a/README.md b/README.md index 00657b0..a21375a 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ in the `docs/content` folder of the repository and the community can use [its fo | Syntax highlighting | ✔ | ✔ | ✔ | ✔ | | Sass compilation | ✔ | ✔ | ✔ | ✔ | | Assets co-location | ✔ | ✔ | ✔ | ✔ | -| i18n | ✕ | ✕ | ✔ | ✔ | +| Multilingual site | ✔ | ✕ | ✔ | ✔ | | Image processing | ✔ | ✕ | ✔ | ✔ | | Sane & powerful template engine | ✔ | ~ | ~ | ✔ | | Themes | ✔ | ✕ | ✔ | ✔ | diff --git a/appveyor.yml b/appveyor.yml index 0cf36cf..10cd462 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -10,7 +10,7 @@ environment: matrix: - target: x86_64-pc-windows-msvc - RUST_VERSION: 1.29.0 + RUST_VERSION: 1.31.0 - target: x86_64-pc-windows-msvc RUST_VERSION: stable diff --git a/components/config/Cargo.toml b/components/config/Cargo.toml index ad0f929..a8090aa 100644 --- a/components/config/Cargo.toml +++ b/components/config/Cargo.toml @@ -13,3 +13,4 @@ lazy_static = "1" syntect = "3" errors = { path = "../errors" } +utils = { path = "../utils" } diff --git a/components/config/src/config.rs b/components/config/src/config.rs index c80c34b..1f37192 100644 --- a/components/config/src/config.rs +++ b/components/config/src/config.rs @@ -1,6 +1,4 @@ use std::collections::HashMap; -use std::fs::File; -use std::io::prelude::*; use std::path::{Path, PathBuf}; use chrono::Utc; @@ -9,13 +7,29 @@ use syntect::parsing::{SyntaxSet, SyntaxSetBuilder}; use toml; use toml::Value as Toml; -use errors::{Result, ResultExt}; +use errors::Result; use highlighting::THEME_SET; use theme::Theme; +use utils::fs::read_file_with_error; // We want a default base url for tests static DEFAULT_BASE_URL: &'static str = "http://a-website.com"; +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(default)] +pub struct Language { + /// The language code + pub code: String, + /// Whether to generate a RSS feed for that language, defaults to `false` + pub rss: bool, +} + +impl Default for Language { + fn default() -> Language { + Language { code: String::new(), rss: false } + } +} + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(default)] pub struct Taxonomy { @@ -27,6 +41,9 @@ pub struct Taxonomy { pub paginate_path: Option, /// Whether to generate a RSS feed only for each taxonomy term, defaults to false pub rss: bool, + /// The language for that taxonomy, only used in multilingual sites. + /// Defaults to the config `default_language` if not set + pub lang: String, } impl Taxonomy { @@ -49,7 +66,13 @@ impl Taxonomy { impl Default for Taxonomy { fn default() -> Taxonomy { - Taxonomy { name: String::new(), paginate_by: None, paginate_path: None, rss: false } + Taxonomy { + name: String::new(), + paginate_by: None, + paginate_path: None, + rss: false, + lang: String::new(), + } } } @@ -68,6 +91,8 @@ pub struct Config { /// The language used in the site. Defaults to "en" pub default_language: String, + /// The list of supported languages outside of the default one + pub languages: Vec, /// Languages list and translated strings pub translations: HashMap, @@ -148,20 +173,23 @@ impl Config { Some(glob_set_builder.build().expect("Bad ignored_content in config file.")); } + for taxonomy in config.taxonomies.iter_mut() { + if taxonomy.lang.is_empty() { + taxonomy.lang = config.default_language.clone(); + } + } + Ok(config) } /// Parses a config file from the given path pub fn from_file>(path: P) -> Result { - let mut content = String::new(); let path = path.as_ref(); let file_name = path.file_name().unwrap(); - File::open(path) - .chain_err(|| { - format!("No `{:?}` file found. Are you in the right directory?", file_name) - })? - .read_to_string(&mut content)?; - + let content = read_file_with_error( + path, + &format!("No `{:?}` file found. Are you in the right directory?", file_name), + )?; Config::parse(&content) } @@ -227,6 +255,16 @@ impl Config { let theme = Theme::from_file(path)?; self.add_theme_extra(&theme) } + + /// Is this site using i18n? + pub fn is_multilingual(&self) -> bool { + !self.languages.is_empty() + } + + /// Returns the codes of all additional languages + pub fn languages_codes(&self) -> Vec<&str> { + self.languages.iter().map(|l| l.code.as_ref()).collect() + } } impl Default for Config { @@ -239,6 +277,7 @@ impl Default for Config { highlight_code: false, highlight_theme: "base16-ocean-dark".to_string(), default_language: "en".to_string(), + languages: Vec::new(), generate_rss: false, rss_limit: None, taxonomies: Vec::new(), diff --git a/components/config/src/lib.rs b/components/config/src/lib.rs index 621e3aa..3c7443a 100644 --- a/components/config/src/lib.rs +++ b/components/config/src/lib.rs @@ -1,18 +1,20 @@ #[macro_use] extern crate serde_derive; -extern crate toml; -#[macro_use] -extern crate errors; extern crate chrono; extern crate globset; +extern crate toml; #[macro_use] extern crate lazy_static; extern crate syntect; +#[macro_use] +extern crate errors; +extern crate utils; + mod config; pub mod highlighting; mod theme; -pub use config::{Config, Taxonomy}; +pub use config::{Config, Language, Taxonomy}; use std::path::Path; diff --git a/components/config/src/theme.rs b/components/config/src/theme.rs index 1bce6bf..d6bd40c 100644 --- a/components/config/src/theme.rs +++ b/components/config/src/theme.rs @@ -1,11 +1,10 @@ use std::collections::HashMap; -use std::fs::File; -use std::io::prelude::*; use std::path::PathBuf; use toml::Value as Toml; -use errors::{Result, ResultExt}; +use errors::Result; +use utils::fs::read_file_with_error; /// Holds the data from a `theme.toml` file. /// There are other fields than `extra` in it but Zola @@ -40,15 +39,12 @@ impl Theme { /// Parses a theme file from the given path pub fn from_file(path: &PathBuf) -> Result { - let mut content = String::new(); - File::open(path) - .chain_err(|| { - "No `theme.toml` file found. \ - Is the `theme` defined in your `config.toml present in the `themes` directory \ - and does it have a `theme.toml` inside?" - })? - .read_to_string(&mut content)?; - + let content = read_file_with_error( + path, + "No `theme.toml` file found. \ + Is the `theme` defined in your `config.toml present in the `themes` directory \ + and does it have a `theme.toml` inside?", + )?; Theme::parse(&content) } } diff --git a/components/errors/Cargo.toml b/components/errors/Cargo.toml index eacf916..11b0863 100644 --- a/components/errors/Cargo.toml +++ b/components/errors/Cargo.toml @@ -4,8 +4,7 @@ version = "0.1.0" authors = ["Vincent Prouillet "] [dependencies] -error-chain = "0.12" -tera = "0.11" +tera = "1.0.0-alpha.3" toml = "0.4" -image = "0.20" +image = "0.21" syntect = "3" diff --git a/components/errors/src/lib.rs b/components/errors/src/lib.rs index 0d0a32d..e9a271f 100755 --- a/components/errors/src/lib.rs +++ b/components/errors/src/lib.rs @@ -1,26 +1,108 @@ -#![allow(unused_doc_comments)] - -#[macro_use] -extern crate error_chain; extern crate image; extern crate syntect; extern crate tera; extern crate toml; -error_chain! { - errors {} +use std::convert::Into; +use std::error::Error as StdError; +use std::fmt; + +#[derive(Debug)] +pub enum ErrorKind { + Msg(String), + Tera(tera::Error), + Io(::std::io::Error), + Toml(toml::de::Error), + Image(image::ImageError), + Syntect(syntect::LoadingError), +} + +/// The Error type +#[derive(Debug)] +pub struct Error { + /// Kind of error + pub kind: ErrorKind, + pub source: Option>, +} +unsafe impl Sync for Error {} +unsafe impl Send for Error {} + +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + let mut source = self.source.as_ref().map(|c| &**c); + if source.is_none() { + match self.kind { + ErrorKind::Tera(ref err) => source = err.source(), + _ => (), + }; + } + + source + } +} - links { - Tera(tera::Error, tera::ErrorKind); +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.kind { + ErrorKind::Msg(ref message) => write!(f, "{}", message), + ErrorKind::Tera(ref e) => write!(f, "{}", e), + ErrorKind::Io(ref e) => write!(f, "{}", e), + ErrorKind::Toml(ref e) => write!(f, "{}", e), + ErrorKind::Image(ref e) => write!(f, "{}", e), + ErrorKind::Syntect(ref e) => write!(f, "{}", e), + } } +} - foreign_links { - Io(::std::io::Error); - Toml(toml::de::Error); - Image(image::ImageError); - Syntect(syntect::LoadingError); +impl Error { + /// Creates generic error + pub fn msg(value: impl ToString) -> Self { + Self { kind: ErrorKind::Msg(value.to_string()), source: None } + } + + /// Creates generic error with a cause + pub fn chain(value: impl ToString, source: impl Into>) -> Self { + Self { kind: ErrorKind::Msg(value.to_string()), source: Some(source.into()) } + } +} + +impl From<&str> for Error { + fn from(e: &str) -> Self { + Self::msg(e) + } +} +impl From for Error { + fn from(e: String) -> Self { + Self::msg(e) + } +} +impl From for Error { + fn from(e: toml::de::Error) -> Self { + Self { kind: ErrorKind::Toml(e), source: None } + } +} +impl From for Error { + fn from(e: syntect::LoadingError) -> Self { + Self { kind: ErrorKind::Syntect(e), source: None } + } +} +impl From for Error { + fn from(e: tera::Error) -> Self { + Self { kind: ErrorKind::Tera(e), source: None } + } +} +impl From<::std::io::Error> for Error { + fn from(e: ::std::io::Error) -> Self { + Self { kind: ErrorKind::Io(e), source: None } + } +} +impl From for Error { + fn from(e: image::ImageError) -> Self { + Self { kind: ErrorKind::Image(e), source: None } } } +/// Convenient wrapper around std::Result. +pub type Result = ::std::result::Result; // So we can use bail! in all other crates #[macro_export] diff --git a/components/front_matter/Cargo.toml b/components/front_matter/Cargo.toml index 1de7545..3b80d83 100644 --- a/components/front_matter/Cargo.toml +++ b/components/front_matter/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Vincent Prouillet "] [dependencies] -tera = "0.11" +tera = "1.0.0-alpha.3" chrono = "0.4" serde = "1" serde_derive = "1" diff --git a/components/front_matter/src/lib.rs b/components/front_matter/src/lib.rs index 986399c..204582c 100644 --- a/components/front_matter/src/lib.rs +++ b/components/front_matter/src/lib.rs @@ -12,7 +12,7 @@ extern crate toml; extern crate errors; extern crate utils; -use errors::{Result, ResultExt}; +use errors::{Error, Result}; use regex::Regex; use std::path::Path; @@ -71,8 +71,11 @@ pub fn split_section_content( content: &str, ) -> Result<(SectionFrontMatter, String)> { let (front_matter, content) = split_content(file_path, content)?; - let meta = SectionFrontMatter::parse(&front_matter).chain_err(|| { - format!("Error when parsing front matter of section `{}`", file_path.to_string_lossy()) + let meta = SectionFrontMatter::parse(&front_matter).map_err(|e| { + Error::chain( + format!("Error when parsing front matter of section `{}`", file_path.to_string_lossy()), + e, + ) })?; Ok((meta, content)) } @@ -81,8 +84,11 @@ pub fn split_section_content( /// Returns a parsed `PageFrontMatter` and the rest of the content pub fn split_page_content(file_path: &Path, content: &str) -> Result<(PageFrontMatter, String)> { let (front_matter, content) = split_content(file_path, content)?; - let meta = PageFrontMatter::parse(&front_matter).chain_err(|| { - format!("Error when parsing front matter of page `{}`", file_path.to_string_lossy()) + let meta = PageFrontMatter::parse(&front_matter).map_err(|e| { + Error::chain( + format!("Error when parsing front matter of page `{}`", file_path.to_string_lossy()), + e, + ) })?; Ok((meta, content)) } diff --git a/components/imageproc/Cargo.toml b/components/imageproc/Cargo.toml index 0851e01..2a88794 100644 --- a/components/imageproc/Cargo.toml +++ b/components/imageproc/Cargo.toml @@ -6,8 +6,8 @@ authors = ["Vojtěch Král "] [dependencies] lazy_static = "1" regex = "1.0" -tera = "0.11" -image = "0.20" +tera = "1.0.0-alpha.3" +image = "0.21" rayon = "1" errors = { path = "../errors" } diff --git a/components/imageproc/src/lib.rs b/components/imageproc/src/lib.rs index b73a7b5..91d1ec3 100644 --- a/components/imageproc/src/lib.rs +++ b/components/imageproc/src/lib.rs @@ -15,18 +15,19 @@ use std::hash::{Hash, Hasher}; use std::path::{Path, PathBuf}; use image::jpeg::JPEGEncoder; +use image::png::PNGEncoder; use image::{FilterType, GenericImageView}; use rayon::prelude::*; use regex::Regex; -use errors::{Result, ResultExt}; +use errors::{Error, Result}; use utils::fs as ufs; static RESIZED_SUBDIR: &'static str = "processed_images"; lazy_static! { pub static ref RESIZED_FILENAME: Regex = - Regex::new(r#"([0-9a-f]{16})([0-9a-f]{2})[.]jpg"#).unwrap(); + Regex::new(r#"([0-9a-f]{16})([0-9a-f]{2})[.](jpg|png)"#).unwrap(); } /// Describes the precise kind of a resize operation @@ -136,12 +137,78 @@ impl Hash for ResizeOp { } } +/// Thumbnail image format +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Format { + /// JPEG, The `u8` argument is JPEG quality (in percent). + Jpeg(u8), + /// PNG + Png, +} + +impl Format { + pub fn from_args(source: &str, format: &str, quality: u8) -> Result { + use Format::*; + + assert!(quality > 0 && quality <= 100, "Jpeg quality must be within the range [1; 100]"); + + match format { + "auto" => match Self::is_lossy(source) { + Some(true) => Ok(Jpeg(quality)), + Some(false) => Ok(Png), + None => Err(format!("Unsupported image file: {}", source).into()), + }, + "jpeg" | "jpg" => Ok(Jpeg(quality)), + "png" => Ok(Png), + _ => Err(format!("Invalid image format: {}", format).into()), + } + } + + /// Looks at file's extension and, if it's a supported image format, returns whether the format is lossless + pub fn is_lossy>(p: P) -> Option { + p.as_ref() + .extension() + .and_then(|s| s.to_str()) + .map(|ext| match ext.to_lowercase().as_str() { + "jpg" | "jpeg" => Some(true), + "png" => Some(false), + "gif" => Some(false), + "bmp" => Some(false), + _ => None, + }) + .unwrap_or(None) + } + + fn extension(&self) -> &str { + // Kept in sync with RESIZED_FILENAME and op_filename + use Format::*; + + match *self { + Png => "png", + Jpeg(_) => "jpg", + } + } +} + +impl Hash for Format { + fn hash(&self, hasher: &mut H) { + use Format::*; + + let q = match *self { + Png => 0, + Jpeg(q) => q, + }; + + hasher.write_u8(q); + } +} + /// Holds all data needed to perform a resize operation #[derive(Debug, PartialEq, Eq)] pub struct ImageOp { source: String, op: ResizeOp, - quality: u8, + format: Format, /// Hash of the above parameters hash: u64, /// If there is a hash collision with another ImageOp, this contains a sequential ID > 1 @@ -152,14 +219,14 @@ pub struct ImageOp { } impl ImageOp { - pub fn new(source: String, op: ResizeOp, quality: u8) -> ImageOp { + pub fn new(source: String, op: ResizeOp, format: Format) -> ImageOp { let mut hasher = DefaultHasher::new(); hasher.write(source.as_ref()); op.hash(&mut hasher); - hasher.write_u8(quality); + format.hash(&mut hasher); let hash = hasher.finish(); - ImageOp { source, op, quality, hash, collision_id: 0 } + ImageOp { source, op, format, hash, collision_id: 0 } } pub fn from_args( @@ -167,10 +234,12 @@ impl ImageOp { op: &str, width: Option, height: Option, + format: &str, quality: u8, ) -> Result { let op = ResizeOp::from_args(op, width, height)?; - Ok(Self::new(source, op, quality)) + let format = Format::from_args(&source, format, quality)?; + Ok(Self::new(source, op, format)) } fn perform(&self, content_path: &Path, target_path: &Path) -> Result<()> { @@ -184,7 +253,7 @@ impl ImageOp { let mut img = image::open(&src_path)?; let (img_w, img_h) = img.dimensions(); - const RESIZE_FILTER: FilterType = FilterType::Gaussian; + const RESIZE_FILTER: FilterType = FilterType::Lanczos3; const RATIO_EPSILLION: f32 = 0.1; let img = match self.op { @@ -223,9 +292,19 @@ impl ImageOp { }; let mut f = File::create(target_path)?; - let mut enc = JPEGEncoder::new_with_quality(&mut f, self.quality); let (img_w, img_h) = img.dimensions(); - enc.encode(&img.raw_pixels(), img_w, img_h, img.color())?; + + match self.format { + Format::Png => { + let mut enc = PNGEncoder::new(&mut f); + enc.encode(&img.raw_pixels(), img_w, img_h, img.color())?; + } + Format::Jpeg(q) => { + let mut enc = JPEGEncoder::new_with_quality(&mut f, q); + enc.encode(&img.raw_pixels(), img_w, img_h, img.color())?; + } + } + Ok(()) } } @@ -323,20 +402,21 @@ impl Processor { collision_id } - fn op_filename(hash: u64, collision_id: u32) -> String { + fn op_filename(hash: u64, collision_id: u32, format: Format) -> String { // Please keep this in sync with RESIZED_FILENAME assert!(collision_id < 256, "Unexpectedly large number of collisions: {}", collision_id); - format!("{:016x}{:02x}.jpg", hash, collision_id) + format!("{:016x}{:02x}.{}", hash, collision_id, format.extension()) } - fn op_url(&self, hash: u64, collision_id: u32) -> String { - format!("{}/{}", &self.resized_url, Self::op_filename(hash, collision_id)) + fn op_url(&self, hash: u64, collision_id: u32, format: Format) -> String { + format!("{}/{}", &self.resized_url, Self::op_filename(hash, collision_id, format)) } pub fn insert(&mut self, img_op: ImageOp) -> String { let hash = img_op.hash; + let format = img_op.format; let collision_id = self.insert_with_collisions(img_op); - self.op_url(hash, collision_id) + self.op_url(hash, collision_id, format) } pub fn prune(&self) -> Result<()> { @@ -373,25 +453,11 @@ impl Processor { self.img_ops .par_iter() .map(|(hash, op)| { - let target = self.resized_path.join(Self::op_filename(*hash, op.collision_id)); + let target = + self.resized_path.join(Self::op_filename(*hash, op.collision_id, op.format)); op.perform(&self.content_path, &target) - .chain_err(|| format!("Failed to process image: {}", op.source)) + .map_err(|e| Error::chain(format!("Failed to process image: {}", op.source), e)) }) .collect::>() } } - -/// Looks at file's extension and returns whether it's a supported image format -pub fn file_is_img>(p: P) -> bool { - p.as_ref() - .extension() - .and_then(|s| s.to_str()) - .map(|ext| match ext.to_lowercase().as_str() { - "jpg" | "jpeg" => true, - "png" => true, - "gif" => true, - "bmp" => true, - _ => false, - }) - .unwrap_or(false) -} diff --git a/components/library/Cargo.toml b/components/library/Cargo.toml index 1391189..289d3e3 100644 --- a/components/library/Cargo.toml +++ b/components/library/Cargo.toml @@ -7,7 +7,7 @@ authors = ["Vincent Prouillet "] slotmap = "0.2" rayon = "1" chrono = { version = "0.4", features = ["serde"] } -tera = "0.11" +tera = "1.0.0-alpha.3" serde = "1" serde_derive = "1" slug = "0.1" diff --git a/components/library/src/content/file_info.rs b/components/library/src/content/file_info.rs index 73ffaa5..8d58ebe 100644 --- a/components/library/src/content/file_info.rs +++ b/components/library/src/content/file_info.rs @@ -1,5 +1,8 @@ use std::path::{Path, PathBuf}; +use config::Config; +use errors::Result; + /// Takes a full path to a file and returns only the components after the first `content` directory /// Will not return the filename as last component pub fn find_content_components>(path: P) -> Vec { @@ -28,7 +31,10 @@ pub fn find_content_components>(path: P) -> Vec { pub struct FileInfo { /// The full path to the .md file pub path: PathBuf, + /// The on-disk filename, will differ from the `name` when there is a language code in it + pub filename: String, /// The name of the .md file without the extension, always `_index` for sections + /// Doesn't contain the language if there was one in the filename pub name: String, /// The .md path, starting from the content directory, with `/` slashes pub relative: String, @@ -40,14 +46,19 @@ pub struct FileInfo { /// For example a file at content/kb/solutions/blabla.md will have 2 components: /// `kb` and `solutions` pub components: Vec, + /// This is `parent` + `name`, used to find content referring to the same content but in + /// various languages. + pub canonical: PathBuf, } impl FileInfo { - pub fn new_page(path: &Path) -> FileInfo { + pub fn new_page(path: &Path, base_path: &PathBuf) -> FileInfo { let file_path = path.to_path_buf(); - let mut parent = file_path.parent().unwrap().to_path_buf(); + let mut parent = file_path.parent().expect("Get parent of page").to_path_buf(); let name = path.file_stem().unwrap().to_string_lossy().to_string(); - let mut components = find_content_components(&file_path); + let mut components = find_content_components( + &file_path.strip_prefix(base_path).expect("Strip base path prefix for page"), + ); let relative = if !components.is_empty() { format!("{}/{}.md", components.join("/"), name) } else { @@ -55,16 +66,20 @@ impl FileInfo { }; // If we have a folder with an asset, don't consider it as a component - if !components.is_empty() && name == "index" { + // Splitting on `.` as we might have a language so it isn't *only* index but also index.fr + // etc + if !components.is_empty() && name.split('.').collect::>()[0] == "index" { components.pop(); // also set parent_path to grandparent instead parent = parent.parent().unwrap().to_path_buf(); } FileInfo { + filename: file_path.file_name().unwrap().to_string_lossy().to_string(), path: file_path, // We don't care about grand parent for pages grand_parent: None, + canonical: parent.join(&name), parent, name, components, @@ -72,26 +87,61 @@ impl FileInfo { } } - pub fn new_section(path: &Path) -> FileInfo { - let parent = path.parent().unwrap().to_path_buf(); - let components = find_content_components(path); - let relative = if components.is_empty() { - // the index one - "_index.md".to_string() + pub fn new_section(path: &Path, base_path: &PathBuf) -> FileInfo { + let file_path = path.to_path_buf(); + let parent = path.parent().expect("Get parent of section").to_path_buf(); + let name = path.file_stem().unwrap().to_string_lossy().to_string(); + let components = find_content_components( + &file_path.strip_prefix(base_path).expect("Strip base path prefix for section"), + ); + let relative = if !components.is_empty() { + format!("{}/{}.md", components.join("/"), name) } else { - format!("{}/_index.md", components.join("/")) + format!("{}.md", name) }; let grand_parent = parent.parent().map(|p| p.to_path_buf()); FileInfo { - path: path.to_path_buf(), + filename: file_path.file_name().unwrap().to_string_lossy().to_string(), + path: file_path, + canonical: parent.join(&name), parent, grand_parent, - name: "_index".to_string(), + name, components, relative, } } + + /// Look for a language in the filename. + /// If a language has been found, update the name of the file in this struct to + /// remove it and return the language code + pub fn find_language(&mut self, config: &Config) -> Result { + // No languages? Nothing to do + if !config.is_multilingual() { + return Ok(config.default_language.clone()); + } + + if !self.name.contains('.') { + return Ok(config.default_language.clone()); + } + + // Go with the assumption that no one is using `.` in filenames when using i18n + // We can document that + let mut parts: Vec = self.name.splitn(2, '.').map(|s| s.to_string()).collect(); + + // The language code is not present in the config: typo or the user forgot to add it to the + // config + if !config.languages_codes().contains(&parts[1].as_ref()) { + bail!("File {:?} has a language code of {} which isn't present in the config.toml `languages`", self.path, parts[1]); + } + + self.name = parts.swap_remove(0); + self.canonical = self.parent.join(&self.name); + let lang = parts.swap_remove(0); + + Ok(lang) + } } #[doc(hidden)] @@ -101,16 +151,22 @@ impl Default for FileInfo { path: PathBuf::new(), parent: PathBuf::new(), grand_parent: None, + filename: String::new(), name: String::new(), components: vec![], relative: String::new(), + canonical: PathBuf::new(), } } } #[cfg(test)] mod tests { - use super::find_content_components; + use std::path::{Path, PathBuf}; + + use config::{Config, Language}; + + use super::{find_content_components, FileInfo}; #[test] fn can_find_content_components() { @@ -118,4 +174,86 @@ mod tests { find_content_components("/home/vincent/code/site/content/posts/tutorials/python.md"); assert_eq!(res, ["posts".to_string(), "tutorials".to_string()]); } + + #[test] + fn can_find_components_in_page_with_assets() { + let file = FileInfo::new_page( + &Path::new("/home/vincent/code/site/content/posts/tutorials/python/index.md"), + &PathBuf::new(), + ); + assert_eq!(file.components, ["posts".to_string(), "tutorials".to_string()]); + } + + #[test] + fn doesnt_fail_with_multiple_content_directories() { + let file = FileInfo::new_page( + &Path::new("/home/vincent/code/content/site/content/posts/tutorials/python/index.md"), + &PathBuf::from("/home/vincent/code/content/site"), + ); + assert_eq!(file.components, ["posts".to_string(), "tutorials".to_string()]); + } + + #[test] + fn can_find_valid_language_in_page() { + let mut config = Config::default(); + config.languages.push(Language { code: String::from("fr"), rss: false }); + let mut file = FileInfo::new_page( + &Path::new("/home/vincent/code/site/content/posts/tutorials/python.fr.md"), + &PathBuf::new(), + ); + let res = file.find_language(&config); + assert!(res.is_ok()); + assert_eq!(res.unwrap(), "fr"); + } + + #[test] + fn can_find_valid_language_in_page_with_assets() { + let mut config = Config::default(); + config.languages.push(Language { code: String::from("fr"), rss: false }); + let mut file = FileInfo::new_page( + &Path::new("/home/vincent/code/site/content/posts/tutorials/python/index.fr.md"), + &PathBuf::new(), + ); + assert_eq!(file.components, ["posts".to_string(), "tutorials".to_string()]); + let res = file.find_language(&config); + assert!(res.is_ok()); + assert_eq!(res.unwrap(), "fr"); + } + + #[test] + fn do_nothing_on_unknown_language_in_page_with_i18n_off() { + let config = Config::default(); + let mut file = FileInfo::new_page( + &Path::new("/home/vincent/code/site/content/posts/tutorials/python.fr.md"), + &PathBuf::new(), + ); + let res = file.find_language(&config); + assert!(res.is_ok()); + assert_eq!(res.unwrap(), config.default_language); + } + + #[test] + fn errors_on_unknown_language_in_page_with_i18n_on() { + let mut config = Config::default(); + config.languages.push(Language { code: String::from("it"), rss: false }); + let mut file = FileInfo::new_page( + &Path::new("/home/vincent/code/site/content/posts/tutorials/python.fr.md"), + &PathBuf::new(), + ); + let res = file.find_language(&config); + assert!(res.is_err()); + } + + #[test] + fn can_find_valid_language_in_section() { + let mut config = Config::default(); + config.languages.push(Language { code: String::from("fr"), rss: false }); + let mut file = FileInfo::new_section( + &Path::new("/home/vincent/code/site/content/posts/tutorials/_index.fr.md"), + &PathBuf::new(), + ); + let res = file.find_language(&config); + assert!(res.is_ok()); + assert_eq!(res.unwrap(), "fr"); + } } diff --git a/components/library/src/content/page.rs b/components/library/src/content/page.rs index 3e75b3b..679764d 100644 --- a/components/library/src/content/page.rs +++ b/components/library/src/content/page.rs @@ -8,7 +8,7 @@ use slug::slugify; use tera::{Context as TeraContext, Tera}; use config::Config; -use errors::{Result, ResultExt}; +use errors::{Error, Result}; use front_matter::{split_page_content, InsertAnchor, PageFrontMatter}; use library::Library; use rendering::{render_content, Header, RenderContext}; @@ -71,14 +71,19 @@ pub struct Page { /// How long would it take to read the raw content. /// See `get_reading_analytics` on how it is calculated pub reading_time: Option, + /// The language of that page. Equal to the default lang if the user doesn't setup `languages` in config. + /// Corresponds to the lang in the {slug}.{lang}.md file scheme + pub lang: String, + /// Contains all the translated version of that page + pub translations: Vec, } impl Page { - pub fn new>(file_path: P, meta: PageFrontMatter) -> Page { + pub fn new>(file_path: P, meta: PageFrontMatter, base_path: &PathBuf) -> Page { let file_path = file_path.as_ref(); Page { - file: FileInfo::new_page(file_path), + file: FileInfo::new_page(file_path, base_path), meta, ancestors: vec![], raw_content: "".to_string(), @@ -97,6 +102,8 @@ impl Page { toc: vec![], word_count: None, reading_time: None, + lang: String::new(), + translations: Vec::new(), } } @@ -107,9 +114,16 @@ impl Page { /// Parse a page given the content of the .md file /// Files without front matter or with invalid front matter are considered /// erroneous - pub fn parse(file_path: &Path, content: &str, config: &Config) -> Result { + pub fn parse( + file_path: &Path, + content: &str, + config: &Config, + base_path: &PathBuf, + ) -> Result { let (meta, content) = split_page_content(file_path, content)?; - let mut page = Page::new(file_path, meta); + let mut page = Page::new(file_path, meta, base_path); + + page.lang = page.file.find_language(config)?; page.raw_content = content; let (word_count, reading_time) = get_reading_analytics(&page.raw_content); @@ -117,7 +131,16 @@ impl Page { page.reading_time = Some(reading_time); let mut slug_from_dated_filename = None; - if let Some(ref caps) = RFC3339_DATE.captures(&page.file.name.replace(".md", "")) { + let file_path = if page.file.name == "index" { + if let Some(parent) = page.file.path.parent() { + parent.file_name().unwrap().to_str().unwrap().to_string() + } else { + page.file.name.replace(".md", "") + } + } else { + page.file.name.replace(".md", "") + }; + if let Some(ref caps) = RFC3339_DATE.captures(&file_path) { slug_from_dated_filename = Some(caps.name("slug").unwrap().as_str().to_string()); if page.meta.date.is_none() { page.meta.date = Some(caps.name("datetime").unwrap().as_str().to_string()); @@ -130,7 +153,11 @@ impl Page { slug.trim().to_string() } else if page.file.name == "index" { if let Some(parent) = page.file.path.parent() { - slugify(parent.file_name().unwrap().to_str().unwrap()) + if let Some(slug) = slug_from_dated_filename { + slugify(&slug) + } else { + slugify(parent.file_name().unwrap().to_str().unwrap()) + } } else { slugify(&page.file.name) } @@ -144,13 +171,19 @@ impl Page { }; if let Some(ref p) = page.meta.path { - page.path = p.trim().trim_left_matches('/').to_string(); + page.path = p.trim().trim_start_matches('/').to_string(); } else { - page.path = if page.file.components.is_empty() { + let mut path = if page.file.components.is_empty() { page.slug.clone() } else { format!("{}/{}", page.file.components.join("/"), page.slug) }; + + if page.lang != config.default_language { + path = format!("{}/{}", page.lang, path); + } + + page.path = path; } if !page.path.ends_with('/') { page.path = format!("{}/", page.path); @@ -168,10 +201,14 @@ impl Page { } /// Read and parse a .md file into a Page struct - pub fn from_file>(path: P, config: &Config) -> Result { + pub fn from_file>( + path: P, + config: &Config, + base_path: &PathBuf, + ) -> Result { let path = path.as_ref(); let content = read_file(path)?; - let mut page = Page::parse(path, &content, config)?; + let mut page = Page::parse(path, &content, config, base_path)?; if page.file.name == "index" { let parent_dir = path.parent().unwrap(); @@ -218,8 +255,9 @@ impl Page { context.tera_context.insert("page", &SerializingPage::from_page_basic(self, None)); - let res = render_content(&self.raw_content, &context) - .chain_err(|| format!("Failed to render content of {}", self.file.path.display()))?; + let res = render_content(&self.raw_content, &context).map_err(|e| { + Error::chain(format!("Failed to render content of {}", self.file.path.display()), e) + })?; self.summary = res.summary_len.map(|l| res.body[0..l].to_owned()); self.content = res.body; @@ -240,9 +278,12 @@ impl Page { context.insert("current_url", &self.permalink); context.insert("current_path", &self.path); context.insert("page", &self.to_serialized(library)); + context.insert("lang", &self.lang); + context.insert("toc", &self.toc); - render_template(&tpl_name, tera, &context, &config.theme) - .chain_err(|| format!("Failed to render page '{}'", self.file.path.display())) + render_template(&tpl_name, tera, context, &config.theme).map_err(|e| { + Error::chain(format!("Failed to render page '{}'", self.file.path.display()), e) + }) } /// Creates a vectors of asset URLs. @@ -286,6 +327,8 @@ impl Default for Page { toc: vec![], word_count: None, reading_time: None, + lang: String::new(), + translations: Vec::new(), } } } @@ -295,14 +338,14 @@ mod tests { use std::collections::HashMap; use std::fs::{create_dir, File}; use std::io::Write; - use std::path::Path; + use std::path::{Path, PathBuf}; use globset::{Glob, GlobSetBuilder}; use tempfile::tempdir; use tera::Tera; use super::Page; - use config::Config; + use config::{Config, Language}; use front_matter::InsertAnchor; #[test] @@ -314,7 +357,7 @@ description = "hey there" slug = "hello-world" +++ Hello world"#; - let res = Page::parse(Path::new("post.md"), content, &Config::default()); + let res = Page::parse(Path::new("post.md"), content, &Config::default(), &PathBuf::new()); assert!(res.is_ok()); let mut page = res.unwrap(); page.render_markdown( @@ -340,7 +383,8 @@ Hello world"#; Hello world"#; let mut conf = Config::default(); conf.base_url = "http://hello.com/".to_string(); - let res = Page::parse(Path::new("content/posts/intro/start.md"), content, &conf); + let res = + Page::parse(Path::new("content/posts/intro/start.md"), content, &conf, &PathBuf::new()); assert!(res.is_ok()); let page = res.unwrap(); assert_eq!(page.path, "posts/intro/hello-world/"); @@ -356,7 +400,7 @@ Hello world"#; +++ Hello world"#; let config = Config::default(); - let res = Page::parse(Path::new("start.md"), content, &config); + let res = Page::parse(Path::new("start.md"), content, &config, &PathBuf::new()); assert!(res.is_ok()); let page = res.unwrap(); assert_eq!(page.path, "hello-world/"); @@ -372,7 +416,12 @@ Hello world"#; +++ Hello world"#; let config = Config::default(); - let res = Page::parse(Path::new("content/posts/intro/start.md"), content, &config); + let res = Page::parse( + Path::new("content/posts/intro/start.md"), + content, + &config, + &PathBuf::new(), + ); assert!(res.is_ok()); let page = res.unwrap(); assert_eq!(page.path, "hello-world/"); @@ -388,7 +437,12 @@ Hello world"#; +++ Hello world"#; let config = Config::default(); - let res = Page::parse(Path::new("content/posts/intro/start.md"), content, &config); + let res = Page::parse( + Path::new("content/posts/intro/start.md"), + content, + &config, + &PathBuf::new(), + ); assert!(res.is_ok()); let page = res.unwrap(); assert_eq!(page.path, "hello-world/"); @@ -404,14 +458,15 @@ Hello world"#; slug = "hello-world" +++ Hello world"#; - let res = Page::parse(Path::new("start.md"), content, &Config::default()); + let res = Page::parse(Path::new("start.md"), content, &Config::default(), &PathBuf::new()); assert!(res.is_err()); } #[test] fn can_make_slug_from_non_slug_filename() { let config = Config::default(); - let res = Page::parse(Path::new(" file with space.md"), "+++\n+++", &config); + let res = + Page::parse(Path::new(" file with space.md"), "+++\n+++", &config, &PathBuf::new()); assert!(res.is_ok()); let page = res.unwrap(); assert_eq!(page.slug, "file-with-space"); @@ -427,7 +482,7 @@ Hello world"#; Hello world "# .to_string(); - let res = Page::parse(Path::new("hello.md"), &content, &config); + let res = Page::parse(Path::new("hello.md"), &content, &config, &PathBuf::new()); assert!(res.is_ok()); let mut page = res.unwrap(); page.render_markdown(&HashMap::default(), &Tera::default(), &config, InsertAnchor::None) @@ -449,7 +504,11 @@ Hello world File::create(nested_path.join("graph.jpg")).unwrap(); File::create(nested_path.join("fail.png")).unwrap(); - let res = Page::from_file(nested_path.join("index.md").as_path(), &Config::default()); + let res = Page::from_file( + nested_path.join("index.md").as_path(), + &Config::default(), + &PathBuf::new(), + ); assert!(res.is_ok()); let page = res.unwrap(); assert_eq!(page.file.parent, path.join("content").join("posts")); @@ -472,7 +531,11 @@ Hello world File::create(nested_path.join("graph.jpg")).unwrap(); File::create(nested_path.join("fail.png")).unwrap(); - let res = Page::from_file(nested_path.join("index.md").as_path(), &Config::default()); + let res = Page::from_file( + nested_path.join("index.md").as_path(), + &Config::default(), + &PathBuf::new(), + ); assert!(res.is_ok()); let page = res.unwrap(); assert_eq!(page.file.parent, path.join("content").join("posts")); @@ -481,6 +544,35 @@ Hello world assert_eq!(page.permalink, "http://a-website.com/posts/hey/"); } + // https://github.com/getzola/zola/issues/607 + #[test] + fn page_with_assets_and_date_in_folder_name() { + let tmp_dir = tempdir().expect("create temp dir"); + let path = tmp_dir.path(); + create_dir(&path.join("content")).expect("create content temp dir"); + create_dir(&path.join("content").join("posts")).expect("create posts temp dir"); + let nested_path = path.join("content").join("posts").join("2013-06-02_with-assets"); + create_dir(&nested_path).expect("create nested temp dir"); + let mut f = File::create(nested_path.join("index.md")).unwrap(); + f.write_all(b"+++\n\n+++\n").unwrap(); + File::create(nested_path.join("example.js")).unwrap(); + File::create(nested_path.join("graph.jpg")).unwrap(); + File::create(nested_path.join("fail.png")).unwrap(); + + let res = Page::from_file( + nested_path.join("index.md").as_path(), + &Config::default(), + &PathBuf::new(), + ); + assert!(res.is_ok()); + let page = res.unwrap(); + assert_eq!(page.file.parent, path.join("content").join("posts")); + assert_eq!(page.slug, "with-assets"); + assert_eq!(page.meta.date, Some("2013-06-02".to_string())); + assert_eq!(page.assets.len(), 3); + assert_eq!(page.permalink, "http://a-website.com/posts/with-assets/"); + } + #[test] fn page_with_ignored_assets_filters_out_correct_files() { let tmp_dir = tempdir().expect("create temp dir"); @@ -500,7 +592,7 @@ Hello world let mut config = Config::default(); config.ignored_content_globset = Some(gsb.build().unwrap()); - let res = Page::from_file(nested_path.join("index.md").as_path(), &config); + let res = Page::from_file(nested_path.join("index.md").as_path(), &config, &PathBuf::new()); assert!(res.is_ok()); let page = res.unwrap(); @@ -517,7 +609,7 @@ Hello world Hello world "# .to_string(); - let res = Page::parse(Path::new("2018-10-08_hello.md"), &content, &config); + let res = Page::parse(Path::new("2018-10-08_hello.md"), &content, &config, &PathBuf::new()); assert!(res.is_ok()); let page = res.unwrap(); @@ -534,7 +626,12 @@ Hello world Hello world "# .to_string(); - let res = Page::parse(Path::new("2018-10-02T15:00:00Z-hello.md"), &content, &config); + let res = Page::parse( + Path::new("2018-10-02T15:00:00Z-hello.md"), + &content, + &config, + &PathBuf::new(), + ); assert!(res.is_ok()); let page = res.unwrap(); @@ -552,11 +649,65 @@ date = 2018-09-09 Hello world "# .to_string(); - let res = Page::parse(Path::new("2018-10-08_hello.md"), &content, &config); + let res = Page::parse(Path::new("2018-10-08_hello.md"), &content, &config, &PathBuf::new()); assert!(res.is_ok()); let page = res.unwrap(); assert_eq!(page.meta.date, Some("2018-09-09".to_string())); assert_eq!(page.slug, "hello"); } + + #[test] + fn can_specify_language_in_filename() { + let mut config = Config::default(); + config.languages.push(Language { code: String::from("fr"), rss: false }); + let content = r#" ++++ ++++ +Bonjour le monde"# + .to_string(); + let res = Page::parse(Path::new("hello.fr.md"), &content, &config, &PathBuf::new()); + assert!(res.is_ok()); + let page = res.unwrap(); + assert_eq!(page.lang, "fr".to_string()); + assert_eq!(page.slug, "hello"); + assert_eq!(page.permalink, "http://a-website.com/fr/hello/"); + } + + #[test] + fn can_specify_language_in_filename_with_date() { + let mut config = Config::default(); + config.languages.push(Language { code: String::from("fr"), rss: false }); + let content = r#" ++++ ++++ +Bonjour le monde"# + .to_string(); + let res = + Page::parse(Path::new("2018-10-08_hello.fr.md"), &content, &config, &PathBuf::new()); + assert!(res.is_ok()); + let page = res.unwrap(); + assert_eq!(page.meta.date, Some("2018-10-08".to_string())); + assert_eq!(page.lang, "fr".to_string()); + assert_eq!(page.slug, "hello"); + assert_eq!(page.permalink, "http://a-website.com/fr/hello/"); + } + + #[test] + fn i18n_frontmatter_path_overrides_default_permalink() { + let mut config = Config::default(); + config.languages.push(Language { code: String::from("fr"), rss: false }); + let content = r#" ++++ +path = "bonjour" ++++ +Bonjour le monde"# + .to_string(); + let res = Page::parse(Path::new("hello.fr.md"), &content, &config, &PathBuf::new()); + assert!(res.is_ok()); + let page = res.unwrap(); + assert_eq!(page.lang, "fr".to_string()); + assert_eq!(page.slug, "hello"); + assert_eq!(page.permalink, "http://a-website.com/bonjour/"); + } } diff --git a/components/library/src/content/section.rs b/components/library/src/content/section.rs index 76f6618..ebc9ca1 100644 --- a/components/library/src/content/section.rs +++ b/components/library/src/content/section.rs @@ -5,7 +5,7 @@ use slotmap::Key; use tera::{Context as TeraContext, Tera}; use config::Config; -use errors::{Result, ResultExt}; +use errors::{Error, Result}; use front_matter::{split_section_content, SectionFrontMatter}; use rendering::{render_content, Header, RenderContext}; use utils::fs::{find_related_assets, read_file}; @@ -51,14 +51,23 @@ pub struct Section { /// How long would it take to read the raw content. /// See `get_reading_analytics` on how it is calculated pub reading_time: Option, + /// The language of that section. Equal to the default lang if the user doesn't setup `languages` in config. + /// Corresponds to the lang in the _index.{lang}.md file scheme + pub lang: String, + /// Contains all the translated version of that section + pub translations: Vec, } impl Section { - pub fn new>(file_path: P, meta: SectionFrontMatter) -> Section { + pub fn new>( + file_path: P, + meta: SectionFrontMatter, + base_path: &PathBuf, + ) -> Section { let file_path = file_path.as_ref(); Section { - file: FileInfo::new_section(file_path), + file: FileInfo::new_section(file_path, base_path), meta, ancestors: vec![], path: "".to_string(), @@ -74,17 +83,30 @@ impl Section { toc: vec![], word_count: None, reading_time: None, + lang: String::new(), + translations: Vec::new(), } } - pub fn parse(file_path: &Path, content: &str, config: &Config) -> Result
{ + pub fn parse( + file_path: &Path, + content: &str, + config: &Config, + base_path: &PathBuf, + ) -> Result
{ let (meta, content) = split_section_content(file_path, content)?; - let mut section = Section::new(file_path, meta); + let mut section = Section::new(file_path, meta, base_path); + section.lang = section.file.find_language(config)?; section.raw_content = content; let (word_count, reading_time) = get_reading_analytics(§ion.raw_content); section.word_count = Some(word_count); section.reading_time = Some(reading_time); - section.path = format!("{}/", section.file.components.join("/")); + let path = section.file.components.join("/"); + if section.lang != config.default_language { + section.path = format!("{}/{}", section.lang, path); + } else { + section.path = format!("{}/", path); + } section.components = section .path .split('/') @@ -96,10 +118,14 @@ impl Section { } /// Read and parse a .md file into a Page struct - pub fn from_file>(path: P, config: &Config) -> Result
{ + pub fn from_file>( + path: P, + config: &Config, + base_path: &PathBuf, + ) -> Result
{ let path = path.as_ref(); let content = read_file(path)?; - let mut section = Section::parse(path, &content, config)?; + let mut section = Section::parse(path, &content, config, base_path)?; let parent_dir = path.parent().unwrap(); let assets = find_related_assets(parent_dir); @@ -158,8 +184,9 @@ impl Section { context.tera_context.insert("section", &SerializingSection::from_section_basic(self, None)); - let res = render_content(&self.raw_content, &context) - .chain_err(|| format!("Failed to render content of {}", self.file.path.display()))?; + let res = render_content(&self.raw_content, &context).map_err(|e| { + Error::chain(format!("Failed to render content of {}", self.file.path.display()), e) + })?; self.content = res.body; self.toc = res.toc; Ok(()) @@ -174,9 +201,12 @@ impl Section { context.insert("current_url", &self.permalink); context.insert("current_path", &self.path); context.insert("section", &self.to_serialized(library)); + context.insert("lang", &self.lang); + context.insert("toc", &self.toc); - render_template(tpl_name, tera, &context, &config.theme) - .chain_err(|| format!("Failed to render section '{}'", self.file.path.display())) + render_template(tpl_name, tera, context, &config.theme).map_err(|e| { + Error::chain(format!("Failed to render section '{}'", self.file.path.display()), e) + }) } /// Is this the index section? @@ -223,6 +253,8 @@ impl Default for Section { toc: vec![], reading_time: None, word_count: None, + lang: String::new(), + translations: Vec::new(), } } } @@ -231,12 +263,13 @@ impl Default for Section { mod tests { use std::fs::{create_dir, File}; use std::io::Write; + use std::path::{Path, PathBuf}; use globset::{Glob, GlobSetBuilder}; use tempfile::tempdir; use super::Section; - use config::Config; + use config::{Config, Language}; #[test] fn section_with_assets_gets_right_info() { @@ -252,7 +285,11 @@ mod tests { File::create(nested_path.join("graph.jpg")).unwrap(); File::create(nested_path.join("fail.png")).unwrap(); - let res = Section::from_file(nested_path.join("_index.md").as_path(), &Config::default()); + let res = Section::from_file( + nested_path.join("_index.md").as_path(), + &Config::default(), + &PathBuf::new(), + ); assert!(res.is_ok()); let section = res.unwrap(); assert_eq!(section.assets.len(), 3); @@ -278,11 +315,51 @@ mod tests { let mut config = Config::default(); config.ignored_content_globset = Some(gsb.build().unwrap()); - let res = Section::from_file(nested_path.join("_index.md").as_path(), &config); + let res = + Section::from_file(nested_path.join("_index.md").as_path(), &config, &PathBuf::new()); assert!(res.is_ok()); let page = res.unwrap(); assert_eq!(page.assets.len(), 1); assert_eq!(page.assets[0].file_name().unwrap().to_str(), Some("graph.jpg")); } + + #[test] + fn can_specify_language_in_filename() { + let mut config = Config::default(); + config.languages.push(Language { code: String::from("fr"), rss: false }); + let content = r#" ++++ ++++ +Bonjour le monde"# + .to_string(); + let res = Section::parse( + Path::new("content/hello/nested/_index.fr.md"), + &content, + &config, + &PathBuf::new(), + ); + assert!(res.is_ok()); + let section = res.unwrap(); + assert_eq!(section.lang, "fr".to_string()); + assert_eq!(section.permalink, "http://a-website.com/fr/hello/nested/"); + } + + // https://zola.discourse.group/t/rfc-i18n/13/17?u=keats + #[test] + fn can_make_links_to_translated_sections_without_double_trailing_slash() { + let mut config = Config::default(); + config.languages.push(Language { code: String::from("fr"), rss: false }); + let content = r#" ++++ ++++ +Bonjour le monde"# + .to_string(); + let res = + Section::parse(Path::new("content/_index.fr.md"), &content, &config, &PathBuf::new()); + assert!(res.is_ok()); + let section = res.unwrap(); + assert_eq!(section.lang, "fr".to_string()); + assert_eq!(section.permalink, "http://a-website.com/fr/"); + } } diff --git a/components/library/src/content/ser.rs b/components/library/src/content/ser.rs index 6fbe51b..f530484 100644 --- a/components/library/src/content/ser.rs +++ b/components/library/src/content/ser.rs @@ -5,7 +5,46 @@ use tera::{Map, Value}; use content::{Page, Section}; use library::Library; -use rendering::Header; + +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct TranslatedContent<'a> { + lang: &'a str, + permalink: &'a str, + title: &'a Option, +} + +impl<'a> TranslatedContent<'a> { + // copypaste eh, not worth creating an enum imo + pub fn find_all_sections(section: &'a Section, library: &'a Library) -> Vec { + let mut translations = vec![]; + + for key in §ion.translations { + let other = library.get_section_by_key(*key); + translations.push(TranslatedContent { + lang: &other.lang, + permalink: &other.permalink, + title: &other.meta.title, + }); + } + + translations + } + + pub fn find_all_pages(page: &'a Page, library: &'a Library) -> Vec { + let mut translations = vec![]; + + for key in &page.translations { + let other = library.get_page_by_key(*key); + translations.push(TranslatedContent { + lang: &other.lang, + permalink: &other.permalink, + title: &other.meta.title, + }); + } + + translations + } +} #[derive(Clone, Debug, PartialEq, Serialize)] pub struct SerializingPage<'a> { @@ -27,13 +66,14 @@ pub struct SerializingPage<'a> { summary: &'a Option, word_count: Option, reading_time: Option, - toc: &'a [Header], assets: &'a [String], draft: bool, + lang: &'a str, lighter: Option>>, heavier: Option>>, earlier: Option>>, later: Option>>, + translations: Vec>, } impl<'a> SerializingPage<'a> { @@ -66,6 +106,8 @@ impl<'a> SerializingPage<'a> { .map(|k| library.get_section_by_key(*k).file.relative.clone()) .collect(); + let translations = TranslatedContent::find_all_pages(page, library); + SerializingPage { relative_path: &page.file.relative, ancestors, @@ -85,13 +127,14 @@ impl<'a> SerializingPage<'a> { summary: &page.summary, word_count: page.word_count, reading_time: page.reading_time, - toc: &page.toc, assets: &page.serialized_assets, draft: page.is_draft(), + lang: &page.lang, lighter, heavier, earlier, later, + translations, } } @@ -114,6 +157,12 @@ impl<'a> SerializingPage<'a> { vec![] }; + let translations = if let Some(ref lib) = library { + TranslatedContent::find_all_pages(page, lib) + } else { + vec![] + }; + SerializingPage { relative_path: &page.file.relative, ancestors, @@ -133,13 +182,14 @@ impl<'a> SerializingPage<'a> { summary: &page.summary, word_count: page.word_count, reading_time: page.reading_time, - toc: &page.toc, assets: &page.serialized_assets, draft: page.is_draft(), + lang: &page.lang, lighter: None, heavier: None, earlier: None, later: None, + translations, } } } @@ -157,10 +207,11 @@ pub struct SerializingSection<'a> { components: &'a [String], word_count: Option, reading_time: Option, - toc: &'a [Header], + lang: &'a str, assets: &'a [String], pages: Vec>, subsections: Vec<&'a str>, + translations: Vec>, } impl<'a> SerializingSection<'a> { @@ -169,7 +220,7 @@ impl<'a> SerializingSection<'a> { let mut subsections = Vec::with_capacity(section.subsections.len()); for k in §ion.pages { - pages.push(library.get_page_by_key(*k).to_serialized(library)); + pages.push(library.get_page_by_key(*k).to_serialized_basic(library)); } for k in §ion.subsections { @@ -181,6 +232,7 @@ impl<'a> SerializingSection<'a> { .iter() .map(|k| library.get_section_by_key(*k).file.relative.clone()) .collect(); + let translations = TranslatedContent::find_all_sections(section, library); SerializingSection { relative_path: §ion.file.relative, @@ -194,10 +246,11 @@ impl<'a> SerializingSection<'a> { components: §ion.components, word_count: section.word_count, reading_time: section.reading_time, - toc: §ion.toc, assets: §ion.serialized_assets, + lang: §ion.lang, pages, subsections, + translations, } } @@ -213,6 +266,12 @@ impl<'a> SerializingSection<'a> { vec![] }; + let translations = if let Some(ref lib) = library { + TranslatedContent::find_all_sections(section, lib) + } else { + vec![] + }; + SerializingSection { relative_path: §ion.file.relative, ancestors, @@ -225,10 +284,11 @@ impl<'a> SerializingSection<'a> { components: §ion.components, word_count: section.word_count, reading_time: section.reading_time, - toc: §ion.toc, assets: §ion.serialized_assets, + lang: §ion.lang, pages: vec![], subsections: vec![], + translations, } } } diff --git a/components/library/src/library.rs b/components/library/src/library.rs index 05971f8..c724a2e 100644 --- a/components/library/src/library.rs +++ b/components/library/src/library.rs @@ -5,6 +5,7 @@ use slotmap::{DenseSlotMap, Key}; use front_matter::SortBy; +use config::Config; use content::{Page, Section}; use sorting::{find_siblings, sort_pages_by_date, sort_pages_by_weight}; @@ -22,18 +23,21 @@ pub struct Library { /// All the sections of the site sections: DenseSlotMap
, /// A mapping path -> key for pages so we can easily get their key - paths_to_pages: HashMap, + pub paths_to_pages: HashMap, /// A mapping path -> key for sections so we can easily get their key pub paths_to_sections: HashMap, + /// Whether we need to look for translations + is_multilingual: bool, } impl Library { - pub fn new(cap_pages: usize, cap_sections: usize) -> Self { + pub fn new(cap_pages: usize, cap_sections: usize, is_multilingual: bool) -> Self { Library { pages: DenseSlotMap::with_capacity(cap_pages), sections: DenseSlotMap::with_capacity(cap_sections), paths_to_pages: HashMap::with_capacity(cap_pages), paths_to_sections: HashMap::with_capacity(cap_sections), + is_multilingual, } } @@ -79,15 +83,9 @@ impl Library { /// Find out the direct subsections of each subsection if there are some /// as well as the pages for each section - pub fn populate_sections(&mut self) { - let (root_path, index_path) = self - .sections - .values() - .find(|s| s.is_index()) - .map(|s| (s.file.parent.clone(), s.file.path.clone())) - .unwrap(); - let root_key = self.paths_to_sections[&index_path]; - + pub fn populate_sections(&mut self, config: &Config) { + let root_path = + self.sections.values().find(|s| s.is_index()).map(|s| s.file.parent.clone()).unwrap(); // We are going to get both the ancestors and grandparents for each section in one go let mut ancestors: HashMap> = HashMap::new(); let mut subsections: HashMap> = HashMap::new(); @@ -99,7 +97,8 @@ impl Library { if let Some(ref grand_parent) = section.file.grand_parent { subsections - .entry(grand_parent.join("_index.md")) + // Using the original filename to work for multi-lingual sections + .entry(grand_parent.join(§ion.file.filename)) .or_insert_with(|| vec![]) .push(section.file.path.clone()); } @@ -111,6 +110,7 @@ impl Library { } let mut path = root_path.clone(); + let root_key = self.paths_to_sections[&root_path.join(§ion.file.filename)]; // Index section is the first ancestor of every single section let mut parents = vec![root_key]; for component in §ion.file.components { @@ -119,7 +119,9 @@ impl Library { if path == section.file.parent { continue; } - if let Some(section_key) = self.paths_to_sections.get(&path.join("_index.md")) { + if let Some(section_key) = + self.paths_to_sections.get(&path.join(§ion.file.filename)) + { parents.push(*section_key); } } @@ -127,7 +129,12 @@ impl Library { } for (key, page) in &mut self.pages { - let mut parent_section_path = page.file.parent.join("_index.md"); + let parent_filename = if page.lang != config.default_language { + format!("_index.{}.md", page.lang) + } else { + "_index.md".to_string() + }; + let mut parent_section_path = page.file.parent.join(&parent_filename); while let Some(section_key) = self.paths_to_sections.get(&parent_section_path) { let parent_is_transparent; // We need to get a reference to a section later so keep the scope of borrowing small @@ -158,14 +165,15 @@ impl Library { break; } - // We've added `_index.md` so if we are here so we need to go up twice + // We've added `_index(.{LANG})?.md` so if we are here so we need to go up twice match parent_section_path.clone().parent().unwrap().parent() { - Some(parent) => parent_section_path = parent.join("_index.md"), + Some(parent) => parent_section_path = parent.join(&parent_filename), None => break, } } } + self.populate_translations(); self.sort_sections_pages(); let sections = self.paths_to_sections.clone(); @@ -185,7 +193,8 @@ impl Library { } } - /// Sort all sections pages + /// Sort all sections pages according to sorting method given + /// Pages that cannot be sorted are set to the section.ignored_pages instead pub fn sort_sections_pages(&mut self) { let mut updates = HashMap::new(); for (key, section) in &self.sections { @@ -265,6 +274,51 @@ impl Library { } } + /// Finds all the translations for each section/page and set the `translations` + /// field of each as needed + /// A no-op for sites without multiple languages + fn populate_translations(&mut self) { + if !self.is_multilingual { + return; + } + + // Sections first + let mut sections_translations = HashMap::new(); + for (key, section) in &self.sections { + sections_translations + .entry(section.file.canonical.clone()) // TODO: avoid this clone + .or_insert_with(Vec::new) + .push(key); + } + + for (key, section) in self.sections.iter_mut() { + let translations = §ions_translations[§ion.file.canonical]; + if translations.len() == 1 { + section.translations = vec![]; + continue; + } + section.translations = translations.iter().filter(|k| **k != key).cloned().collect(); + } + + // Same thing for pages + let mut pages_translations = HashMap::new(); + for (key, page) in &self.pages { + pages_translations + .entry(page.file.canonical.clone()) // TODO: avoid this clone + .or_insert_with(Vec::new) + .push(key); + } + + for (key, page) in self.pages.iter_mut() { + let translations = &pages_translations[&page.file.canonical]; + if translations.len() == 1 { + page.translations = vec![]; + continue; + } + page.translations = translations.iter().filter(|k| **k != key).cloned().collect(); + } + } + /// Find all the orphan pages: pages that are in a folder without an `_index.md` pub fn get_all_orphan_pages(&self) -> Vec<&Page> { let pages_in_sections = @@ -277,15 +331,19 @@ impl Library { .collect() } - pub fn find_parent_section>(&self, path: P) -> Option<&Section> { - let page_key = self.paths_to_pages[path.as_ref()]; - for s in self.sections.values() { - if s.pages.contains(&page_key) { - return Some(s); + /// Find the parent section & all grandparents section that have transparent=true + /// Only used in rebuild. + pub fn find_parent_sections>(&self, path: P) -> Vec<&Section> { + let mut parents = vec![]; + let page = self.get_page(path.as_ref()).unwrap(); + for ancestor in page.ancestors.iter().rev() { + let section = self.get_section_by_key(*ancestor); + if parents.is_empty() || section.meta.transparent { + parents.push(section); } } - None + parents } /// Only used in tests diff --git a/components/library/src/pagination/mod.rs b/components/library/src/pagination/mod.rs index 9566189..2fc9203 100644 --- a/components/library/src/pagination/mod.rs +++ b/components/library/src/pagination/mod.rs @@ -4,7 +4,7 @@ use slotmap::Key; use tera::{to_value, Context, Tera, Value}; use config::Config; -use errors::{Result, ResultExt}; +use errors::{Error, Result}; use utils::templates::render_template; use content::{Section, SerializingPage, SerializingSection}; @@ -221,13 +221,14 @@ impl<'a> Paginator<'a> { context.insert("current_path", &pager.path); context.insert("paginator", &self.build_paginator_context(pager)); - render_template(&self.template, tera, &context, &config.theme) - .chain_err(|| format!("Failed to render pager {}", pager.index)) + render_template(&self.template, tera, context, &config.theme) + .map_err(|e| Error::chain(format!("Failed to render pager {}", pager.index), e)) } } #[cfg(test)] mod tests { + use std::path::PathBuf; use tera::to_value; use config::Taxonomy as TaxonomyConfig; @@ -242,7 +243,7 @@ mod tests { let mut f = SectionFrontMatter::default(); f.paginate_by = Some(2); f.paginate_path = "page".to_string(); - let mut s = Section::new("content/_index.md", f); + let mut s = Section::new("content/_index.md", f, &PathBuf::new()); if !is_index { s.path = "posts/".to_string(); s.permalink = "https://vincent.is/posts/".to_string(); @@ -254,7 +255,7 @@ mod tests { } fn create_library(is_index: bool) -> (Section, Library) { - let mut library = Library::new(3, 0); + let mut library = Library::new(3, 0, false); library.insert_page(Page::default()); library.insert_page(Page::default()); library.insert_page(Page::default()); diff --git a/components/library/src/sorting.rs b/components/library/src/sorting.rs index 20844d8..c6cfd67 100644 --- a/components/library/src/sorting.rs +++ b/components/library/src/sorting.rs @@ -113,6 +113,7 @@ pub fn find_siblings(sorted: Vec<(&Key, bool)>) -> Vec<(Key, Option, Option #[cfg(test)] mod tests { use slotmap::DenseSlotMap; + use std::path::PathBuf; use super::{find_siblings, sort_pages_by_date, sort_pages_by_weight}; use content::Page; @@ -122,13 +123,13 @@ mod tests { let mut front_matter = PageFrontMatter::default(); front_matter.date = Some(date.to_string()); front_matter.date_to_datetime(); - Page::new("content/hello.md", front_matter) + Page::new("content/hello.md", front_matter, &PathBuf::new()) } fn create_page_with_weight(weight: usize) -> Page { let mut front_matter = PageFrontMatter::default(); front_matter.weight = Some(weight); - Page::new("content/hello.md", front_matter) + Page::new("content/hello.md", front_matter, &PathBuf::new()) } #[test] diff --git a/components/library/src/taxonomies/mod.rs b/components/library/src/taxonomies/mod.rs index 6bba13c..a82c3e5 100644 --- a/components/library/src/taxonomies/mod.rs +++ b/components/library/src/taxonomies/mod.rs @@ -5,7 +5,7 @@ use slug::slugify; use tera::{Context, Tera}; use config::{Config, Taxonomy as TaxonomyConfig}; -use errors::{Result, ResultExt}; +use errors::{Error, Result}; use utils::templates::render_template; use content::SerializingPage; @@ -48,7 +48,13 @@ pub struct TaxonomyItem { } impl TaxonomyItem { - pub fn new(name: &str, path: &str, config: &Config, keys: Vec, library: &Library) -> Self { + pub fn new( + name: &str, + taxonomy: &TaxonomyConfig, + config: &Config, + keys: Vec, + library: &Library, + ) -> Self { // Taxonomy are almost always used for blogs so we filter by dates // and it's not like we can sort things across sections by anything other // than dates @@ -64,7 +70,11 @@ impl TaxonomyItem { .collect(); let (mut pages, ignored_pages) = sort_pages_by_date(data); let slug = slugify(name); - let permalink = config.make_permalink(&format!("/{}/{}", path, slug)); + let permalink = if taxonomy.lang != config.default_language { + config.make_permalink(&format!("/{}/{}/{}", taxonomy.lang, taxonomy.name, slug)) + } else { + config.make_permalink(&format!("/{}/{}", taxonomy.name, slug)) + }; // We still append pages without dates at the end pages.extend(ignored_pages); @@ -108,7 +118,7 @@ impl Taxonomy { ) -> Taxonomy { let mut sorted_items = vec![]; for (name, pages) in items { - sorted_items.push(TaxonomyItem::new(&name, &kind.name, config, pages, library)); + sorted_items.push(TaxonomyItem::new(&name, &kind, config, pages, library)); } sorted_items.sort_by(|a, b| a.name.cmp(&b.name)); @@ -140,8 +150,10 @@ impl Taxonomy { ); context.insert("current_path", &format!("/{}/{}", self.kind.name, item.slug)); - render_template(&format!("{}/single.html", self.kind.name), tera, &context, &config.theme) - .chain_err(|| format!("Failed to render single term {} page.", self.kind.name)) + render_template(&format!("{}/single.html", self.kind.name), tera, context, &config.theme) + .map_err(|e| { + Error::chain(format!("Failed to render single term {} page.", self.kind.name), e) + }) } pub fn render_all_terms( @@ -159,8 +171,10 @@ impl Taxonomy { context.insert("current_url", &config.make_permalink(&self.kind.name)); context.insert("current_path", &self.kind.name); - render_template(&format!("{}/list.html", self.kind.name), tera, &context, &config.theme) - .chain_err(|| format!("Failed to render a list of {} page.", self.kind.name)) + render_template(&format!("{}/list.html", self.kind.name), tera, context, &config.theme) + .map_err(|e| { + Error::chain(format!("Failed to render a list of {} page.", self.kind.name), e) + }) } pub fn to_serialized<'a>(&'a self, library: &'a Library) -> SerializedTaxonomy<'a> { @@ -186,6 +200,14 @@ pub fn find_taxonomies(config: &Config, library: &Library) -> Result t = Some(x), + "categories" => c = Some(x), + "auteurs" => a = Some(x), + _ => unreachable!(), + } + } + (t.unwrap(), c.unwrap(), a.unwrap()) + }; + + assert_eq!(tags.items.len(), 2); + assert_eq!(categories.items.len(), 2); + assert_eq!(authors.items.len(), 1); + + assert_eq!(tags.items[0].name, "db"); + assert_eq!(tags.items[0].slug, "db"); + assert_eq!(tags.items[0].permalink, "http://a-website.com/tags/db/"); + assert_eq!(tags.items[0].pages.len(), 1); + + assert_eq!(tags.items[1].name, "rust"); + assert_eq!(tags.items[1].slug, "rust"); + assert_eq!(tags.items[1].permalink, "http://a-website.com/tags/rust/"); + assert_eq!(tags.items[1].pages.len(), 2); + + assert_eq!(authors.items[0].name, "Vincent Prouillet"); + assert_eq!(authors.items[0].slug, "vincent-prouillet"); + assert_eq!( + authors.items[0].permalink, + "http://a-website.com/fr/auteurs/vincent-prouillet/" + ); + assert_eq!(authors.items[0].pages.len(), 1); + + assert_eq!(categories.items[0].name, "Other"); + assert_eq!(categories.items[0].slug, "other"); + assert_eq!(categories.items[0].permalink, "http://a-website.com/categories/other/"); + assert_eq!(categories.items[0].pages.len(), 1); + + assert_eq!(categories.items[1].name, "Programming tutorials"); + assert_eq!(categories.items[1].slug, "programming-tutorials"); + assert_eq!( + categories.items[1].permalink, + "http://a-website.com/categories/programming-tutorials/" + ); + assert_eq!(categories.items[1].pages.len(), 1); + } + + #[test] + fn errors_on_taxonomy_of_different_language() { + let mut config = Config::default(); + config.languages.push(Language { rss: false, code: "fr".to_string() }); + let mut library = Library::new(2, 0, false); + + config.taxonomies = + vec![TaxonomyConfig { name: "tags".to_string(), ..TaxonomyConfig::default() }]; + + let mut page1 = Page::default(); + page1.lang = "fr".to_string(); + let mut taxo_page1 = HashMap::new(); + taxo_page1.insert("tags".to_string(), vec!["rust".to_string(), "db".to_string()]); + page1.meta.taxonomies = taxo_page1; + library.insert_page(page1); + + let taxonomies = find_taxonomies(&config, &library); + assert!(taxonomies.is_err()); + let err = taxonomies.unwrap_err(); + // no path as this is created by Default + assert_eq!( + format!("{}", err), + "Page `` has taxonomy `tags` which is not available in that language" + ); + } } diff --git a/components/rebuild/src/lib.rs b/components/rebuild/src/lib.rs index ddc9668..41b4fc4 100644 --- a/components/rebuild/src/lib.rs +++ b/components/rebuild/src/lib.rs @@ -98,25 +98,27 @@ fn find_page_front_matter_changes( /// Handles a path deletion: could be a page, a section, a folder fn delete_element(site: &mut Site, path: &Path, is_section: bool) -> Result<()> { - // Ignore the event if this path was not known - if !site.library.contains_section(&path.to_path_buf()) - && !site.library.contains_page(&path.to_path_buf()) { - return Ok(()); - } - - if is_section { - if let Some(s) = site.library.remove_section(&path.to_path_buf()) { - site.permalinks.remove(&s.file.relative); + let mut library = site.library.write().unwrap(); + // Ignore the event if this path was not known + if !library.contains_section(&path.to_path_buf()) + && !library.contains_page(&path.to_path_buf()) + { + return Ok(()); } - } else if let Some(p) = site.library.remove_page(&path.to_path_buf()) { - site.permalinks.remove(&p.file.relative); - if !p.meta.taxonomies.is_empty() { - site.populate_taxonomies()?; + if is_section { + if let Some(s) = library.remove_section(&path.to_path_buf()) { + site.permalinks.remove(&s.file.relative); + } + } else if let Some(p) = library.remove_page(&path.to_path_buf()) { + site.permalinks.remove(&p.file.relative); } } + // We might have delete the root _index.md so ensure we have at least the default one + // before populating + site.create_default_index_sections()?; site.populate_sections(); site.populate_taxonomies()?; // Ensure we have our fn updated so it doesn't contain the permalink(s)/section/page deleted @@ -129,35 +131,41 @@ fn delete_element(site: &mut Site, path: &Path, is_section: bool) -> Result<()> /// Handles a `_index.md` (a section) being edited in some ways fn handle_section_editing(site: &mut Site, path: &Path) -> Result<()> { - let section = Section::from_file(path, &site.config)?; + let section = Section::from_file(path, &site.config, &site.base_path)?; let pathbuf = path.to_path_buf(); match site.add_section(section, true)? { // Updating a section Some(prev) => { site.populate_sections(); + { + let library = site.library.read().unwrap(); - if site.library.get_section(&pathbuf).unwrap().meta == prev.meta { - // Front matter didn't change, only content did - // so we render only the section page, not its pages - return site.render_section(&site.library.get_section(&pathbuf).unwrap(), false); + if library.get_section(&pathbuf).unwrap().meta == prev.meta { + // Front matter didn't change, only content did + // so we render only the section page, not its pages + return site.render_section(&library.get_section(&pathbuf).unwrap(), false); + } } // Front matter changed - for changes in find_section_front_matter_changes( - &site.library.get_section(&pathbuf).unwrap().meta, + let changes = find_section_front_matter_changes( + &site.library.read().unwrap().get_section(&pathbuf).unwrap().meta, &prev.meta, - ) { + ); + for change in changes { // Sort always comes first if present so the rendering will be fine - match changes { + match change { SectionChangesNeeded::Sort => { site.register_tera_global_fns(); } - SectionChangesNeeded::Render => { - site.render_section(&site.library.get_section(&pathbuf).unwrap(), false)? - } - SectionChangesNeeded::RenderWithPages => { - site.render_section(&site.library.get_section(&pathbuf).unwrap(), true)? - } + SectionChangesNeeded::Render => site.render_section( + &site.library.read().unwrap().get_section(&pathbuf).unwrap(), + false, + )?, + SectionChangesNeeded::RenderWithPages => site.render_section( + &site.library.read().unwrap().get_section(&pathbuf).unwrap(), + true, + )?, // not a common enough operation to make it worth optimizing SectionChangesNeeded::Delete | SectionChangesNeeded::Transparent => { site.build()?; @@ -170,49 +178,54 @@ fn handle_section_editing(site: &mut Site, path: &Path) -> Result<()> { None => { site.populate_sections(); site.register_tera_global_fns(); - site.render_section(&site.library.get_section(&pathbuf).unwrap(), true) + site.render_section(&site.library.read().unwrap().get_section(&pathbuf).unwrap(), true) } } } -macro_rules! render_parent_section { +macro_rules! render_parent_sections { ($site: expr, $path: expr) => { - if let Some(s) = $site.library.find_parent_section($path) { + for s in $site.library.read().unwrap().find_parent_sections($path) { $site.render_section(s, false)?; - }; + } }; } /// Handles a page being edited in some ways fn handle_page_editing(site: &mut Site, path: &Path) -> Result<()> { - let page = Page::from_file(path, &site.config)?; + let page = Page::from_file(path, &site.config, &site.base_path)?; let pathbuf = path.to_path_buf(); match site.add_page(page, true)? { // Updating a page Some(prev) => { site.populate_sections(); site.populate_taxonomies()?; + site.register_tera_global_fns(); + { + let library = site.library.read().unwrap(); - // Front matter didn't change, only content did - if site.library.get_page(&pathbuf).unwrap().meta == prev.meta { - // Other than the page itself, the summary might be seen - // on a paginated list for a blog for example - if site.library.get_page(&pathbuf).unwrap().summary.is_some() { - render_parent_section!(site, path); + // Front matter didn't change, only content did + if library.get_page(&pathbuf).unwrap().meta == prev.meta { + // Other than the page itself, the summary might be seen + // on a paginated list for a blog for example + if library.get_page(&pathbuf).unwrap().summary.is_some() { + render_parent_sections!(site, path); + } + return site.render_page(&library.get_page(&pathbuf).unwrap()); } - site.register_tera_global_fns(); - return site.render_page(&site.library.get_page(&pathbuf).unwrap()); } // Front matter changed - for changes in find_page_front_matter_changes( - &site.library.get_page(&pathbuf).unwrap().meta, + let changes = find_page_front_matter_changes( + &site.library.read().unwrap().get_page(&pathbuf).unwrap().meta, &prev.meta, - ) { + ); + + for change in changes { site.register_tera_global_fns(); // Sort always comes first if present so the rendering will be fine - match changes { + match change { PageChangesNeeded::Taxonomies => { site.populate_taxonomies()?; site.render_taxonomies()?; @@ -221,8 +234,10 @@ fn handle_page_editing(site: &mut Site, path: &Path) -> Result<()> { site.render_index()?; } PageChangesNeeded::Render => { - render_parent_section!(site, path); - site.render_page(&site.library.get_page(&path.to_path_buf()).unwrap())?; + render_parent_sections!(site, path); + site.render_page( + &site.library.read().unwrap().get_page(&path.to_path_buf()).unwrap(), + )?; } }; } @@ -275,8 +290,11 @@ pub fn after_content_rename(site: &mut Site, old: &Path, new: &Path) -> Result<( if new_path.file_name().unwrap() == "_index.md" { // We aren't entirely sure where the original thing was so just try to delete whatever was // at the old path - site.library.remove_page(&old.to_path_buf()); - site.library.remove_section(&old.to_path_buf()); + { + let mut library = site.library.write().unwrap(); + library.remove_page(&old.to_path_buf()); + library.remove_section(&old.to_path_buf()); + } return handle_section_editing(site, &new_path); } @@ -287,8 +305,8 @@ pub fn after_content_rename(site: &mut Site, old: &Path, new: &Path) -> Result<( } else { old.to_path_buf() }; - site.library.remove_page(&old_path); - return handle_page_editing(site, &new_path); + site.library.write().unwrap().remove_page(&old_path); + handle_page_editing(site, &new_path) } /// What happens when a section or a page is created/edited @@ -297,9 +315,15 @@ pub fn after_content_change(site: &mut Site, path: &Path) -> Result<()> { let is_md = path.extension().unwrap() == "md"; let index = path.parent().unwrap().join("index.md"); + let mut potential_indices = vec![path.parent().unwrap().join("index.md")]; + for language in &site.config.languages { + potential_indices.push(path.parent().unwrap().join(format!("index.{}.md", language.code))); + } + let colocated_index = potential_indices.contains(&path.to_path_buf()); + // A few situations can happen: // 1. Change on .md files - // a. Is there an `index.md`? Return an error if it's something other than delete + // a. Is there already an `index.md`? Return an error if it's something other than delete // b. Deleted? remove the element // c. Edited? // 1. filename is `_index.md`, this is a section @@ -315,9 +339,9 @@ pub fn after_content_change(site: &mut Site, path: &Path) -> Result<()> { } // Added another .md in a assets directory - if index.exists() && path.exists() && path != index { + if index.exists() && path.exists() && !colocated_index { bail!( - "Change on {:?} detected but there is already an `index.md` in the same folder", + "Change on {:?} detected but only files named `index.md` with an optional language code are allowed", path.display() ); } else if index.exists() && !path.exists() { @@ -344,7 +368,8 @@ pub fn after_template_change(site: &mut Site, path: &Path) -> Result<()> { match filename { "sitemap.xml" => site.render_sitemap(), - "rss.xml" => site.render_rss_feed(site.library.pages_values(), None), + "rss.xml" => site.render_rss_feed(site.library.read().unwrap().pages_values(), None), + "split_sitemap_index.xml" => site.render_sitemap(), "robots.txt" => site.render_robots(), "single.html" | "list.html" => site.render_taxonomies(), "page.html" => { diff --git a/components/rebuild/tests/rebuild.rs b/components/rebuild/tests/rebuild.rs index b5a96ea..f35c0c2 100644 --- a/components/rebuild/tests/rebuild.rs +++ b/components/rebuild/tests/rebuild.rs @@ -16,15 +16,15 @@ use rebuild::{after_content_change, after_content_rename}; // Loads the test_site in a tempdir and build it there // Returns (site_path_in_tempdir, site) macro_rules! load_and_build_site { - ($tmp_dir: expr) => {{ + ($tmp_dir: expr, $site: expr) => {{ let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf(); - path.push("test_site"); + path.push($site); let mut options = dir::CopyOptions::new(); options.copy_inside = true; dir::copy(&path, &$tmp_dir, &options).unwrap(); - let site_path = $tmp_dir.path().join("test_site"); + let site_path = $tmp_dir.path().join($site); let mut site = Site::new(&site_path, "config.toml").unwrap(); site.load().unwrap(); let public = &site_path.join("public"); @@ -81,7 +81,7 @@ macro_rules! rename { #[test] fn can_rebuild_after_simple_change_to_page_content() { let tmp_dir = tempdir().expect("create temp dir"); - let (site_path, mut site) = load_and_build_site!(tmp_dir); + let (site_path, mut site) = load_and_build_site!(tmp_dir, "test_site"); let file_path = edit_file!( site_path, "content/rebuild/first.md", @@ -103,7 +103,7 @@ Some content"# #[test] fn can_rebuild_after_title_change_page_global_func_usage() { let tmp_dir = tempdir().expect("create temp dir"); - let (site_path, mut site) = load_and_build_site!(tmp_dir); + let (site_path, mut site) = load_and_build_site!(tmp_dir, "test_site"); let file_path = edit_file!( site_path, "content/rebuild/first.md", @@ -125,7 +125,7 @@ date = 2017-01-01 #[test] fn can_rebuild_after_sort_change_in_section() { let tmp_dir = tempdir().expect("create temp dir"); - let (site_path, mut site) = load_and_build_site!(tmp_dir); + let (site_path, mut site) = load_and_build_site!(tmp_dir, "test_site"); let file_path = edit_file!( site_path, "content/rebuild/_index.md", @@ -150,7 +150,7 @@ template = "rebuild.html" #[test] fn can_rebuild_after_transparent_change() { let tmp_dir = tempdir().expect("create temp dir"); - let (site_path, mut site) = load_and_build_site!(tmp_dir); + let (site_path, mut site) = load_and_build_site!(tmp_dir, "test_site"); let file_path = edit_file!( site_path, "content/posts/2018/_index.md", @@ -182,7 +182,7 @@ insert_anchor_links = "left" #[test] fn can_rebuild_after_renaming_page() { let tmp_dir = tempdir().expect("create temp dir"); - let (site_path, mut site) = load_and_build_site!(tmp_dir); + let (site_path, mut site) = load_and_build_site!(tmp_dir, "test_site"); let (old_path, new_path) = rename!(site_path, "content/posts/simple.md", "hard.md"); let res = after_content_rename(&mut site, &old_path, &new_path); @@ -195,7 +195,7 @@ fn can_rebuild_after_renaming_page() { #[test] fn can_rebuild_after_renaming_colocated_asset_folder() { let tmp_dir = tempdir().expect("create temp dir"); - let (site_path, mut site) = load_and_build_site!(tmp_dir); + let (site_path, mut site) = load_and_build_site!(tmp_dir, "test_site"); let (old_path, new_path) = rename!(site_path, "content/posts/with-assets", "with-assets-updated"); assert!(file_contains!(site_path, "content/posts/with-assets-updated/index.md", "Hello")); @@ -214,7 +214,7 @@ fn can_rebuild_after_renaming_colocated_asset_folder() { #[test] fn can_rebuild_after_renaming_section_folder() { let tmp_dir = tempdir().expect("create temp dir"); - let (site_path, mut site) = load_and_build_site!(tmp_dir); + let (site_path, mut site) = load_and_build_site!(tmp_dir, "test_site"); let (old_path, new_path) = rename!(site_path, "content/posts", "new-posts"); assert!(file_contains!(site_path, "content/new-posts/simple.md", "simple")); @@ -227,7 +227,7 @@ fn can_rebuild_after_renaming_section_folder() { #[test] fn can_rebuild_after_renaming_non_md_asset_in_colocated_folder() { let tmp_dir = tempdir().expect("create temp dir"); - let (site_path, mut site) = load_and_build_site!(tmp_dir); + let (site_path, mut site) = load_and_build_site!(tmp_dir, "test_site"); let (old_path, new_path) = rename!(site_path, "content/posts/with-assets/zola.png", "gutenberg.png"); @@ -239,7 +239,7 @@ fn can_rebuild_after_renaming_non_md_asset_in_colocated_folder() { #[test] fn can_rebuild_after_deleting_file() { let tmp_dir = tempdir().expect("create temp dir"); - let (site_path, mut site) = load_and_build_site!(tmp_dir); + let (site_path, mut site) = load_and_build_site!(tmp_dir, "test_site"); let path = site_path.join("content").join("posts").join("fixed-slug.md"); fs::remove_file(&path).unwrap(); @@ -247,3 +247,42 @@ fn can_rebuild_after_deleting_file() { println!("{:?}", res); assert!(res.is_ok()); } + +#[test] +fn can_rebuild_after_editing_in_colocated_asset_folder_with_language() { + let tmp_dir = tempdir().expect("create temp dir"); + let (site_path, mut site) = load_and_build_site!(tmp_dir, "test_site_i18n"); + let file_path = edit_file!( + site_path, + "content/blog/with-assets/index.fr.md", + br#" ++++ +date = 2018-11-11 ++++ + +Edite +"# + ); + + let res = after_content_change(&mut site, &file_path); + println!("{:?}", res); + assert!(res.is_ok()); + assert!(file_contains!(site_path, "public/fr/blog/with-assets/index.html", "Edite")); +} + +// https://github.com/getzola/zola/issues/620 +#[test] +fn can_rebuild_after_renaming_section_and_deleting_file() { + let tmp_dir = tempdir().expect("create temp dir"); + let (site_path, mut site) = load_and_build_site!(tmp_dir, "test_site"); + let (old_path, new_path) = rename!(site_path, "content/posts/", "post/"); + let res = after_content_rename(&mut site, &old_path, &new_path); + assert!(res.is_ok()); + + let path = site_path.join("content").join("_index.md"); + fs::remove_file(&path).unwrap(); + + let res = after_content_change(&mut site, &path); + println!("{:?}", res); + assert!(res.is_ok()); +} diff --git a/components/rendering/Cargo.toml b/components/rendering/Cargo.toml index 21815e1..a4a6408 100644 --- a/components/rendering/Cargo.toml +++ b/components/rendering/Cargo.toml @@ -4,9 +4,9 @@ version = "0.1.0" authors = ["Vincent Prouillet "] [dependencies] -tera = { version = "0.11", features = ["preserve_order"] } +tera = { version = "1.0.0-alpha.3", features = ["preserve_order"] } syntect = "3" -pulldown-cmark = "0.2" +pulldown-cmark = "0.4" slug = "0.1" serde = "1" serde_derive = "1" diff --git a/components/rendering/src/markdown.rs b/components/rendering/src/markdown.rs index c21e58b..6c34a58 100644 --- a/components/rendering/src/markdown.rs +++ b/components/rendering/src/markdown.rs @@ -1,6 +1,3 @@ -use std::borrow::Cow::{Borrowed, Owned}; - -use self::cmark::{Event, Options, Parser, Tag}; use pulldown_cmark as cmark; use slug::slugify; use syntect::easy::HighlightLines; @@ -9,14 +6,19 @@ use syntect::html::{ }; use config::highlighting::{get_highlighter, SYNTAX_SET, THEME_SET}; -use errors::Result; +use context::RenderContext; +use errors::{Error, Result}; +use front_matter::InsertAnchor; use link_checker::check_url; +use table_of_contents::{make_table_of_contents, Header}; use utils::site::resolve_internal_link; +use utils::vec::InsertMany; -use context::RenderContext; -use table_of_contents::{make_table_of_contents, Header, TempHeader}; +use self::cmark::{Event, LinkType, Options, Parser, Tag}; -const CONTINUE_READING: &str = "

\n"; +const CONTINUE_READING: &str = + "

\n"; +const ANCHOR_LINK_TEMPLATE: &str = "anchor-link.html"; #[derive(Debug)] pub struct Rendered { @@ -25,6 +27,20 @@ pub struct Rendered { pub toc: Vec
, } +// tracks a header in a slice of pulldown-cmark events +#[derive(Debug)] +struct HeaderRef { + start_idx: usize, + end_idx: usize, + level: i32, +} + +impl HeaderRef { + fn new(start: usize, level: i32) -> HeaderRef { + HeaderRef { start_idx: start, end_idx: 0, level } + } +} + // We might have cases where the slug is already present in our list of anchor // for example an article could have several titles named Example // We add a counter after the slug if the slug is already present, which @@ -49,6 +65,71 @@ fn is_colocated_asset_link(link: &str) -> bool { && !link.starts_with("mailto:") } +fn fix_link(link_type: LinkType, link: &str, context: &RenderContext) -> Result { + if link_type == LinkType::Email { + return Ok(link.to_string()); + } + // A few situations here: + // - it could be a relative link (starting with `./`) + // - it could be a link to a co-located asset + // - it could be a normal link + let result = if link.starts_with("./") { + match resolve_internal_link(&link, context.permalinks) { + Ok(url) => url, + Err(_) => { + return Err(format!("Relative link {} not found.", link).into()); + } + } + } else if is_colocated_asset_link(&link) { + format!("{}{}", context.current_page_permalink, link) + } else if context.config.check_external_links + && !link.starts_with('#') + && !link.starts_with("mailto:") + { + let res = check_url(&link); + if res.is_valid() { + link.to_string() + } else { + return Err(format!("Link {} is not valid: {}", link, res.message()).into()); + } + } else { + link.to_string() + }; + Ok(result) +} + +/// get only text in a slice of events +fn get_text(parser_slice: &[Event]) -> String { + let mut title = String::new(); + + for event in parser_slice.iter() { + if let Event::Text(text) = event { + title += text; + } + } + + title +} + +fn get_header_refs(events: &[Event]) -> Vec { + let mut header_refs = vec![]; + + for (i, event) in events.iter().enumerate() { + match event { + Event::Start(Tag::Header(level)) => { + header_refs.push(HeaderRef::new(i, *level)); + } + Event::End(Tag::Header(_)) => { + let msg = "Header end before start?"; + header_refs.last_mut().expect(msg).end_idx = i; + } + _ => (), + } + } + + header_refs +} + pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result { // the rendered html let mut html = String::with_capacity(content.len()); @@ -57,17 +138,9 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result = None; - // If we get text in header, we need to insert the id and a anchor - let mut in_header = false; - // pulldown_cmark can send several text events for a title if there are markdown - // specific characters like `!` in them. We only want to insert the anchor the first time - let mut header_created = false; - let mut anchors: Vec = vec![]; - - let mut headers = vec![]; - // Defaults to a 0 level so not a real header - // It should be an Option ideally but not worth the hassle to update - let mut temp_header = TempHeader::default(); + + let mut inserted_anchors: Vec = vec![]; + let mut headers: Vec
= vec![]; let mut opts = Options::empty(); let mut has_summary = false; @@ -75,168 +148,129 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result { - // Header first - if in_header { - if header_created { - temp_header.add_text(&text); - return Event::Html(Borrowed("")); - } - // += as we might have some or other things already there - temp_header.add_text(&text); - header_created = true; - return Event::Html(Borrowed("")); - } - - // if we are in the middle of a code block - if let Some((ref mut highlighter, in_extra)) = highlighter { - let highlighted = if in_extra { - if let Some(ref extra) = context.config.extra_syntax_set { - highlighter.highlight(&text, &extra) + let mut events = Parser::new_ext(content, opts) + .map(|event| { + match event { + Event::Text(text) => { + // if we are in the middle of a code block + if let Some((ref mut highlighter, in_extra)) = highlighter { + let highlighted = if in_extra { + if let Some(ref extra) = context.config.extra_syntax_set { + highlighter.highlight(&text, &extra) + } else { + unreachable!( + "Got a highlighter from extra syntaxes but no extra?" + ); + } } else { - unreachable!("Got a highlighter from extra syntaxes but no extra?"); - } - } else { - highlighter.highlight(&text, &SYNTAX_SET) - }; - //let highlighted = &highlighter.highlight(&text, ss); - let html = styled_line_to_highlighted_html(&highlighted, background); - return Event::Html(Owned(html)); - } + highlighter.highlight(&text, &SYNTAX_SET) + }; + //let highlighted = &highlighter.highlight(&text, ss); + let html = styled_line_to_highlighted_html(&highlighted, background); + return Event::Html(html.into()); + } - // Business as usual - Event::Text(text) - } - Event::Start(Tag::CodeBlock(ref info)) => { - if !context.config.highlight_code { - return Event::Html(Borrowed("
"));
+                        // Business as usual
+                        Event::Text(text)
                     }
+                    Event::Start(Tag::CodeBlock(ref info)) => {
+                        if !context.config.highlight_code {
+                            return Event::Html("
".into());
+                        }
 
-                    let theme = &THEME_SET.themes[&context.config.highlight_theme];
-                    highlighter = Some(get_highlighter(info, &context.config));
-                    // This selects the background color the same way that start_coloured_html_snippet does
-                    let color =
-                        theme.settings.background.unwrap_or(::syntect::highlighting::Color::WHITE);
-                    background = IncludeBackground::IfDifferent(color);
-                    let snippet = start_highlighted_html_snippet(theme);
-                    Event::Html(Owned(snippet.0))
-                }
-                Event::End(Tag::CodeBlock(_)) => {
-                    if !context.config.highlight_code {
-                        return Event::Html(Borrowed("
\n")); + let theme = &THEME_SET.themes[&context.config.highlight_theme]; + highlighter = Some(get_highlighter(info, &context.config)); + // This selects the background color the same way that start_coloured_html_snippet does + let color = theme + .settings + .background + .unwrap_or(::syntect::highlighting::Color::WHITE); + background = IncludeBackground::IfDifferent(color); + let snippet = start_highlighted_html_snippet(theme); + Event::Html(snippet.0.into()) } - // reset highlight and close the code block - highlighter = None; - Event::Html(Borrowed("
")) - } - Event::Start(Tag::Image(src, title)) => { - if is_colocated_asset_link(&src) { - return Event::Start(Tag::Image( - Owned(format!("{}{}", context.current_page_permalink, src)), - title, - )); + Event::End(Tag::CodeBlock(_)) => { + if !context.config.highlight_code { + return Event::Html("\n".into()); + } + // reset highlight and close the code block + highlighter = None; + Event::Html("".into()) } + Event::Start(Tag::Image(link_type, src, title)) => { + if is_colocated_asset_link(&src) { + let link = format!("{}{}", context.current_page_permalink, &*src); + return Event::Start(Tag::Image(link_type, link.into(), title)); + } - Event::Start(Tag::Image(src, title)) - } - Event::Start(Tag::Link(link, title)) => { - // A few situations here: - // - it could be a relative link (starting with `./`) - // - it could be a link to a co-located asset - // - it could be a normal link - // - any of those can be in a header or not: if it's in a header - // we need to append to a string - let fixed_link = if link.starts_with("./") { - match resolve_internal_link(&link, context.permalinks) { - Ok(url) => url, - Err(_) => { - error = Some(format!("Relative link {} not found.", link).into()); - return Event::Html(Borrowed("")); + Event::Start(Tag::Image(link_type, src, title)) + } + Event::Start(Tag::Link(link_type, link, title)) => { + let fixed_link = match fix_link(link_type, &link, context) { + Ok(fixed_link) => fixed_link, + Err(err) => { + error = Some(err); + return Event::Html("".into()); } - } - } else if is_colocated_asset_link(&link) { - format!("{}{}", context.current_page_permalink, link) - } else if context.config.check_external_links - && !link.starts_with('#') - && !link.starts_with("mailto:") - { - let res = check_url(&link); - if res.is_valid() { - link.to_string() - } else { - error = Some( - format!("Link {} is not valid: {}", link, res.message()).into(), - ); - String::new() - } - } else { - link.to_string() - }; - - if in_header { - let html = if title.is_empty() { - format!("", fixed_link) - } else { - format!("", fixed_link, title) }; - temp_header.add_html(&html); - return Event::Html(Borrowed("")); - } - Event::Start(Tag::Link(Owned(fixed_link), title)) - } - Event::End(Tag::Link(_, _)) => { - if in_header { - temp_header.add_html(""); - return Event::Html(Borrowed("")); - } - event - } - Event::Start(Tag::Code) => { - if in_header { - temp_header.add_html(""); - return Event::Html(Borrowed("")); + Event::Start(Tag::Link(link_type, fixed_link.into(), title)) } - event - } - Event::End(Tag::Code) => { - if in_header { - temp_header.add_html(""); - return Event::Html(Borrowed("")); + Event::Html(ref markup) if markup.contains("") => { + has_summary = true; + Event::Html(CONTINUE_READING.into()) } - event + _ => event, } - Event::Start(Tag::Header(num)) => { - in_header = true; - temp_header = TempHeader::new(num); - Event::Html(Borrowed("")) - } - Event::End(Tag::Header(_)) => { - // End of a header, reset all the things and return the header string - - let id = find_anchor(&anchors, slugify(&temp_header.title), 0); - anchors.push(id.clone()); - temp_header.permalink = format!("{}#{}", context.current_page_permalink, id); - temp_header.id = id; - - in_header = false; - header_created = false; - let val = temp_header.to_string(context.tera, context.insert_anchor); - headers.push(temp_header.clone()); - temp_header = TempHeader::default(); - Event::Html(Owned(val)) - } - Event::Html(ref markup) if markup.contains("") => { - has_summary = true; - Event::Html(Borrowed(CONTINUE_READING)) - } - _ => event, + }) + .collect::>(); // We need to collect the events to make a second pass + + let header_refs = get_header_refs(&events); + + let mut anchors_to_insert = vec![]; + + for header_ref in header_refs { + let start_idx = header_ref.start_idx; + let end_idx = header_ref.end_idx; + let title = get_text(&events[start_idx + 1..end_idx]); + let id = find_anchor(&inserted_anchors, slugify(&title), 0); + inserted_anchors.push(id.clone()); + + // insert `id` to the tag + let html = format!("", lvl = header_ref.level, id = id); + events[start_idx] = Event::Html(html.into()); + + // generate anchors and places to insert them + if context.insert_anchor != InsertAnchor::None { + let anchor_idx = match context.insert_anchor { + InsertAnchor::Left => start_idx + 1, + InsertAnchor::Right => end_idx, + InsertAnchor::None => 0, // Not important + }; + let mut c = tera::Context::new(); + c.insert("id", &id); + + let anchor_link = utils::templates::render_template( + &ANCHOR_LINK_TEMPLATE, + context.tera, + c, + &None, + ) + .map_err(|e| Error::chain("Failed to render anchor link template", e))?; + anchors_to_insert.push((anchor_idx, Event::Html(anchor_link.into()))); } - }); - cmark::html::push_html(&mut html, parser); + // record header to make table of contents + let permalink = format!("{}#{}", context.current_page_permalink, id); + let h = Header { level: header_ref.level, id, permalink, title, children: Vec::new() }; + headers.push(h); + } + + if context.insert_anchor != InsertAnchor::None { + events.insert_many(anchors_to_insert); + } + + cmark::html::push_html(&mut html, events.into_iter()); } if let Some(e) = error { @@ -245,7 +279,7 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result) -> (String, Map) { for p in pair.into_inner() { match p.as_rule() { Rule::ident => { - name = Some(p.into_span().as_str().to_string()); + name = Some(p.as_span().as_str().to_string()); } Rule::kwarg => { let mut arg_name = None; @@ -66,7 +66,7 @@ fn parse_shortcode_call(pair: Pair) -> (String, Map) { for p2 in p.into_inner() { match p2.as_rule() { Rule::ident => { - arg_name = Some(p2.into_span().as_str().to_string()); + arg_name = Some(p2.as_span().as_str().to_string()); } Rule::literal => { arg_val = Some(parse_literal(p2)); @@ -108,15 +108,14 @@ fn render_shortcode( } if let Some(ref b) = body { // Trimming right to avoid most shortcodes with bodies ending up with a HTML new line - tera_context.insert("body", b.trim_right()); + tera_context.insert("body", b.trim_end()); } tera_context.extend(context.tera_context.clone()); - let tpl_name = format!("shortcodes/{}.html", name); - let res = context - .tera - .render(&tpl_name, &tera_context) - .chain_err(|| format!("Failed to render {} shortcode", name))?; + let template_name = format!("shortcodes/{}.html", name); + + let res = utils::templates::render_template(&template_name, &context.tera, tera_context, &None) + .map_err(|e| Error::chain(format!("Failed to render {} shortcode", name), e))?; // Small hack to avoid having multiple blank lines because of Tera tags for example // A blank like will cause the markdown parser to think we're out of HTML and start looking @@ -170,7 +169,7 @@ pub fn render_shortcodes(content: &str, context: &RenderContext) -> Result res.push_str(p.into_span().as_str()), + Rule::text => res.push_str(p.as_span().as_str()), Rule::inline_shortcode => { let (name, args) = parse_shortcode_call(p); res.push_str(&render_shortcode(&name, &args, context, None)?); @@ -180,12 +179,12 @@ pub fn render_shortcodes(content: &str, context: &RenderContext) -> Result { res.push_str( - &p.into_span().as_str().replacen("{{/*", "{{", 1).replacen("*/}}", "}}", 1), + &p.as_span().as_str().replacen("{{/*", "{{", 1).replacen("*/}}", "}}", 1), ); } Rule::ignored_shortcode_with_body => { @@ -193,13 +192,13 @@ pub fn render_shortcodes(content: &str, context: &RenderContext) -> Result { res.push_str( - &p2.into_span() + &p2.as_span() .as_str() .replacen("{%/*", "{%", 1) .replacen("*/%}", "%}", 1), ); } - Rule::text_in_ignored_body_sc => res.push_str(p2.into_span().as_str()), + Rule::text_in_ignored_body_sc => res.push_str(p2.as_span().as_str()), _ => unreachable!("Got something weird in an ignored shortcode: {:?}", p2), } } @@ -231,7 +230,7 @@ mod tests { panic!(); } assert!(res.is_ok()); - assert_eq!(res.unwrap().last().unwrap().into_span().end(), $input.len()); + assert_eq!(res.unwrap().last().unwrap().as_span().end(), $input.len()); }; } diff --git a/components/rendering/src/table_of_contents.rs b/components/rendering/src/table_of_contents.rs index 5cc115e..377b94d 100644 --- a/components/rendering/src/table_of_contents.rs +++ b/components/rendering/src/table_of_contents.rs @@ -1,160 +1,59 @@ -use front_matter::InsertAnchor; -use tera::{Context as TeraContext, Tera}; - +/// Populated while receiving events from the markdown parser #[derive(Debug, PartialEq, Clone, Serialize)] pub struct Header { #[serde(skip_serializing)] pub level: i32, pub id: String, - pub title: String, pub permalink: String, + pub title: String, pub children: Vec
, } impl Header { - pub fn from_temp_header(tmp: &TempHeader, children: Vec
) -> Header { + pub fn new(level: i32) -> Header { Header { - level: tmp.level, - id: tmp.id.clone(), - title: tmp.title.clone(), - permalink: tmp.permalink.clone(), - children, - } - } -} - -/// Populated while receiving events from the markdown parser -#[derive(Debug, PartialEq, Clone)] -pub struct TempHeader { - pub level: i32, - pub id: String, - pub permalink: String, - pub title: String, - pub html: String, -} - -impl TempHeader { - pub fn new(level: i32) -> TempHeader { - TempHeader { level, id: String::new(), permalink: String::new(), title: String::new(), - html: String::new(), - } - } - - pub fn add_html(&mut self, val: &str) { - self.html += val; - } - - pub fn add_text(&mut self, val: &str) { - self.html += val; - self.title += val; - } - - /// Transform all the information we have about this header into the HTML string for it - pub fn to_string(&self, tera: &Tera, insert_anchor: InsertAnchor) -> String { - let anchor_link = if insert_anchor != InsertAnchor::None { - let mut c = TeraContext::new(); - c.insert("id", &self.id); - tera.render("anchor-link.html", &c).unwrap() - } else { - String::new() - }; - - match insert_anchor { - InsertAnchor::None => format!( - "{t}\n", - lvl = self.level, - t = self.html, - id = self.id - ), - InsertAnchor::Left => format!( - "{a}{t}\n", - lvl = self.level, - a = anchor_link, - t = self.html, - id = self.id - ), - InsertAnchor::Right => format!( - "{t}{a}\n", - lvl = self.level, - a = anchor_link, - t = self.html, - id = self.id - ), + children: Vec::new(), } } } -impl Default for TempHeader { +impl Default for Header { fn default() -> Self { - TempHeader::new(0) - } -} - -/// Recursively finds children of a header -fn find_children( - parent_level: i32, - start_at: usize, - temp_headers: &[TempHeader], -) -> (usize, Vec
) { - let mut headers = vec![]; - - let mut start_at = start_at; - // If we have children, we will need to skip some headers since they are already inserted - let mut to_skip = 0; - - for h in &temp_headers[start_at..] { - // stop when we encounter a title at the same level or higher - // than the parent one. Here a lower integer is considered higher as we are talking about - // HTML headers: h1, h2, h3, h4, h5 and h6 - if h.level <= parent_level { - return (start_at, headers); - } - - // Do we need to skip some headers? - if to_skip > 0 { - to_skip -= 1; - continue; - } - - let (end, children) = find_children(h.level, start_at + 1, temp_headers); - headers.push(Header::from_temp_header(h, children)); - - // we didn't find any children - if end == start_at { - start_at += 1; - to_skip = 0; - } else { - // calculates how many we need to skip. Since the find_children start_at starts at 1, - // we need to remove 1 to ensure correctness - to_skip = end - start_at - 1; - start_at = end; - } - - // we don't want to index out of bounds - if start_at + 1 > temp_headers.len() { - return (start_at, headers); - } + Header::new(0) } - - (start_at, headers) } /// Converts the flat temp headers into a nested set of headers /// representing the hierarchy -pub fn make_table_of_contents(temp_headers: &[TempHeader]) -> Vec
{ +pub fn make_table_of_contents(headers: Vec
) -> Vec
{ let mut toc = vec![]; - let mut start_idx = 0; - for (i, h) in temp_headers.iter().enumerate() { - if i < start_idx { + 'parent: for header in headers { + if toc.is_empty() { + toc.push(header); continue; } - let (end_idx, children) = find_children(h.level, start_idx + 1, temp_headers); - start_idx = end_idx; - toc.push(Header::from_temp_header(h, children)); + + // See if we have to insert as a child of a previous header + for h in toc.iter_mut().rev() { + // Look in its children first + for child in h.children.iter_mut().rev() { + if header.level > child.level { + child.children.push(header); + continue 'parent; + } + } + if header.level > h.level { + h.children.push(header); + continue 'parent; + } + } + + // Nop, just insert it + toc.push(header) } toc @@ -166,25 +65,25 @@ mod tests { #[test] fn can_make_basic_toc() { - let input = vec![TempHeader::new(1), TempHeader::new(1), TempHeader::new(1)]; - let toc = make_table_of_contents(&input); + let input = vec![Header::new(1), Header::new(1), Header::new(1)]; + let toc = make_table_of_contents(input); assert_eq!(toc.len(), 3); } #[test] fn can_make_more_complex_toc() { let input = vec![ - TempHeader::new(1), - TempHeader::new(2), - TempHeader::new(2), - TempHeader::new(3), - TempHeader::new(2), - TempHeader::new(1), - TempHeader::new(2), - TempHeader::new(3), - TempHeader::new(3), + Header::new(1), + Header::new(2), + Header::new(2), + Header::new(3), + Header::new(2), + Header::new(1), + Header::new(2), + Header::new(3), + Header::new(3), ]; - let toc = make_table_of_contents(&input); + let toc = make_table_of_contents(input); assert_eq!(toc.len(), 2); assert_eq!(toc[0].children.len(), 3); assert_eq!(toc[1].children.len(), 1); @@ -195,15 +94,16 @@ mod tests { #[test] fn can_make_messy_toc() { let input = vec![ - TempHeader::new(3), - TempHeader::new(2), - TempHeader::new(2), - TempHeader::new(3), - TempHeader::new(2), - TempHeader::new(1), - TempHeader::new(4), + Header::new(3), + Header::new(2), + Header::new(2), + Header::new(3), + Header::new(2), + Header::new(1), + Header::new(4), ]; - let toc = make_table_of_contents(&input); + let toc = make_table_of_contents(input); + println!("{:#?}", toc); assert_eq!(toc.len(), 5); assert_eq!(toc[2].children.len(), 1); assert_eq!(toc[4].children.len(), 1); diff --git a/components/rendering/tests/markdown.rs b/components/rendering/tests/markdown.rs index 8585ae3..e2f36ce 100644 --- a/components/rendering/tests/markdown.rs +++ b/components/rendering/tests/markdown.rs @@ -44,7 +44,7 @@ fn can_highlight_code_block_no_lang() { let res = render_content("```\n$ gutenberg server\n$ ping\n```", &context).unwrap(); assert_eq!( res.body, - "
\n$ gutenberg server\n$ ping\n
" + "
\n$ gutenberg server\n$ ping\n
" ); } @@ -375,6 +375,19 @@ fn can_insert_anchor_right() { ); } +#[test] +fn can_insert_anchor_for_multi_header() { + let permalinks_ctx = HashMap::new(); + let config = Config::default(); + let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::Right); + let res = render_content("# Hello\n# World", &context).unwrap(); + assert_eq!( + res.body, + "

Hello🔗\n

\n\ +

World🔗\n

\n" + ); +} + // See https://github.com/Keats/gutenberg/issues/42 #[test] fn can_insert_anchor_with_exclamation_mark() { @@ -522,6 +535,47 @@ fn can_understand_link_with_title_in_header() { ); } +#[test] +fn can_understand_emphasis_in_header() { + let permalinks_ctx = HashMap::new(); + let config = Config::default(); + let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::None); + let res = render_content("# *Emphasis* text", &context).unwrap(); + assert_eq!(res.body, "

Emphasis text

\n"); +} + +#[test] +fn can_understand_strong_in_header() { + let permalinks_ctx = HashMap::new(); + let config = Config::default(); + let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::None); + let res = render_content("# **Strong** text", &context).unwrap(); + assert_eq!(res.body, "

Strong text

\n"); +} + +#[test] +fn can_understand_code_in_header() { + let permalinks_ctx = HashMap::new(); + let config = Config::default(); + let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::None); + let res = render_content("# `Code` text", &context).unwrap(); + assert_eq!(res.body, "

Code text

\n"); +} + +// See https://github.com/getzola/zola/issues/569 +#[test] +fn can_understand_footnote_in_header() { + let permalinks_ctx = HashMap::new(); + let config = Config::default(); + let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::None); + let res = render_content("# text [^1] there\n[^1]: footnote", &context).unwrap(); + assert_eq!(res.body, r##"

text 1 there

+
1 +

footnote

+
+"##); +} + #[test] fn can_make_valid_relative_link_in_header() { let mut permalinks = HashMap::new(); @@ -633,7 +687,7 @@ fn can_show_error_message_for_invalid_external_links() { let res = render_content("[a link](http://google.comy)", &context); assert!(res.is_err()); let err = res.unwrap_err(); - assert!(err.description().contains("Link http://google.comy is not valid")); + assert!(format!("{}", err).contains("Link http://google.comy is not valid")); } #[test] @@ -675,17 +729,25 @@ fn can_handle_summaries() { let config = Config::default(); let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let res = render_content( - "Hello [world]\n\n\n\nBla bla\n\n[world]: https://vincent.is/about/", + r#" +Hello [My site][world] + + + +Bla bla + +[world]: https://vincentprouillet.com +"#, &context, ) .unwrap(); assert_eq!( res.body, - "

Hello world

\n

\n

Bla bla

\n" + "

Hello My site

\n

\n

Bla bla

\n" ); assert_eq!( res.summary_len, - Some("

Hello world

\n".len()) + Some("

Hello My site

".len()) ); } @@ -721,3 +783,31 @@ fn doesnt_try_to_highlight_content_from_shortcode() { let res = render_content(markdown_string, &context).unwrap(); assert_eq!(res.body, expected); } + +// TODO: re-enable once it's fixed in Tera +// https://github.com/Keats/tera/issues/373 +//#[test] +//fn can_split_lines_shortcode_body() { +// let permalinks_ctx = HashMap::new(); +// let mut tera = Tera::default(); +// tera.extend(&ZOLA_TERA).unwrap(); +// +// let shortcode = r#"{{ body | split(pat="\n") }}"#; +// +// let markdown_string = r#" +//{% alert() %} +//multi +//ple +//lines +//{% end %} +// "#; +// +// let expected = r#"

["multi", "ple", "lines"]

"#; +// +// tera.add_raw_template(&format!("shortcodes/{}.html", "alert"), shortcode).unwrap(); +// let config = Config::default(); +// let context = RenderContext::new(&tera, &config, "", &permalinks_ctx, InsertAnchor::None); +// +// let res = render_content(markdown_string, &context).unwrap(); +// assert_eq!(res.body, expected); +//} diff --git a/components/search/Cargo.toml b/components/search/Cargo.toml index 0a666b0..dbfe736 100644 --- a/components/search/Cargo.toml +++ b/components/search/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Vincent Prouillet "] [dependencies] elasticlunr-rs = "2" -ammonia = "1" +ammonia = "2" lazy_static = "1" errors = { path = "../errors" } diff --git a/components/site/Cargo.toml b/components/site/Cargo.toml index 58291ab..bb88d12 100644 --- a/components/site/Cargo.toml +++ b/components/site/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Vincent Prouillet "] [dependencies] -tera = "0.11" +tera = "1.0.0-alpha.3" glob = "0.2" rayon = "1" serde = "1" diff --git a/components/site/benches/gen.py b/components/site/benches/gen.py index 060104f..c30709b 100644 --- a/components/site/benches/gen.py +++ b/components/site/benches/gen.py @@ -169,6 +169,7 @@ if __name__ == "__main__": gen_site("medium-blog", [""], 250, is_blog=True) gen_site("big-blog", [""], 1000, is_blog=True) gen_site("huge-blog", [""], 10000, is_blog=True) + gen_site("extra-huge-blog", [""], 100000, is_blog=True) gen_site("small-kb", ["help", "help1", "help2", "help3", "help4", "help5", "help6", "help7", "help8", "help9"], 10) gen_site("medium-kb", ["help", "help1", "help2", "help3", "help4", "help5", "help6", "help7", "help8", "help9"], 100) diff --git a/components/site/benches/site.rs b/components/site/benches/site.rs index 0c507c1..c65badb 100644 --- a/components/site/benches/site.rs +++ b/components/site/benches/site.rs @@ -43,7 +43,7 @@ fn bench_render_rss_feed(b: &mut test::Bencher) { let tmp_dir = tempdir().expect("create temp dir"); let public = &tmp_dir.path().join("public"); site.set_output_path(&public); - b.iter(|| site.render_rss_feed(site.library.pages_values(), None).unwrap()); + b.iter(|| site.render_rss_feed(site.library.read().unwrap().pages_values(), None).unwrap()); } #[bench] @@ -61,8 +61,9 @@ fn bench_render_paginated(b: &mut test::Bencher) { let tmp_dir = tempdir().expect("create temp dir"); let public = &tmp_dir.path().join("public"); site.set_output_path(&public); - let section = site.library.sections_values()[0]; - let paginator = Paginator::from_section(§ion, &site.library); + let library = site.library.read().unwrap(); + let section = library.sections_values()[0]; + let paginator = Paginator::from_section(§ion, &library); b.iter(|| site.render_paginated(public, &paginator)); } diff --git a/components/site/src/lib.rs b/components/site/src/lib.rs index d46f1c5..57ba91d 100644 --- a/components/site/src/lib.rs +++ b/components/site/src/lib.rs @@ -19,10 +19,13 @@ extern crate utils; #[cfg(test)] extern crate tempfile; -use std::collections::HashMap; + +mod sitemap; + +use std::collections::{HashMap}; use std::fs::{copy, create_dir_all, remove_dir_all}; use std::path::{Path, PathBuf}; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, RwLock}; use glob::glob; use rayon::prelude::*; @@ -30,7 +33,7 @@ use sass_rs::{compile_file, Options as SassOptions, OutputStyle}; use tera::{Context, Tera}; use config::{get_config, Config}; -use errors::{Result, ResultExt}; +use errors::{Error, Result}; use front_matter::InsertAnchor; use library::{ find_taxonomies, sort_actual_pages_by_date, Library, Page, Paginator, Section, Taxonomy, @@ -40,20 +43,6 @@ use utils::fs::{copy_directory, create_directory, create_file, ensure_directory_ use utils::net::get_available_port; use utils::templates::{render_template, rewrite_theme_paths}; -/// The sitemap only needs links and potentially date so we trim down -/// all pages to only that -#[derive(Debug, Serialize)] -struct SitemapEntry { - permalink: String, - date: Option, -} - -impl SitemapEntry { - pub fn new(permalink: String, date: Option) -> SitemapEntry { - SitemapEntry { permalink, date } - } -} - #[derive(Debug)] pub struct Site { /// The base path of the zola site @@ -72,12 +61,12 @@ pub struct Site { /// We need that if there are relative links in the content that need to be resolved pub permalinks: HashMap, /// Contains all pages and sections of the site - pub library: Library, + pub library: Arc>, } impl Site { /// Parse a site at the given path. Defaults to the current dir - /// Passing in a path is only used in tests + /// Passing in a path is possible using the `base-path` command line build option pub fn new>(path: P, config_file: &str) -> Result { let path = path.as_ref(); let mut config = get_config(path, config_file); @@ -87,7 +76,8 @@ impl Site { format!("{}/{}", path.to_string_lossy().replace("\\", "/"), "templates/**/*.*ml"); // Only parsing as we might be extending templates from themes and that would error // as we haven't loaded them yet - let mut tera = Tera::parse(&tpl_glob).chain_err(|| "Error parsing templates")?; + let mut tera = + Tera::parse(&tpl_glob).map_err(|e| Error::chain("Error parsing templates", e))?; if let Some(theme) = config.theme.clone() { // Grab data from the extra section of the theme config.merge_with_theme(&path.join("themes").join(&theme).join("theme.toml"))?; @@ -103,10 +93,10 @@ impl Site { path.to_string_lossy().replace("\\", "/"), format!("themes/{}/templates/**/*.*ml", theme) ); - let mut tera_theme = - Tera::parse(&theme_tpl_glob).chain_err(|| "Error parsing templates from themes")?; + let mut tera_theme = Tera::parse(&theme_tpl_glob) + .map_err(|e| Error::chain("Error parsing templates from themes", e))?; rewrite_theme_paths(&mut tera_theme, &theme); - // TODO: same as below + // TODO: we do that twice, make it dry? if theme_path.join("templates").join("robots.txt").exists() { tera_theme .add_template_file(theme_path.join("templates").join("robots.txt"), None)?; @@ -141,15 +131,23 @@ impl Site { taxonomies: Vec::new(), permalinks: HashMap::new(), // We will allocate it properly later on - library: Library::new(0, 0), + library: Arc::new(RwLock::new(Library::new(0, 0, false))), }; Ok(site) } - /// The index section is ALWAYS at that path - pub fn index_section_path(&self) -> PathBuf { - self.content_path.join("_index.md") + /// The index sections are ALWAYS at those paths + /// There are one index section for the basic language + 1 per language + fn index_section_paths(&self) -> Vec<(PathBuf, Option)> { + let mut res = vec![(self.content_path.join("_index.md"), None)]; + for language in &self.config.languages { + res.push(( + self.content_path.join(format!("_index.{}.md", language.code)), + Some(language.code.clone()), + )); + } + res } /// We avoid the port the server is going to use as it's not bound yet @@ -159,13 +157,13 @@ impl Site { self.live_reload = get_available_port(port_to_avoid); } - /// Get all the orphan (== without section) pages in the site - pub fn get_all_orphan_pages(&self) -> Vec<&Page> { - self.library.get_all_orphan_pages() + /// Get the number of orphan (== without section) pages in the site + pub fn get_number_orphan_pages(&self) -> usize { + self.library.read().unwrap().get_all_orphan_pages().len() } pub fn set_base_url(&mut self, base_url: String) { - let mut imageproc = self.imageproc.lock().unwrap(); + let mut imageproc = self.imageproc.lock().expect("Couldn't lock imageproc (set_base_url)"); imageproc.set_base_url(&base_url); self.config.base_url = base_url; } @@ -181,12 +179,18 @@ impl Site { let content_glob = format!("{}/{}", base_path, "content/**/*.md"); let (section_entries, page_entries): (Vec<_>, Vec<_>) = glob(&content_glob) - .unwrap() + .expect("Invalid glob") .filter_map(|e| e.ok()) .filter(|e| !e.as_path().file_name().unwrap().to_str().unwrap().starts_with('.')) - .partition(|entry| entry.as_path().file_name().unwrap() == "_index.md"); + .partition(|entry| { + entry.as_path().file_name().unwrap().to_str().unwrap().starts_with("_index.") + }); - self.library = Library::new(page_entries.len(), section_entries.len()); + self.library = Arc::new(RwLock::new(Library::new( + page_entries.len(), + section_entries.len(), + self.config.is_multilingual(), + ))); let sections = { let config = &self.config; @@ -195,7 +199,7 @@ impl Site { .into_par_iter() .map(|entry| { let path = entry.as_path(); - Section::from_file(path, config) + Section::from_file(path, config, &self.base_path) }) .collect::>() }; @@ -207,7 +211,7 @@ impl Site { .into_par_iter() .map(|entry| { let path = entry.as_path(); - Page::from_file(path, config) + Page::from_file(path, config, &self.base_path) }) .collect::>() }; @@ -219,47 +223,69 @@ impl Site { self.add_section(s, false)?; } - // Insert a default index section if necessary so we don't need to create - // a _index.md to render the index page at the root of the site - let index_path = self.index_section_path(); - if let Some(ref index_section) = self.library.get_section(&index_path) { - if self.config.build_search_index && !index_section.meta.in_search_index { - bail!( - "You have enabled search in the config but disabled it in the index section: \ - either turn off the search in the config or remote `in_search_index = true` from the \ - section front-matter." - ) - } - } - // Not in else because of borrow checker - if !self.library.contains_section(&index_path) { - let mut index_section = Section::default(); - index_section.permalink = self.config.make_permalink(""); - index_section.file.path = self.content_path.join("_index.md"); - index_section.file.parent = self.content_path.clone(); - index_section.file.relative = "_index.md".to_string(); - self.library.insert_section(index_section); - } + self.create_default_index_sections()?; let mut pages_insert_anchors = HashMap::new(); for page in pages { let p = page?; pages_insert_anchors.insert( p.file.path.clone(), - self.find_parent_section_insert_anchor(&p.file.parent.clone()), + self.find_parent_section_insert_anchor(&p.file.parent.clone(), &p.lang), ); self.add_page(p, false)?; } + // taxonomy Tera fns are loaded in `register_early_global_fns` + // so we do need to populate it first. + self.populate_taxonomies()?; self.register_early_global_fns(); self.populate_sections(); self.render_markdown()?; - self.populate_taxonomies()?; self.register_tera_global_fns(); Ok(()) } + /// Insert a default index section for each language if necessary so we don't need to create + /// a _index.md to render the index page at the root of the site + pub fn create_default_index_sections(&mut self) -> Result<()> { + for (index_path, lang) in self.index_section_paths() { + if let Some(ref index_section) = self.library.read().unwrap().get_section(&index_path) { + if self.config.build_search_index && !index_section.meta.in_search_index { + bail!( + "You have enabled search in the config but disabled it in the index section: \ + either turn off the search in the config or remote `in_search_index = true` from the \ + section front-matter." + ) + } + } + let mut library = self.library.write().expect("Get lock for load"); + // Not in else because of borrow checker + if !library.contains_section(&index_path) { + let mut index_section = Section::default(); + index_section.file.parent = self.content_path.clone(); + index_section.file.filename = + index_path.file_name().unwrap().to_string_lossy().to_string(); + if let Some(ref l) = lang { + index_section.file.name = format!("_index.{}", l); + index_section.permalink = self.config.make_permalink(l); + let filename = format!("_index.{}.md", l); + index_section.file.path = self.content_path.join(&filename); + index_section.file.relative = filename; + index_section.lang = index_section.file.find_language(&self.config)?; + } else { + index_section.file.name = "_index".to_string(); + index_section.permalink = self.config.make_permalink(""); + index_section.file.path = self.content_path.join("_index.md"); + index_section.file.relative = "_index.md".to_string(); + } + library.insert_section(index_section); + } + } + + Ok(()) + } + /// Render the markdown of all pages/sections /// Used in a build and in `serve` if a shortcode has changed pub fn render_markdown(&mut self) -> Result<()> { @@ -271,14 +297,15 @@ impl Site { // This is needed in the first place because of silly borrow checker let mut pages_insert_anchors = HashMap::new(); - for (_, p) in self.library.pages() { + for (_, p) in self.library.read().unwrap().pages() { pages_insert_anchors.insert( p.file.path.clone(), - self.find_parent_section_insert_anchor(&p.file.parent.clone()), + self.find_parent_section_insert_anchor(&p.file.parent.clone(), &p.lang), ); } - self.library + let mut library = self.library.write().expect("Get lock for render_markdown"); + library .pages_mut() .values_mut() .collect::>() @@ -289,7 +316,7 @@ impl Site { }) .collect::>()?; - self.library + library .sections_mut() .values_mut() .collect::>() @@ -300,33 +327,37 @@ impl Site { Ok(()) } - /// Adds global fns that are to be available to shortcodes while rendering markdown + /// Adds global fns that are to be available to shortcodes while + /// markdown pub fn register_early_global_fns(&mut self) { self.tera.register_function( "get_url", - global_fns::make_get_url(self.permalinks.clone(), self.config.clone()), + global_fns::GetUrl::new(self.config.clone(), self.permalinks.clone()), ); self.tera.register_function( "resize_image", - global_fns::make_resize_image(self.imageproc.clone()), + global_fns::ResizeImage::new(self.imageproc.clone()), + ); + self.tera.register_function("load_data", global_fns::LoadData::new(self.base_path.clone())); + self.tera.register_function("trans", global_fns::Trans::new(self.config.clone())); + self.tera.register_function( + "get_taxonomy_url", + global_fns::GetTaxonomyUrl::new(&self.taxonomies), ); } pub fn register_tera_global_fns(&mut self) { - self.tera.register_function("trans", global_fns::make_trans(self.config.clone())); - self.tera.register_function("get_page", global_fns::make_get_page(&self.library)); - self.tera.register_function("get_section", global_fns::make_get_section(&self.library)); self.tera.register_function( - "get_taxonomy", - global_fns::make_get_taxonomy(&self.taxonomies, &self.library), + "get_page", + global_fns::GetPage::new(self.base_path.clone(), self.library.clone()), ); self.tera.register_function( - "get_taxonomy_url", - global_fns::make_get_taxonomy_url(&self.taxonomies), + "get_section", + global_fns::GetSection::new(self.base_path.clone(), self.library.clone()), ); self.tera.register_function( - "load_data", - global_fns::make_load_data(self.content_path.clone(), self.base_path.clone()), + "get_taxonomy", + global_fns::GetTaxonomy::new(self.taxonomies.clone(), self.library.clone()), ); } @@ -337,11 +368,13 @@ impl Site { pub fn add_page(&mut self, mut page: Page, render: bool) -> Result> { self.permalinks.insert(page.file.relative.clone(), page.permalink.clone()); if render { - let insert_anchor = self.find_parent_section_insert_anchor(&page.file.parent); + let insert_anchor = + self.find_parent_section_insert_anchor(&page.file.parent, &page.lang); page.render_markdown(&self.permalinks, &self.tera, &self.config, insert_anchor)?; } - let prev = self.library.remove_page(&page.file.path); - self.library.insert_page(page); + let mut library = self.library.write().expect("Get lock for add_page"); + let prev = library.remove_page(&page.file.path); + library.insert_page(page); Ok(prev) } @@ -355,16 +388,26 @@ impl Site { if render { section.render_markdown(&self.permalinks, &self.tera, &self.config)?; } - let prev = self.library.remove_section(§ion.file.path); - self.library.insert_section(section); + let mut library = self.library.write().expect("Get lock for add_section"); + let prev = library.remove_section(§ion.file.path); + library.insert_section(section); Ok(prev) } /// Finds the insert_anchor for the parent section of the directory at `path`. /// Defaults to `AnchorInsert::None` if no parent section found - pub fn find_parent_section_insert_anchor(&self, parent_path: &PathBuf) -> InsertAnchor { - match self.library.get_section(&parent_path.join("_index.md")) { + pub fn find_parent_section_insert_anchor( + &self, + parent_path: &PathBuf, + lang: &str, + ) -> InsertAnchor { + let parent = if lang != self.config.default_language { + parent_path.join(format!("_index.{}.md", lang)) + } else { + parent_path.join("_index.md") + }; + match self.library.read().unwrap().get_section(&parent) { Some(s) => s.meta.insert_anchor_links, None => InsertAnchor::None, } @@ -373,7 +416,8 @@ impl Site { /// Find out the direct subsections of each subsection if there are some /// as well as the pages for each section pub fn populate_sections(&mut self) { - self.library.populate_sections(); + let mut library = self.library.write().expect("Get lock for populate_sections"); + library.populate_sections(&self.config); } /// Find all the tags and categories if it's asked in the config @@ -382,7 +426,7 @@ impl Site { return Ok(()); } - self.taxonomies = find_taxonomies(&self.config, &self.library)?; + self.taxonomies = find_taxonomies(&self.config, &self.library.read().unwrap())?; Ok(()) } @@ -420,12 +464,13 @@ impl Site { } pub fn num_img_ops(&self) -> usize { - let imageproc = self.imageproc.lock().unwrap(); + let imageproc = self.imageproc.lock().expect("Couldn't lock imageproc (num_img_ops)"); imageproc.num_img_ops() } pub fn process_images(&self) -> Result<()> { - let mut imageproc = self.imageproc.lock().unwrap(); + let mut imageproc = + self.imageproc.lock().expect("Couldn't lock imageproc (process_images)"); imageproc.prune()?; imageproc.do_process() } @@ -434,7 +479,8 @@ impl Site { pub fn clean(&self) -> Result<()> { if self.output_path.exists() { // Delete current `public` directory so we can start fresh - remove_dir_all(&self.output_path).chain_err(|| "Couldn't delete output directory")?; + remove_dir_all(&self.output_path) + .map_err(|e| Error::chain("Couldn't delete output directory", e))?; } Ok(()) @@ -459,13 +505,17 @@ impl Site { create_directory(¤t_path)?; // Finally, create a index.html file there with the page rendered - let output = page.render_html(&self.tera, &self.config, &self.library)?; + let output = page.render_html(&self.tera, &self.config, &self.library.read().unwrap())?; create_file(¤t_path.join("index.html"), &self.inject_livereload(output))?; // Copy any asset we found previously into the same directory as the index.html for asset in &page.assets { let asset_path = asset.as_path(); - copy(&asset_path, ¤t_path.join(asset_path.file_name().unwrap()))?; + copy( + &asset_path, + ¤t_path + .join(asset_path.file_name().expect("Couldn't get filename from page asset")), + )?; } Ok(()) @@ -474,18 +524,8 @@ impl Site { /// Deletes the `public` directory and builds the site pub fn build(&self) -> Result<()> { self.clean()?; - // Render aliases first to allow overwriting - self.render_aliases()?; - self.render_sections()?; - self.render_orphan_pages()?; - self.render_sitemap()?; - if self.config.generate_rss { - self.render_rss_feed(self.library.pages_values(), None)?; - } - self.render_404()?; - self.render_robots()?; - self.render_taxonomies()?; + // Generate/move all assets before rendering any content if let Some(ref theme) = self.config.theme { let theme_path = self.base_path.join("themes").join(theme); if theme_path.join("sass").exists() { @@ -504,6 +544,40 @@ impl Site { self.build_search_index()?; } + // Render aliases first to allow overwriting + self.render_aliases()?; + self.render_sections()?; + self.render_orphan_pages()?; + self.render_sitemap()?; + + let library = self.library.read().unwrap(); + if self.config.generate_rss { + let pages = if self.config.is_multilingual() { + library + .pages_values() + .iter() + .filter(|p| p.lang == self.config.default_language) + .map(|p| *p) + .collect() + } else { + library.pages_values() + }; + self.render_rss_feed(pages, None)?; + } + + for lang in &self.config.languages { + if !lang.rss { + continue; + } + let pages = + library.pages_values().iter().filter(|p| p.lang == lang.code).map(|p| *p).collect(); + self.render_rss_feed(pages, Some(&PathBuf::from(lang.code.clone())))?; + } + + self.render_404()?; + self.render_robots()?; + self.render_taxonomies()?; + Ok(()) } @@ -513,7 +587,7 @@ impl Site { &self.output_path.join(&format!("search_index.{}.js", self.config.default_language)), &format!( "window.searchIndex = {};", - search::build_index(&self.config.default_language, &self.library)? + search::build_index(&self.config.default_language, &self.library.read().unwrap())? ), )?; @@ -562,7 +636,7 @@ impl Site { ) -> Result> { let glob_string = format!("{}/**/*.{}", sass_path.display(), extension); let files = glob(&glob_string) - .unwrap() + .expect("Invalid glob for sass") .filter_map(|e| e.ok()) .filter(|entry| { !entry.as_path().file_name().unwrap().to_string_lossy().starts_with('_') @@ -590,7 +664,7 @@ impl Site { pub fn render_aliases(&self) -> Result<()> { ensure_directory_exists(&self.output_path)?; - for (_, page) in self.library.pages() { + for (_, page) in self.library.read().unwrap().pages() { for alias in &page.meta.aliases { let mut output_path = self.output_path.to_path_buf(); let mut split = alias.split('/').collect::>(); @@ -627,7 +701,7 @@ impl Site { ensure_directory_exists(&self.output_path)?; let mut context = Context::new(); context.insert("config", &self.config); - let output = render_template("404.html", &self.tera, &context, &self.config.theme)?; + let output = render_template("404.html", &self.tera, context, &self.config.theme)?; create_file(&self.output_path.join("404.html"), &self.inject_livereload(output)) } @@ -638,7 +712,7 @@ impl Site { context.insert("config", &self.config); create_file( &self.output_path.join("robots.txt"), - &render_template("robots.txt", &self.tera, &context, &self.config.theme)?, + &render_template("robots.txt", &self.tera, context, &self.config.theme)?, ) } @@ -657,11 +731,18 @@ impl Site { } ensure_directory_exists(&self.output_path)?; - let output_path = self.output_path.join(&taxonomy.kind.name); - let list_output = taxonomy.render_all_terms(&self.tera, &self.config, &self.library)?; + let output_path = if taxonomy.kind.lang != self.config.default_language { + let mid_path = self.output_path.join(&taxonomy.kind.lang); + create_directory(&mid_path)?; + mid_path.join(&taxonomy.kind.name) + } else { + self.output_path.join(&taxonomy.kind.name) + }; + let list_output = + taxonomy.render_all_terms(&self.tera, &self.config, &self.library.read().unwrap())?; create_directory(&output_path)?; create_file(&output_path.join("index.html"), &self.inject_livereload(list_output))?; - + let library = self.library.read().unwrap(); taxonomy .items .par_iter() @@ -670,18 +751,18 @@ impl Site { if taxonomy.kind.is_paginated() { self.render_paginated( &path, - &Paginator::from_taxonomy(&taxonomy, item, &self.library), + &Paginator::from_taxonomy(&taxonomy, item, &library), )?; } else { let single_output = - taxonomy.render_term(item, &self.tera, &self.config, &self.library)?; + taxonomy.render_term(item, &self.tera, &self.config, &library)?; create_directory(&path)?; create_file(&path.join("index.html"), &self.inject_livereload(single_output))?; } if taxonomy.kind.rss { self.render_rss_feed( - item.pages.iter().map(|p| self.library.get_page_by_key(*p)).collect(), + item.pages.iter().map(|p| library.get_page_by_key(*p)).collect(), Some(&PathBuf::from(format!("{}/{}", taxonomy.kind.name, item.slug))), ) } else { @@ -695,82 +776,46 @@ impl Site { pub fn render_sitemap(&self) -> Result<()> { ensure_directory_exists(&self.output_path)?; - let mut context = Context::new(); - - let mut pages = self - .library - .pages_values() - .iter() - .filter(|p| !p.is_draft()) - .map(|p| { - let date = match p.meta.date { - Some(ref d) => Some(d.to_string()), - None => None, - }; - SitemapEntry::new(p.permalink.clone(), date) - }) - .collect::>(); - pages.sort_by(|a, b| a.permalink.cmp(&b.permalink)); - context.insert("pages", &pages); - - let mut sections = self - .library - .sections_values() - .iter() - .map(|s| SitemapEntry::new(s.permalink.clone(), None)) - .collect::>(); - for section in - self.library.sections_values().iter().filter(|s| s.meta.paginate_by.is_some()) - { - let number_pagers = (section.pages.len() as f64 - / section.meta.paginate_by.unwrap() as f64) - .ceil() as isize; - for i in 1..number_pagers + 1 { - let permalink = - format!("{}{}/{}/", section.permalink, section.meta.paginate_path, i); - sections.push(SitemapEntry::new(permalink, None)) - } - } - sections.sort_by(|a, b| a.permalink.cmp(&b.permalink)); - context.insert("sections", §ions); - - let mut taxonomies = vec![]; - for taxonomy in &self.taxonomies { - let name = &taxonomy.kind.name; - let mut terms = vec![]; - terms.push(SitemapEntry::new(self.config.make_permalink(name), None)); - for item in &taxonomy.items { - terms.push(SitemapEntry::new( - self.config.make_permalink(&format!("{}/{}", &name, item.slug)), - None, - )); - - if taxonomy.kind.is_paginated() { - let number_pagers = (item.pages.len() as f64 - / taxonomy.kind.paginate_by.unwrap() as f64) - .ceil() as isize; - for i in 1..number_pagers + 1 { - let permalink = self.config.make_permalink(&format!( - "{}/{}/{}/{}", - name, - item.slug, - taxonomy.kind.paginate_path(), - i - )); - terms.push(SitemapEntry::new(permalink, None)) - } - } - } - - terms.sort_by(|a, b| a.permalink.cmp(&b.permalink)); - taxonomies.push(terms); + let library = self.library.read().unwrap(); + let all_sitemap_entries = sitemap::find_entries( + &library, + &self.taxonomies[..], + &self.config, + ); + let sitemap_limit = 30000; + + if all_sitemap_entries.len() < sitemap_limit { + // Create single sitemap + let mut context = Context::new(); + context.insert("entries", &all_sitemap_entries); + let sitemap = &render_template("sitemap.xml", &self.tera, context, &self.config.theme)?; + create_file(&self.output_path.join("sitemap.xml"), sitemap)?; + return Ok(()); } - context.insert("taxonomies", &taxonomies); - context.insert("config", &self.config); - - let sitemap = &render_template("sitemap.xml", &self.tera, &context, &self.config.theme)?; - + // Create multiple sitemaps (max 30000 urls each) + let mut sitemap_index = Vec::new(); + for (i, chunk) in + all_sitemap_entries.iter().collect::>().chunks(sitemap_limit).enumerate() + { + let mut context = Context::new(); + context.insert("entries", &chunk); + let sitemap = &render_template("sitemap.xml", &self.tera, context, &self.config.theme)?; + let file_name = format!("sitemap{}.xml", i + 1); + create_file(&self.output_path.join(&file_name), sitemap)?; + let mut sitemap_url: String = self.config.make_permalink(&file_name); + sitemap_url.pop(); // Remove trailing slash + sitemap_index.push(sitemap_url); + } + // Create main sitemap that reference numbered sitemaps + let mut main_context = Context::new(); + main_context.insert("sitemaps", &sitemap_index); + let sitemap = &render_template( + "split_sitemap_index.xml", + &self.tera, + main_context, + &self.config.theme, + )?; create_file(&self.output_path.join("sitemap.xml"), sitemap)?; Ok(()) @@ -800,12 +845,13 @@ impl Site { pages.par_sort_unstable_by(sort_actual_pages_by_date); context.insert("last_build_date", &pages[0].meta.date.clone()); + let library = self.library.read().unwrap(); // limit to the last n elements if the limit is set; otherwise use all. - let num_entries = self.config.rss_limit.unwrap_or(pages.len()); + let num_entries = self.config.rss_limit.unwrap_or_else(|| pages.len()); let p = pages .iter() .take(num_entries) - .map(|x| x.to_serialized_basic(&self.library)) + .map(|x| x.to_serialized_basic(&library)) .collect::>(); context.insert("pages", &p); @@ -819,7 +865,7 @@ impl Site { context.insert("feed_url", &rss_feed_url); - let feed = &render_template("rss.xml", &self.tera, &context, &self.config.theme)?; + let feed = &render_template("rss.xml", &self.tera, context, &self.config.theme)?; if let Some(ref base) = base_path { let mut output_path = self.output_path.clone(); @@ -840,6 +886,14 @@ impl Site { pub fn render_section(&self, section: &Section, render_pages: bool) -> Result<()> { ensure_directory_exists(&self.output_path)?; let mut output_path = self.output_path.clone(); + + if section.lang != self.config.default_language { + output_path.push(§ion.lang); + if !output_path.exists() { + create_directory(&output_path)?; + } + } + for component in §ion.file.components { output_path.push(component); @@ -851,14 +905,19 @@ impl Site { // Copy any asset we found previously into the same directory as the index.html for asset in §ion.assets { let asset_path = asset.as_path(); - copy(&asset_path, &output_path.join(asset_path.file_name().unwrap()))?; + copy( + &asset_path, + &output_path.join( + asset_path.file_name().expect("Failed to get asset filename for section"), + ), + )?; } if render_pages { section .pages .par_iter() - .map(|k| self.render_page(self.library.get_page_by_key(*k))) + .map(|k| self.render_page(self.library.read().unwrap().get_page_by_key(*k))) .collect::>()?; } @@ -876,9 +935,13 @@ impl Site { } if section.meta.is_paginated() { - self.render_paginated(&output_path, &Paginator::from_section(§ion, &self.library))?; + self.render_paginated( + &output_path, + &Paginator::from_section(§ion, &self.library.read().unwrap()), + )?; } else { - let output = section.render_html(&self.tera, &self.config, &self.library)?; + let output = + section.render_html(&self.tera, &self.config, &self.library.read().unwrap())?; create_file(&output_path.join("index.html"), &self.inject_livereload(output))?; } @@ -888,7 +951,12 @@ impl Site { /// Used only on reload pub fn render_index(&self) -> Result<()> { self.render_section( - &self.library.get_section(&self.content_path.join("_index.md")).unwrap(), + &self + .library + .read() + .unwrap() + .get_section(&self.content_path.join("_index.md")) + .expect("Failed to get index section"), false, ) } @@ -896,6 +964,8 @@ impl Site { /// Renders all sections pub fn render_sections(&self) -> Result<()> { self.library + .read() + .unwrap() .sections_values() .into_par_iter() .map(|s| self.render_section(s, true)) @@ -905,8 +975,8 @@ impl Site { /// Renders all pages that do not belong to any sections pub fn render_orphan_pages(&self) -> Result<()> { ensure_directory_exists(&self.output_path)?; - - for page in self.get_all_orphan_pages() { + let library = self.library.read().unwrap(); + for page in library.get_all_orphan_pages() { self.render_page(page)?; } @@ -926,8 +996,12 @@ impl Site { .map(|pager| { let page_path = folder_path.join(&format!("{}", pager.index)); create_directory(&page_path)?; - let output = - paginator.render_pager(pager, &self.config, &self.tera, &self.library)?; + let output = paginator.render_pager( + pager, + &self.config, + &self.tera, + &self.library.read().unwrap(), + )?; if pager.index > 1 { create_file(&page_path.join("index.html"), &self.inject_livereload(output))?; } else { diff --git a/components/site/src/sitemap.rs b/components/site/src/sitemap.rs new file mode 100644 index 0000000..c38ed96 --- /dev/null +++ b/components/site/src/sitemap.rs @@ -0,0 +1,127 @@ +use std::borrow::Cow; +use std::hash::{Hash, Hasher}; +use std::collections::{HashSet}; + +use tera::{Map, Value}; +use config::{Config}; +use library::{Library, Taxonomy}; + +/// The sitemap only needs links, potentially date and extra for pages in case of updates +/// for examples so we trim down all entries to only that +#[derive(Debug, Serialize)] +pub struct SitemapEntry<'a> { + permalink: Cow<'a, str>, + date: Option, + extra: Option<&'a Map>, +} + +// Hash/Eq is not implemented for tera::Map but in our case we only care about the permalink +// when comparing/hashing so we implement it manually +impl<'a> Hash for SitemapEntry<'a> { + fn hash(&self, state: &mut H) { + self.permalink.hash(state); + } +} +impl<'a> PartialEq for SitemapEntry<'a> { + fn eq(&self, other: &SitemapEntry) -> bool { + self.permalink == other.permalink + } +} +impl<'a> Eq for SitemapEntry<'a> {} + +impl<'a> SitemapEntry<'a> { + pub fn new(permalink: Cow<'a, str>, date: Option) -> Self { + SitemapEntry { permalink, date, extra: None } + } + + pub fn add_extra(&mut self, extra: &'a Map) { + self.extra = Some(extra); + } +} + +/// Finds out all the links to put in a sitemap from the pages/sections/taxonomies +/// There are no duplicate permalinks in the output vec +pub fn find_entries<'a>(library: &'a Library, taxonomies: &'a [Taxonomy], config: &'a Config) -> Vec> { + let pages = library + .pages_values() + .iter() + .filter(|p| !p.is_draft()) + .map(|p| { + let date = match p.meta.date { + Some(ref d) => Some(d.to_string()), + None => None, + }; + let mut entry = SitemapEntry::new(Cow::Borrowed(&p.permalink), date); + entry.add_extra(&p.meta.extra); + entry + }) + .collect::>(); + + let mut sections = library + .sections_values() + .iter() + .filter(|s| s.meta.render) + .map(|s| SitemapEntry::new(Cow::Borrowed(&s.permalink), None)) + .collect::>(); + + for section in library + .sections_values() + .iter() + .filter(|s| s.meta.paginate_by.is_some()) + { + let number_pagers = (section.pages.len() as f64 + / section.meta.paginate_by.unwrap() as f64) + .ceil() as isize; + for i in 1..=number_pagers { + let permalink = + format!("{}{}/{}/", section.permalink, section.meta.paginate_path, i); + sections.push(SitemapEntry::new(Cow::Owned(permalink), None)) + } + } + + let mut taxonomies_entries = vec![]; + for taxonomy in taxonomies { + let name = &taxonomy.kind.name; + let mut terms = vec![]; + terms.push(SitemapEntry::new(Cow::Owned(config.make_permalink(name)), None)); + for item in &taxonomy.items { + terms.push(SitemapEntry::new( + Cow::Owned(config.make_permalink(&format!("{}/{}", name, item.slug))), + None, + )); + + if taxonomy.kind.is_paginated() { + let number_pagers = (item.pages.len() as f64 + / taxonomy.kind.paginate_by.unwrap() as f64) + .ceil() as isize; + for i in 1..=number_pagers { + let permalink = config.make_permalink(&format!( + "{}/{}/{}/{}", + name, + item.slug, + taxonomy.kind.paginate_path(), + i + )); + terms.push(SitemapEntry::new(Cow::Owned(permalink), None)) + } + } + } + + taxonomies_entries.push(terms); + } + + let mut all_sitemap_entries = HashSet::new(); + for p in pages { + all_sitemap_entries.insert(p); + } + for s in sections { + all_sitemap_entries.insert(s); + } + for terms in taxonomies_entries { + for term in terms { + all_sitemap_entries.insert(term); + } + } + + all_sitemap_entries.into_iter().collect::>() +} diff --git a/components/site/tests/common.rs b/components/site/tests/common.rs new file mode 100644 index 0000000..77d5de8 --- /dev/null +++ b/components/site/tests/common.rs @@ -0,0 +1,69 @@ +extern crate site; +extern crate tempfile; + +use std::env; +use std::path::PathBuf; + +use self::site::Site; +use self::tempfile::{tempdir, TempDir}; + +// 2 helper macros to make all the build testing more bearable +#[macro_export] +macro_rules! file_exists { + ($root: expr, $path: expr) => {{ + let mut path = $root.clone(); + for component in $path.split("/") { + path = path.join(component); + } + std::path::Path::new(&path).exists() + }}; +} + +#[macro_export] +macro_rules! file_contains { + ($root: expr, $path: expr, $text: expr) => {{ + use std::io::prelude::*; + let mut path = $root.clone(); + for component in $path.split("/") { + path = path.join(component); + } + let mut file = std::fs::File::open(&path).expect(&format!("Failed to open {:?}", $path)); + let mut s = String::new(); + file.read_to_string(&mut s).unwrap(); + println!("{}", s); + s.contains($text) + }}; +} + +/// We return the tmpdir otherwise it would get out of scope and be deleted +/// The tests can ignore it if they dont need it by prefixing it with a `_` +pub fn build_site(name: &str) -> (Site, TempDir, PathBuf) { + let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf(); + path.push(name); + let mut site = Site::new(&path, "config.toml").unwrap(); + site.load().unwrap(); + let tmp_dir = tempdir().expect("create temp dir"); + let public = &tmp_dir.path().join("public"); + site.set_output_path(&public); + site.build().expect("Couldn't build the site"); + (site, tmp_dir, public.clone()) +} + +/// Same as `build_site` but has a hook to setup some config options +pub fn build_site_with_setup(name: &str, mut setup_cb: F) -> (Site, TempDir, PathBuf) +where + F: FnMut(Site) -> (Site, bool), +{ + let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf(); + path.push(name); + let site = Site::new(&path, "config.toml").unwrap(); + let (mut site, needs_loading) = setup_cb(site); + if needs_loading { + site.load().unwrap(); + } + let tmp_dir = tempdir().expect("create temp dir"); + let public = &tmp_dir.path().join("public"); + site.set_output_path(&public); + site.build().expect("Couldn't build the site"); + (site, tmp_dir, public.clone()) +} diff --git a/components/site/tests/site.rs b/components/site/tests/site.rs index 9d813dd..9286b43 100644 --- a/components/site/tests/site.rs +++ b/components/site/tests/site.rs @@ -1,16 +1,14 @@ extern crate config; extern crate site; -extern crate tempfile; +mod common; use std::collections::HashMap; use std::env; -use std::fs::File; -use std::io::prelude::*; use std::path::Path; +use common::{build_site, build_site_with_setup}; use config::Taxonomy; use site::Site; -use tempfile::tempdir; #[test] fn can_parse_site() { @@ -18,59 +16,59 @@ fn can_parse_site() { path.push("test_site"); let mut site = Site::new(&path, "config.toml").unwrap(); site.load().unwrap(); + let library = site.library.read().unwrap(); // Correct number of pages (sections do not count as pages) - assert_eq!(site.library.pages().len(), 22); + assert_eq!(library.pages().len(), 22); let posts_path = path.join("content").join("posts"); // Make sure the page with a url doesn't have any sections - let url_post = site.library.get_page(&posts_path.join("fixed-url.md")).unwrap(); + let url_post = library.get_page(&posts_path.join("fixed-url.md")).unwrap(); assert_eq!(url_post.path, "a-fixed-url/"); // Make sure the article in a folder with only asset doesn't get counted as a section let asset_folder_post = - site.library.get_page(&posts_path.join("with-assets").join("index.md")).unwrap(); + library.get_page(&posts_path.join("with-assets").join("index.md")).unwrap(); assert_eq!(asset_folder_post.file.components, vec!["posts".to_string()]); // That we have the right number of sections - assert_eq!(site.library.sections().len(), 11); + assert_eq!(library.sections().len(), 11); // And that the sections are correct - let index_section = site.library.get_section(&path.join("content").join("_index.md")).unwrap(); + let index_section = library.get_section(&path.join("content").join("_index.md")).unwrap(); assert_eq!(index_section.subsections.len(), 4); assert_eq!(index_section.pages.len(), 1); assert!(index_section.ancestors.is_empty()); - let posts_section = site.library.get_section(&posts_path.join("_index.md")).unwrap(); + let posts_section = library.get_section(&posts_path.join("_index.md")).unwrap(); assert_eq!(posts_section.subsections.len(), 2); assert_eq!(posts_section.pages.len(), 10); assert_eq!( posts_section.ancestors, - vec![*site.library.get_section_key(&index_section.file.path).unwrap()] + vec![*library.get_section_key(&index_section.file.path).unwrap()] ); // Make sure we remove all the pwd + content from the sections - let basic = site.library.get_page(&posts_path.join("simple.md")).unwrap(); + let basic = library.get_page(&posts_path.join("simple.md")).unwrap(); assert_eq!(basic.file.components, vec!["posts".to_string()]); assert_eq!( basic.ancestors, vec![ - *site.library.get_section_key(&index_section.file.path).unwrap(), - *site.library.get_section_key(&posts_section.file.path).unwrap(), + *library.get_section_key(&index_section.file.path).unwrap(), + *library.get_section_key(&posts_section.file.path).unwrap(), ] ); let tutorials_section = - site.library.get_section(&posts_path.join("tutorials").join("_index.md")).unwrap(); + library.get_section(&posts_path.join("tutorials").join("_index.md")).unwrap(); assert_eq!(tutorials_section.subsections.len(), 2); - let sub1 = site.library.get_section_by_key(tutorials_section.subsections[0]); - let sub2 = site.library.get_section_by_key(tutorials_section.subsections[1]); + let sub1 = library.get_section_by_key(tutorials_section.subsections[0]); + let sub2 = library.get_section_by_key(tutorials_section.subsections[1]); assert_eq!(sub1.clone().meta.title.unwrap(), "Programming"); assert_eq!(sub2.clone().meta.title.unwrap(), "DevOps"); assert_eq!(tutorials_section.pages.len(), 0); - let devops_section = site - .library + let devops_section = library .get_section(&posts_path.join("tutorials").join("devops").join("_index.md")) .unwrap(); assert_eq!(devops_section.subsections.len(), 0); @@ -78,55 +76,22 @@ fn can_parse_site() { assert_eq!( devops_section.ancestors, vec![ - *site.library.get_section_key(&index_section.file.path).unwrap(), - *site.library.get_section_key(&posts_section.file.path).unwrap(), - *site.library.get_section_key(&tutorials_section.file.path).unwrap(), + *library.get_section_key(&index_section.file.path).unwrap(), + *library.get_section_key(&posts_section.file.path).unwrap(), + *library.get_section_key(&tutorials_section.file.path).unwrap(), ] ); - let prog_section = site - .library + let prog_section = library .get_section(&posts_path.join("tutorials").join("programming").join("_index.md")) .unwrap(); assert_eq!(prog_section.subsections.len(), 0); assert_eq!(prog_section.pages.len(), 2); } -// 2 helper macros to make all the build testing more bearable -macro_rules! file_exists { - ($root: expr, $path: expr) => {{ - let mut path = $root.clone(); - for component in $path.split("/") { - path = path.join(component); - } - Path::new(&path).exists() - }}; -} - -macro_rules! file_contains { - ($root: expr, $path: expr, $text: expr) => {{ - let mut path = $root.clone(); - for component in $path.split("/") { - path = path.join(component); - } - let mut file = File::open(&path).unwrap(); - let mut s = String::new(); - file.read_to_string(&mut s).unwrap(); - println!("{}", s); - s.contains($text) - }}; -} - #[test] fn can_build_site_without_live_reload() { - let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf(); - path.push("test_site"); - let mut site = Site::new(&path, "config.toml").unwrap(); - site.load().unwrap(); - let tmp_dir = tempdir().expect("create temp dir"); - let public = &tmp_dir.path().join("public"); - site.set_output_path(&public); - site.build().unwrap(); + let (_, _tmp_dir, public) = build_site("test_site"); assert!(&public.exists()); assert!(file_exists!(public, "index.html")); @@ -210,6 +175,8 @@ fn can_build_site_without_live_reload() { )); // Drafts are not in the sitemap assert!(!file_contains!(public, "sitemap.xml", "draft")); + // render: false sections are not in the sitemap either + assert!(!file_contains!(public, "sitemap.xml", "posts/2018/")); // robots.txt has been rendered from the template assert!(file_contains!(public, "robots.txt", "User-agent: zola")); @@ -222,17 +189,12 @@ fn can_build_site_without_live_reload() { #[test] fn can_build_site_with_live_reload() { - let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf(); - path.push("test_site"); - let mut site = Site::new(&path, "config.toml").unwrap(); - site.load().unwrap(); - let tmp_dir = tempdir().expect("create temp dir"); - let public = &tmp_dir.path().join("public"); - site.set_output_path(&public); - site.enable_live_reload(1000); - site.build().unwrap(); + let (_, _tmp_dir, public) = build_site_with_setup("test_site", |mut site| { + site.enable_live_reload(1000); + (site, true) + }); - assert!(Path::new(&public).exists()); + assert!(&public.exists()); assert!(file_exists!(public, "index.html")); assert!(file_exists!(public, "sitemap.xml")); @@ -271,28 +233,26 @@ fn can_build_site_with_live_reload() { #[test] fn can_build_site_with_taxonomies() { - let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf(); - path.push("test_site"); - let mut site = Site::new(&path, "config.toml").unwrap(); - site.load().unwrap(); - - for (i, (_, page)) in site.library.pages_mut().iter_mut().enumerate() { - page.meta.taxonomies = { - let mut taxonomies = HashMap::new(); - taxonomies.insert( - "categories".to_string(), - vec![if i % 2 == 0 { "A" } else { "B" }.to_string()], - ); - taxonomies - }; - } - site.populate_taxonomies().unwrap(); - let tmp_dir = tempdir().expect("create temp dir"); - let public = &tmp_dir.path().join("public"); - site.set_output_path(&public); - site.build().unwrap(); + let (site, _tmp_dir, public) = build_site_with_setup("test_site", |mut site| { + site.load().unwrap(); + { + let mut library = site.library.write().unwrap(); + for (i, (_, page)) in library.pages_mut().iter_mut().enumerate() { + page.meta.taxonomies = { + let mut taxonomies = HashMap::new(); + taxonomies.insert( + "categories".to_string(), + vec![if i % 2 == 0 { "A" } else { "B" }.to_string()], + ); + taxonomies + }; + } + } + site.populate_taxonomies().unwrap(); + (site, false) + }); - assert!(Path::new(&public).exists()); + assert!(&public.exists()); assert_eq!(site.taxonomies.len(), 1); assert!(file_exists!(public, "index.html")); @@ -340,15 +300,7 @@ fn can_build_site_with_taxonomies() { #[test] fn can_build_site_and_insert_anchor_links() { - let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf(); - path.push("test_site"); - let mut site = Site::new(&path, "config.toml").unwrap(); - site.load().unwrap(); - - let tmp_dir = tempdir().expect("create temp dir"); - let public = &tmp_dir.path().join("public"); - site.set_output_path(&public); - site.build().unwrap(); + let (_, _tmp_dir, public) = build_site("test_site"); assert!(Path::new(&public).exists()); // anchor link inserted @@ -361,23 +313,22 @@ fn can_build_site_and_insert_anchor_links() { #[test] fn can_build_site_with_pagination_for_section() { - let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf(); - path.push("test_site"); - let mut site = Site::new(&path, "config.toml").unwrap(); - site.load().unwrap(); - for (_, section) in site.library.sections_mut() { - if section.is_index() { - continue; + let (_, _tmp_dir, public) = build_site_with_setup("test_site", |mut site| { + site.load().unwrap(); + { + let mut library = site.library.write().unwrap(); + for (_, section) in library.sections_mut() { + if section.is_index() { + continue; + } + section.meta.paginate_by = Some(2); + section.meta.template = Some("section_paginated.html".to_string()); + } } - section.meta.paginate_by = Some(2); - section.meta.template = Some("section_paginated.html".to_string()); - } - let tmp_dir = tempdir().expect("create temp dir"); - let public = &tmp_dir.path().join("public"); - site.set_output_path(&public); - site.build().unwrap(); + (site, false) + }); - assert!(Path::new(&public).exists()); + assert!(&public.exists()); assert!(file_exists!(public, "index.html")); assert!(file_exists!(public, "sitemap.xml")); @@ -478,21 +429,22 @@ fn can_build_site_with_pagination_for_section() { #[test] fn can_build_site_with_pagination_for_index() { - let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf(); - path.push("test_site"); - let mut site = Site::new(&path, "config.toml").unwrap(); - site.load().unwrap(); - { - let index = site.library.get_section_mut(&path.join("content").join("_index.md")).unwrap(); - index.meta.paginate_by = Some(2); - index.meta.template = Some("index_paginated.html".to_string()); - } - let tmp_dir = tempdir().expect("create temp dir"); - let public = &tmp_dir.path().join("public"); - site.set_output_path(&public); - site.build().unwrap(); + let (_, _tmp_dir, public) = build_site_with_setup("test_site", |mut site| { + site.load().unwrap(); + { + let mut library = site.library.write().unwrap(); + { + let index = library + .get_section_mut(&site.base_path.join("content").join("_index.md")) + .unwrap(); + index.meta.paginate_by = Some(2); + index.meta.template = Some("index_paginated.html".to_string()); + } + } + (site, false) + }); - assert!(Path::new(&public).exists()); + assert!(&public.exists()); assert!(file_exists!(public, "index.html")); assert!(file_exists!(public, "sitemap.xml")); @@ -530,33 +482,34 @@ fn can_build_site_with_pagination_for_index() { #[test] fn can_build_site_with_pagination_for_taxonomy() { - let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf(); - path.push("test_site"); - let mut site = Site::new(&path, "config.toml").unwrap(); - site.config.taxonomies.push(Taxonomy { - name: "tags".to_string(), - paginate_by: Some(2), - paginate_path: None, - rss: true, + let (_, _tmp_dir, public) = build_site_with_setup("test_site", |mut site| { + site.config.taxonomies.push(Taxonomy { + name: "tags".to_string(), + paginate_by: Some(2), + paginate_path: None, + rss: true, + lang: site.config.default_language.clone(), + }); + site.load().unwrap(); + { + let mut library = site.library.write().unwrap(); + + for (i, (_, page)) in library.pages_mut().iter_mut().enumerate() { + page.meta.taxonomies = { + let mut taxonomies = HashMap::new(); + taxonomies.insert( + "tags".to_string(), + vec![if i % 2 == 0 { "A" } else { "B" }.to_string()], + ); + taxonomies + }; + } + } + site.populate_taxonomies().unwrap(); + (site, false) }); - site.load().unwrap(); - for (i, (_, page)) in site.library.pages_mut().iter_mut().enumerate() { - page.meta.taxonomies = { - let mut taxonomies = HashMap::new(); - taxonomies - .insert("tags".to_string(), vec![if i % 2 == 0 { "A" } else { "B" }.to_string()]); - taxonomies - }; - } - site.populate_taxonomies().unwrap(); - - let tmp_dir = tempdir().expect("create temp dir"); - let public = &tmp_dir.path().join("public"); - site.set_output_path(&public); - site.build().unwrap(); - - assert!(Path::new(&public).exists()); + assert!(&public.exists()); assert!(file_exists!(public, "index.html")); assert!(file_exists!(public, "sitemap.xml")); @@ -610,16 +563,9 @@ fn can_build_site_with_pagination_for_taxonomy() { #[test] fn can_build_rss_feed() { - let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf(); - path.push("test_site"); - let mut site = Site::new(&path, "config.toml").unwrap(); - site.load().unwrap(); - let tmp_dir = tempdir().expect("create temp dir"); - let public = &tmp_dir.path().join("public"); - site.set_output_path(&public); - site.build().unwrap(); + let (_, _tmp_dir, public) = build_site("test_site"); - assert!(Path::new(&public).exists()); + assert!(&public.exists()); assert!(file_exists!(public, "rss.xml")); // latest article is posts/extra-syntax.md assert!(file_contains!(public, "rss.xml", "Extra Syntax")); @@ -629,15 +575,10 @@ fn can_build_rss_feed() { #[test] fn can_build_search_index() { - let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf(); - path.push("test_site"); - let mut site = Site::new(&path, "config.toml").unwrap(); - site.load().unwrap(); - site.config.build_search_index = true; - let tmp_dir = tempdir().expect("create temp dir"); - let public = &tmp_dir.path().join("public"); - site.set_output_path(&public); - site.build().unwrap(); + let (_, _tmp_dir, public) = build_site_with_setup("test_site", |mut site| { + site.config.build_search_index = true; + (site, true) + }); assert!(Path::new(&public).exists()); assert!(file_exists!(public, "elasticlunr.min.js")); @@ -646,14 +587,7 @@ fn can_build_search_index() { #[test] fn can_build_with_extra_syntaxes() { - let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf(); - path.push("test_site"); - let mut site = Site::new(&path, "config.toml").unwrap(); - site.load().unwrap(); - let tmp_dir = tempdir().expect("create temp dir"); - let public = &tmp_dir.path().join("public"); - site.set_output_path(&public); - site.build().unwrap(); + let (_, _tmp_dir, public) = build_site("test_site"); assert!(&public.exists()); assert!(file_exists!(public, "posts/extra-syntax/index.html")); @@ -672,38 +606,48 @@ fn can_apply_page_templates() { site.load().unwrap(); let template_path = path.join("content").join("applying_page_template"); + let library = site.library.read().unwrap(); - let template_section = site.library.get_section(&template_path.join("_index.md")).unwrap(); + let template_section = library.get_section(&template_path.join("_index.md")).unwrap(); assert_eq!(template_section.subsections.len(), 2); assert_eq!(template_section.pages.len(), 2); - let from_section_config = site.library.get_page_by_key(template_section.pages[0]); + let from_section_config = library.get_page_by_key(template_section.pages[0]); assert_eq!(from_section_config.meta.template, Some("page_template.html".into())); assert_eq!(from_section_config.meta.title, Some("From section config".into())); - let override_page_template = site.library.get_page_by_key(template_section.pages[1]); + let override_page_template = library.get_page_by_key(template_section.pages[1]); assert_eq!(override_page_template.meta.template, Some("page_template_override.html".into())); assert_eq!(override_page_template.meta.title, Some("Override".into())); // It should have applied recursively as well let another_section = - site.library.get_section(&template_path.join("another_section").join("_index.md")).unwrap(); + library.get_section(&template_path.join("another_section").join("_index.md")).unwrap(); assert_eq!(another_section.subsections.len(), 0); assert_eq!(another_section.pages.len(), 1); - let changed_recursively = site.library.get_page_by_key(another_section.pages[0]); + let changed_recursively = library.get_page_by_key(another_section.pages[0]); assert_eq!(changed_recursively.meta.template, Some("page_template.html".into())); assert_eq!(changed_recursively.meta.title, Some("Changed recursively".into())); // But it should not have override a children page_template - let yet_another_section = site - .library - .get_section(&template_path.join("yet_another_section").join("_index.md")) - .unwrap(); + let yet_another_section = + library.get_section(&template_path.join("yet_another_section").join("_index.md")).unwrap(); assert_eq!(yet_another_section.subsections.len(), 0); assert_eq!(yet_another_section.pages.len(), 1); - let child = site.library.get_page_by_key(yet_another_section.pages[0]); + let child = library.get_page_by_key(yet_another_section.pages[0]); assert_eq!(child.meta.template, Some("page_template_child.html".into())); assert_eq!(child.meta.title, Some("Local section override".into())); } + +// https://github.com/getzola/zola/issues/571 +#[test] +fn can_build_site_custom_builtins_from_theme() { + let (_, _tmp_dir, public) = build_site("test_site"); + + assert!(&public.exists()); + // 404.html is a theme template. + assert!(file_exists!(public, "404.html")); + assert!(file_contains!(public, "404.html", "Oops")); +} diff --git a/components/site/tests/site_i18n.rs b/components/site/tests/site_i18n.rs new file mode 100644 index 0000000..2f81c7c --- /dev/null +++ b/components/site/tests/site_i18n.rs @@ -0,0 +1,141 @@ +extern crate site; +mod common; + +use std::env; + +use common::build_site; +use site::Site; + +#[test] +fn can_parse_multilingual_site() { + let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf(); + path.push("test_site_i18n"); + let mut site = Site::new(&path, "config.toml").unwrap(); + site.load().unwrap(); + + let library = site.library.read().unwrap(); + assert_eq!(library.pages().len(), 10); + assert_eq!(library.sections().len(), 6); + + // default index sections + let default_index_section = + library.get_section(&path.join("content").join("_index.md")).unwrap(); + assert_eq!(default_index_section.pages.len(), 1); + assert!(default_index_section.ancestors.is_empty()); + + let fr_index_section = library.get_section(&path.join("content").join("_index.fr.md")).unwrap(); + assert_eq!(fr_index_section.pages.len(), 1); + assert!(fr_index_section.ancestors.is_empty()); + + // blog sections get only their own language pages + let blog_path = path.join("content").join("blog"); + + let default_blog = library.get_section(&blog_path.join("_index.md")).unwrap(); + assert_eq!(default_blog.subsections.len(), 0); + assert_eq!(default_blog.pages.len(), 4); + assert_eq!( + default_blog.ancestors, + vec![*library.get_section_key(&default_index_section.file.path).unwrap()] + ); + for key in &default_blog.pages { + let page = library.get_page_by_key(*key); + assert_eq!(page.lang, "en"); + } + + let fr_blog = library.get_section(&blog_path.join("_index.fr.md")).unwrap(); + assert_eq!(fr_blog.subsections.len(), 0); + assert_eq!(fr_blog.pages.len(), 3); + assert_eq!( + fr_blog.ancestors, + vec![*library.get_section_key(&fr_index_section.file.path).unwrap()] + ); + for key in &fr_blog.pages { + let page = library.get_page_by_key(*key); + assert_eq!(page.lang, "fr"); + } +} + +#[test] +fn can_build_multilingual_site() { + let (_, _tmp_dir, public) = build_site("test_site_i18n"); + + assert!(public.exists()); + + // Index pages + assert!(file_exists!(public, "index.html")); + assert!(file_exists!(public, "fr/index.html")); + assert!(file_contains!(public, "fr/index.html", "Une page")); + assert!(file_contains!(public, "fr/index.html", "Language: fr")); + + assert!(file_exists!(public, "base/index.html")); + assert!(file_exists!(public, "fr/base/index.html")); + + // Sections are there as well, with translations info + assert!(file_exists!(public, "blog/index.html")); + assert!(file_contains!( + public, + "blog/index.html", + "Translated in fr: Mon blog https://example.com/fr/blog/" + )); + assert!(file_contains!( + public, + "blog/index.html", + "Translated in it: Il mio blog https://example.com/it/blog/" + )); + assert!(file_exists!(public, "fr/blog/index.html")); + assert!(file_contains!(public, "fr/blog/index.html", "Language: fr")); + assert!(file_contains!( + public, + "fr/blog/index.html", + "Translated in en: My blog https://example.com/blog/" + )); + assert!(file_contains!( + public, + "fr/blog/index.html", + "Translated in it: Il mio blog https://example.com/it/blog/" + )); + + // Normal pages are there with the translations + assert!(file_exists!(public, "blog/something/index.html")); + assert!(file_contains!( + public, + "blog/something/index.html", + "Translated in fr: Quelque chose https://example.com/fr/blog/something/" + )); + assert!(file_exists!(public, "fr/blog/something/index.html")); + assert!(file_contains!(public, "fr/blog/something/index.html", "Language: fr")); + assert!(file_contains!( + public, + "fr/blog/something/index.html", + "Translated in en: Something https://example.com/blog/something/" + )); + + // sitemap contains all languages + assert!(file_exists!(public, "sitemap.xml")); + assert!(file_contains!(public, "sitemap.xml", "https://example.com/blog/something-else/")); + assert!(file_contains!(public, "sitemap.xml", "https://example.com/fr/blog/something-else/")); + assert!(file_contains!(public, "sitemap.xml", "https://example.com/it/blog/something-else/")); + + // one rss per language + assert!(file_exists!(public, "rss.xml")); + assert!(file_contains!(public, "rss.xml", "https://example.com/blog/something-else/")); + assert!(!file_contains!(public, "rss.xml", "https://example.com/fr/blog/something-else/")); + assert!(file_exists!(public, "fr/rss.xml")); + assert!(!file_contains!(public, "fr/rss.xml", "https://example.com/blog/something-else/")); + assert!(file_contains!(public, "fr/rss.xml", "https://example.com/fr/blog/something-else/")); + // Italian doesn't have RSS enabled + assert!(!file_exists!(public, "it/rss.xml")); + + // Taxonomies are per-language + assert!(file_exists!(public, "authors/index.html")); + assert!(file_contains!(public, "authors/index.html", "Queen")); + assert!(!file_contains!(public, "authors/index.html", "Vincent")); + assert!(!file_exists!(public, "auteurs/index.html")); + assert!(file_exists!(public, "authors/queen-elizabeth/rss.xml")); + + assert!(!file_exists!(public, "fr/authors/index.html")); + assert!(file_exists!(public, "fr/auteurs/index.html")); + assert!(!file_contains!(public, "fr/auteurs/index.html", "Queen")); + assert!(file_contains!(public, "fr/auteurs/index.html", "Vincent")); + assert!(!file_exists!(public, "fr/auteurs/vincent-prouillet/rss.xml")); +} diff --git a/components/templates/Cargo.toml b/components/templates/Cargo.toml index c3001f4..4f95aac 100644 --- a/components/templates/Cargo.toml +++ b/components/templates/Cargo.toml @@ -4,14 +4,13 @@ version = "0.1.0" authors = ["Vincent Prouillet "] [dependencies] -tera = "0.11" +tera = "1.0.0-alpha.3" base64 = "0.10" lazy_static = "1" pulldown-cmark = "0.2" toml = "0.4" csv = "1" serde_json = "1.0" -error-chain = "0.12" reqwest = "0.9" url = "1.5" diff --git a/components/templates/src/builtins/robots.txt b/components/templates/src/builtins/robots.txt index 7d329b1..451ba12 100644 --- a/components/templates/src/builtins/robots.txt +++ b/components/templates/src/builtins/robots.txt @@ -1 +1,2 @@ User-agent: * +Sitemap: {{ get_url(path="sitemap.xml") }} diff --git a/components/templates/src/builtins/sitemap.xml b/components/templates/src/builtins/sitemap.xml index 6eba3d7..6d55d37 100644 --- a/components/templates/src/builtins/sitemap.xml +++ b/components/templates/src/builtins/sitemap.xml @@ -1,22 +1,10 @@ - {% for page in pages %} + {% for sitemap_entry in entries %} - {{ page.permalink | safe }} - {% if page.date %} - {{ page.date }} + {{ sitemap_entry.permalink | safe }} + {% if sitemap_entry.date %} + {{ sitemap_entry.date }} {% endif %} {% endfor %} - {% for section in sections %} - - {{ section.permalink | safe }} - - {% endfor %} - {% for taxonomy in taxonomies %} - {% for entry in taxonomy %} - - {{ entry.permalink | safe }} - - {% endfor %} - {% endfor %} diff --git a/components/templates/src/builtins/split_sitemap_index.xml b/components/templates/src/builtins/split_sitemap_index.xml new file mode 100644 index 0000000..1b883e4 --- /dev/null +++ b/components/templates/src/builtins/split_sitemap_index.xml @@ -0,0 +1,7 @@ + + {% for sitemap in sitemaps %} + + {{ sitemap }} + + {% endfor %} + \ No newline at end of file diff --git a/components/templates/src/filters.rs b/components/templates/src/filters.rs index 901c0f6..133feb8 100644 --- a/components/templates/src/filters.rs +++ b/components/templates/src/filters.rs @@ -4,7 +4,7 @@ use base64::{decode, encode}; use pulldown_cmark as cmark; use tera::{to_value, Result as TeraResult, Value}; -pub fn markdown(value: Value, args: HashMap) -> TeraResult { +pub fn markdown(value: &Value, args: &HashMap) -> TeraResult { let s = try_get_value!("markdown", "value", String, value); let inline = match args.get("inline") { Some(val) => try_get_value!("markdown", "inline", bool, val), @@ -21,21 +21,21 @@ pub fn markdown(value: Value, args: HashMap) -> TeraResult if inline { html = html - .trim_left_matches("

") + .trim_start_matches("

") // pulldown_cmark finishes a paragraph with `

\n` - .trim_right_matches("

\n") + .trim_end_matches("

\n") .to_string(); } Ok(to_value(&html).unwrap()) } -pub fn base64_encode(value: Value, _: HashMap) -> TeraResult { +pub fn base64_encode(value: &Value, _: &HashMap) -> TeraResult { let s = try_get_value!("base64_encode", "value", String, value); Ok(to_value(&encode(s.as_bytes())).unwrap()) } -pub fn base64_decode(value: Value, _: HashMap) -> TeraResult { +pub fn base64_decode(value: &Value, _: &HashMap) -> TeraResult { let s = try_get_value!("base64_decode", "value", String, value); Ok(to_value(&String::from_utf8(decode(s.as_bytes()).unwrap()).unwrap()).unwrap()) } @@ -50,7 +50,7 @@ mod tests { #[test] fn markdown_filter() { - let result = markdown(to_value(&"# Hey").unwrap(), HashMap::new()); + let result = markdown(&to_value(&"# Hey").unwrap(), &HashMap::new()); assert!(result.is_ok()); assert_eq!(result.unwrap(), to_value(&"

Hey

\n").unwrap()); } @@ -60,8 +60,8 @@ mod tests { let mut args = HashMap::new(); args.insert("inline".to_string(), to_value(true).unwrap()); let result = markdown( - to_value(&"Using `map`, `filter`, and `fold` instead of `for`").unwrap(), - args, + &to_value(&"Using `map`, `filter`, and `fold` instead of `for`").unwrap(), + &args, ); assert!(result.is_ok()); assert_eq!(result.unwrap(), to_value(&"Using map, filter, and fold instead of for").unwrap()); @@ -73,7 +73,7 @@ mod tests { let mut args = HashMap::new(); args.insert("inline".to_string(), to_value(true).unwrap()); let result = markdown( - to_value( + &to_value( &r#" |id|author_id| timestamp_created|title |content | |-:|--------:|-----------------------:|:---------------------|:-----------------| @@ -82,7 +82,7 @@ mod tests { "#, ) .unwrap(), - args, + &args, ); assert!(result.is_ok()); assert!(result.unwrap().as_str().unwrap().contains("")); @@ -102,7 +102,7 @@ mod tests { ]; for (input, expected) in tests { let args = HashMap::new(); - let result = base64_encode(to_value(input).unwrap(), args); + let result = base64_encode(&to_value(input).unwrap(), &args); assert!(result.is_ok()); assert_eq!(result.unwrap(), to_value(expected).unwrap()); } @@ -121,7 +121,7 @@ mod tests { ]; for (input, expected) in tests { let args = HashMap::new(); - let result = base64_decode(to_value(input).unwrap(), args); + let result = base64_decode(&to_value(input).unwrap(), &args); assert!(result.is_ok()); assert_eq!(result.unwrap(), to_value(expected).unwrap()); } diff --git a/components/templates/src/global_fns/load_data.rs b/components/templates/src/global_fns/load_data.rs index 1161d01..b8f053c 100644 --- a/components/templates/src/global_fns/load_data.rs +++ b/components/templates/src/global_fns/load_data.rs @@ -16,7 +16,7 @@ use std::sync::{Arc, Mutex}; use csv::Reader; use std::collections::HashMap; -use tera::{from_value, to_value, Error, GlobalFn, Map, Result, Value}; +use tera::{from_value, to_value, Error, Function as TeraFn, Map, Result, Value}; static GET_DATA_ARGUMENT_ERROR_MESSAGE: &str = "`load_data`: requires EITHER a `path` or `url` argument"; @@ -50,24 +50,24 @@ impl FromStr for OutputFormat { type Err = Error; fn from_str(output_format: &str) -> Result { - return match output_format { + match output_format { "toml" => Ok(OutputFormat::Toml), "csv" => Ok(OutputFormat::Csv), "json" => Ok(OutputFormat::Json), "plain" => Ok(OutputFormat::Plain), format => Err(format!("Unknown output format {}", format).into()), - }; + } } } impl OutputFormat { fn as_accept_header(&self) -> header::HeaderValue { - return header::HeaderValue::from_static(match self { + header::HeaderValue::from_static(match self { OutputFormat::Json => "application/json", OutputFormat::Csv => "text/csv", OutputFormat::Toml => "application/toml", OutputFormat::Plain => "text/plain", - }); + }) } } @@ -91,18 +91,18 @@ impl DataSource { if let Some(url) = url_arg { return Url::parse(&url) - .map(|parsed_url| DataSource::Url(parsed_url)) + .map(DataSource::Url) .map_err(|e| format!("Failed to parse {} as url: {}", url, e).into()); } - return Err(GET_DATA_ARGUMENT_ERROR_MESSAGE.into()); + Err(GET_DATA_ARGUMENT_ERROR_MESSAGE.into()) } fn get_cache_key(&self, format: &OutputFormat) -> u64 { let mut hasher = DefaultHasher::new(); format.hash(&mut hasher); self.hash(&mut hasher); - return hasher.finish(); + hasher.finish() } } @@ -123,10 +123,9 @@ fn get_data_source_from_args( args: &HashMap, ) -> Result { let path_arg = optional_arg!(String, args.get("path"), GET_DATA_ARGUMENT_ERROR_MESSAGE); - let url_arg = optional_arg!(String, args.get("url"), GET_DATA_ARGUMENT_ERROR_MESSAGE); - return DataSource::from_args(path_arg, url_arg, content_path); + DataSource::from_args(path_arg, url_arg, content_path) } fn read_data_file(base_path: &PathBuf, full_path: PathBuf) -> Result { @@ -140,9 +139,9 @@ fn read_data_file(base_path: &PathBuf, full_path: PathBuf) -> Result { ) .into()); } - return read_file(&full_path).map_err(|e| { + read_file(&full_path).map_err(|e| { format!("`load_data`: error {} loading file {}", full_path.to_str().unwrap(), e).into() - }); + }) } fn get_output_format_from_args( @@ -152,47 +151,56 @@ fn get_output_format_from_args( let format_arg = optional_arg!( String, args.get("format"), - "`load_data`: `format` needs to be an argument with a string value, being one of the supported `load_data` file types (csv, json, toml)" + "`load_data`: `format` needs to be an argument with a string value, being one of the supported `load_data` file types (csv, json, toml, plain)" ); if let Some(format) = format_arg { + if format == "plain" { + return Ok(OutputFormat::Plain); + } return OutputFormat::from_str(&format); } let from_extension = if let DataSource::Path(path) = data_source { - let extension_result: Result<&str> = - path.extension().map(|extension| extension.to_str().unwrap()).ok_or( - format!("Could not determine format for {} from extension", path.display()).into(), - ); - extension_result? + path.extension().map(|extension| extension.to_str().unwrap()).unwrap_or_else(|| "plain") } else { "plain" }; - return OutputFormat::from_str(from_extension); + + // Always default to Plain if we don't know what it is + OutputFormat::from_str(from_extension).or_else(|_| Ok(OutputFormat::Plain)) } -/// A global function to load data from a file or from a URL +/// A Tera function to load data from a file or from a URL /// Currently the supported formats are json, toml, csv and plain text -pub fn make_load_data(content_path: PathBuf, base_path: PathBuf) -> GlobalFn { - let mut headers = header::HeaderMap::new(); - headers.insert(header::USER_AGENT, "zola".parse().unwrap()); - let client = Arc::new(Mutex::new(Client::builder().build().expect("reqwest client build"))); - let result_cache: Arc>> = Arc::new(Mutex::new(HashMap::new())); - Box::new(move |args| -> Result { - let data_source = get_data_source_from_args(&content_path, &args)?; +#[derive(Debug)] +pub struct LoadData { + base_path: PathBuf, + client: Arc>, + result_cache: Arc>>, +} +impl LoadData { + pub fn new(base_path: PathBuf) -> Self { + let client = Arc::new(Mutex::new(Client::builder().build().expect("reqwest client build"))); + let result_cache = Arc::new(Mutex::new(HashMap::new())); + Self { base_path, client, result_cache } + } +} +impl TeraFn for LoadData { + fn call(&self, args: &HashMap) -> Result { + let data_source = get_data_source_from_args(&self.base_path, &args)?; let file_format = get_output_format_from_args(&args, &data_source)?; - let cache_key = data_source.get_cache_key(&file_format); - let mut cache = result_cache.lock().expect("result cache lock"); - let response_client = client.lock().expect("response client lock"); + let mut cache = self.result_cache.lock().expect("result cache lock"); + let response_client = self.client.lock().expect("response client lock"); if let Some(cached_result) = cache.get(&cache_key) { return Ok(cached_result.clone()); } let data = match data_source { - DataSource::Path(path) => read_data_file(&base_path, path), + DataSource::Path(path) => read_data_file(&self.base_path, path), DataSource::Url(url) => { let mut response = response_client .get(url.as_str()) @@ -224,14 +232,14 @@ pub fn make_load_data(content_path: PathBuf, base_path: PathBuf) -> GlobalFn { } result_value - }) + } } /// Parse a JSON string and convert it to a Tera Value fn load_json(json_data: String) -> Result { let json_content: Value = serde_json::from_str(json_data.as_str()).map_err(|e| format!("{:?}", e))?; - return Ok(json_content); + Ok(json_content) } /// Parse a TOML string and convert it to a Tera Value @@ -283,7 +291,16 @@ fn load_csv(csv_data: String) -> Result { let mut records_array: Vec = Vec::new(); for result in records { - let record = result.unwrap(); + let record = match result { + Ok(r) => r, + Err(e) => { + return Err(tera::Error::chain( + String::from("Error encountered when parsing csv records"), + e, + )); + } + }; + let mut elements_array: Vec = Vec::new(); for e in record.into_iter() { @@ -302,12 +319,12 @@ fn load_csv(csv_data: String) -> Result { #[cfg(test)] mod tests { - use super::{make_load_data, DataSource, OutputFormat}; + use super::{DataSource, LoadData, OutputFormat}; use std::collections::HashMap; use std::path::PathBuf; - use tera::to_value; + use tera::{to_value, Function}; fn get_test_file(filename: &str) -> PathBuf { let test_files = PathBuf::from("../utils/test-files").canonicalize().unwrap(); @@ -316,27 +333,25 @@ mod tests { #[test] fn fails_when_missing_file() { - let static_fn = - make_load_data(PathBuf::from("../utils/test-files"), PathBuf::from("../utils")); + let static_fn = LoadData::new(PathBuf::from("../utils")); let mut args = HashMap::new(); args.insert("path".to_string(), to_value("../../../READMEE.md").unwrap()); - let result = static_fn(args); + let result = static_fn.call(&args); assert!(result.is_err()); - assert!(result.unwrap_err().description().contains("READMEE.md doesn't exist")); + assert!(result.unwrap_err().to_string().contains("READMEE.md doesn't exist")); } #[test] fn cant_load_outside_content_dir() { - let static_fn = - make_load_data(PathBuf::from("../utils/test-files"), PathBuf::from("../utils")); + let static_fn = LoadData::new(PathBuf::from(PathBuf::from("../utils"))); let mut args = HashMap::new(); - args.insert("path".to_string(), to_value("../../../README.md").unwrap()); + args.insert("path".to_string(), to_value("../../README.md").unwrap()); args.insert("format".to_string(), to_value("plain").unwrap()); - let result = static_fn(args); + let result = static_fn.call(&args); assert!(result.is_err()); assert!(result .unwrap_err() - .description() + .to_string() .contains("README.md is not inside the base site directory")); } @@ -378,11 +393,11 @@ mod tests { #[test] fn can_load_remote_data() { - let static_fn = make_load_data(PathBuf::new(), PathBuf::new()); + let static_fn = LoadData::new(PathBuf::new()); let mut args = HashMap::new(); args.insert("url".to_string(), to_value("https://httpbin.org/json").unwrap()); args.insert("format".to_string(), to_value("json").unwrap()); - let result = static_fn(args).unwrap(); + let result = static_fn.call(&args).unwrap(); assert_eq!( result.get("slideshow").unwrap().get("title").unwrap(), &to_value("Sample Slide Show").unwrap() @@ -391,29 +406,26 @@ mod tests { #[test] fn fails_when_request_404s() { - let static_fn = make_load_data(PathBuf::new(), PathBuf::new()); + let static_fn = LoadData::new(PathBuf::new()); let mut args = HashMap::new(); args.insert("url".to_string(), to_value("https://httpbin.org/status/404/").unwrap()); args.insert("format".to_string(), to_value("json").unwrap()); - let result = static_fn(args); + let result = static_fn.call(&args); assert!(result.is_err()); assert_eq!( - result.unwrap_err().description(), + result.unwrap_err().to_string(), "Failed to request https://httpbin.org/status/404/: 404 Not Found" ); } #[test] fn can_load_toml() { - let static_fn = make_load_data( - PathBuf::from("../utils/test-files"), - PathBuf::from("../utils/test-files"), - ); + let static_fn = LoadData::new(PathBuf::from("../utils/test-files")); let mut args = HashMap::new(); args.insert("path".to_string(), to_value("test.toml").unwrap()); - let result = static_fn(args.clone()).unwrap(); + let result = static_fn.call(&args.clone()).unwrap(); - //TOML does not load in order + // TOML does not load in order assert_eq!( result, json!({ @@ -426,14 +438,52 @@ mod tests { } #[test] - fn can_load_csv() { - let static_fn = make_load_data( - PathBuf::from("../utils/test-files"), - PathBuf::from("../utils/test-files"), + fn unknown_extension_defaults_to_plain() { + let static_fn = LoadData::new(PathBuf::from("../utils/test-files")); + let mut args = HashMap::new(); + args.insert("path".to_string(), to_value("test.css").unwrap()); + let result = static_fn.call(&args.clone()).unwrap(); + + assert_eq!( + result, + ".hello {}\n", ); + } + + #[test] + fn can_override_known_extension_with_format() { + let static_fn = LoadData::new(PathBuf::from("../utils/test-files")); let mut args = HashMap::new(); args.insert("path".to_string(), to_value("test.csv").unwrap()); - let result = static_fn(args.clone()).unwrap(); + args.insert("format".to_string(), to_value("plain").unwrap()); + let result = static_fn.call(&args.clone()).unwrap(); + + assert_eq!( + result, + "Number,Title\n1,Gutenberg\n2,Printing", + ); + } + + #[test] + fn will_use_format_on_unknown_extension() { + let static_fn = LoadData::new(PathBuf::from("../utils/test-files")); + let mut args = HashMap::new(); + args.insert("path".to_string(), to_value("test.css").unwrap()); + args.insert("format".to_string(), to_value("plain").unwrap()); + let result = static_fn.call(&args.clone()).unwrap(); + + assert_eq!( + result, + ".hello {}\n", + ); + } + + #[test] + fn can_load_csv() { + let static_fn = LoadData::new(PathBuf::from("../utils/test-files")); + let mut args = HashMap::new(); + args.insert("path".to_string(), to_value("test.csv").unwrap()); + let result = static_fn.call(&args.clone()).unwrap(); assert_eq!( result, @@ -447,15 +497,33 @@ mod tests { ) } + // Test points to bad csv file with uneven row lengths + #[test] + fn bad_csv_should_result_in_error() { + let static_fn = LoadData::new(PathBuf::from("../utils/test-files")); + let mut args = HashMap::new(); + args.insert("path".to_string(), to_value("uneven_rows.csv").unwrap()); + let result = static_fn.call(&args.clone()); + + assert!(result.is_err()); + + let error_kind = result.err().unwrap().kind; + match error_kind { + tera::ErrorKind::Msg(msg) => { + if msg != String::from("Error encountered when parsing csv records") { + panic!("Error message is wrong. Perhaps wrong error is being returned?"); + } + } + _ => panic!("Error encountered was not expected CSV error"), + } + } + #[test] fn can_load_json() { - let static_fn = make_load_data( - PathBuf::from("../utils/test-files"), - PathBuf::from("../utils/test-files"), - ); + let static_fn = LoadData::new(PathBuf::from("../utils/test-files")); let mut args = HashMap::new(); args.insert("path".to_string(), to_value("test.json").unwrap()); - let result = static_fn(args.clone()).unwrap(); + let result = static_fn.call(&args.clone()).unwrap(); assert_eq!( result, diff --git a/components/templates/src/global_fns/mod.rs b/components/templates/src/global_fns/mod.rs index 83cffb7..cc34740 100644 --- a/components/templates/src/global_fns/mod.rs +++ b/components/templates/src/global_fns/mod.rs @@ -1,9 +1,8 @@ -extern crate error_chain; - use std::collections::HashMap; -use std::sync::{Arc, Mutex}; +use std::path::PathBuf; +use std::sync::{Arc, Mutex, RwLock}; -use tera::{from_value, to_value, GlobalFn, Result, Value}; +use tera::{from_value, to_value, Function as TeraFn, Result, Value}; use config::Config; use library::{Library, Taxonomy}; @@ -16,82 +15,39 @@ mod macros; mod load_data; -pub use self::load_data::make_load_data; - -pub fn make_trans(config: Config) -> GlobalFn { - let translations_config = config.translations; - let default_lang = config.default_language.clone(); +pub use self::load_data::LoadData; - Box::new(move |args| -> Result { +#[derive(Debug)] +pub struct Trans { + config: Config, +} +impl Trans { + pub fn new(config: Config) -> Self { + Self { config } + } +} +impl TeraFn for Trans { + fn call(&self, args: &HashMap) -> Result { let key = required_arg!(String, args.get("key"), "`trans` requires a `key` argument."); let lang = optional_arg!(String, args.get("lang"), "`trans`: `lang` must be a string.") - .unwrap_or_else(|| default_lang.clone()); - let translations = &translations_config[lang.as_str()]; + .unwrap_or_else(|| self.config.default_language.clone()); + let translations = &self.config.translations[lang.as_str()]; Ok(to_value(&translations[key.as_str()]).unwrap()) - }) -} - -pub fn make_get_page(library: &Library) -> GlobalFn { - let mut pages = HashMap::new(); - for page in library.pages_values() { - pages.insert( - page.file.relative.clone(), - to_value(library.get_page(&page.file.path).unwrap().to_serialized(library)).unwrap(), - ); } - - Box::new(move |args| -> Result { - let path = required_arg!( - String, - args.get("path"), - "`get_page` requires a `path` argument with a string value" - ); - match pages.get(&path) { - Some(p) => Ok(p.clone()), - None => Err(format!("Page `{}` not found.", path).into()), - } - }) } -pub fn make_get_section(library: &Library) -> GlobalFn { - let mut sections = HashMap::new(); - let mut sections_basic = HashMap::new(); - for section in library.sections_values() { - sections.insert( - section.file.relative.clone(), - to_value(library.get_section(§ion.file.path).unwrap().to_serialized(library)) - .unwrap(), - ); - - sections_basic.insert( - section.file.relative.clone(), - to_value(library.get_section(§ion.file.path).unwrap().to_serialized_basic(library)) - .unwrap(), - ); +#[derive(Debug)] +pub struct GetUrl { + config: Config, + permalinks: HashMap, +} +impl GetUrl { + pub fn new(config: Config, permalinks: HashMap) -> Self { + Self { config, permalinks } } - - Box::new(move |args| -> Result { - let path = required_arg!( - String, - args.get("path"), - "`get_section` requires a `path` argument with a string value" - ); - - let metadata_only = args - .get("metadata_only") - .map_or(false, |c| from_value::(c.clone()).unwrap_or(false)); - - let container = if metadata_only { §ions_basic } else { §ions }; - - match container.get(&path) { - Some(p) => Ok(p.clone()), - None => Err(format!("Section `{}` not found.", path).into()), - } - }) } - -pub fn make_get_url(permalinks: HashMap, config: Config) -> GlobalFn { - Box::new(move |args| -> Result { +impl TeraFn for GetUrl { + fn call(&self, args: &HashMap) -> Result { let cachebust = args.get("cachebust").map_or(false, |c| from_value::(c.clone()).unwrap_or(false)); @@ -105,7 +61,7 @@ pub fn make_get_url(permalinks: HashMap, config: Config) -> Glob "`get_url` requires a `path` argument with a string value" ); if path.starts_with("./") { - match resolve_internal_link(&path, &permalinks) { + match resolve_internal_link(&path, &self.permalinks) { Ok(url) => Ok(to_value(url).unwrap()), Err(_) => { Err(format!("Could not resolve URL for link `{}` not found.", path).into()) @@ -113,56 +69,96 @@ pub fn make_get_url(permalinks: HashMap, config: Config) -> Glob } } else { // anything else - let mut permalink = config.make_permalink(&path); + let mut permalink = self.config.make_permalink(&path); if !trailing_slash && permalink.ends_with('/') { permalink.pop(); // Removes the slash } if cachebust { - permalink = format!("{}?t={}", permalink, config.build_timestamp.unwrap()); + permalink = format!("{}?t={}", permalink, self.config.build_timestamp.unwrap()); } Ok(to_value(permalink).unwrap()) } - }) + } } -pub fn make_get_taxonomy(all_taxonomies: &[Taxonomy], library: &Library) -> GlobalFn { - let mut taxonomies = HashMap::new(); - for taxonomy in all_taxonomies { - taxonomies - .insert(taxonomy.kind.name.clone(), to_value(taxonomy.to_serialized(library)).unwrap()); +#[derive(Debug)] +pub struct ResizeImage { + imageproc: Arc>, +} +impl ResizeImage { + pub fn new(imageproc: Arc>) -> Self { + Self { imageproc } } +} - Box::new(move |args| -> Result { - let kind = required_arg!( +static DEFAULT_OP: &'static str = "fill"; +static DEFAULT_FMT: &'static str = "auto"; +const DEFAULT_Q: u8 = 75; + +impl TeraFn for ResizeImage { + fn call(&self, args: &HashMap) -> Result { + let path = required_arg!( String, - args.get("kind"), - "`get_taxonomy` requires a `kind` argument with a string value" + args.get("path"), + "`resize_image` requires a `path` argument with a string value" ); - let container = match taxonomies.get(&kind) { - Some(c) => c, - None => { - return Err( - format!("`get_taxonomy` received an unknown taxonomy as kind: {}", kind).into() - ); - } - }; + let width = optional_arg!( + u32, + args.get("width"), + "`resize_image`: `width` must be a non-negative integer" + ); + let height = optional_arg!( + u32, + args.get("height"), + "`resize_image`: `height` must be a non-negative integer" + ); + let op = optional_arg!(String, args.get("op"), "`resize_image`: `op` must be a string") + .unwrap_or_else(|| DEFAULT_OP.to_string()); - Ok(to_value(container).unwrap()) - }) -} + let format = + optional_arg!(String, args.get("format"), "`resize_image`: `format` must be a string") + .unwrap_or_else(|| DEFAULT_FMT.to_string()); -pub fn make_get_taxonomy_url(all_taxonomies: &[Taxonomy]) -> GlobalFn { - let mut taxonomies = HashMap::new(); - for taxonomy in all_taxonomies { - let mut items = HashMap::new(); - for item in &taxonomy.items { - items.insert(item.name.clone(), item.permalink.clone()); + let quality = + optional_arg!(u8, args.get("quality"), "`resize_image`: `quality` must be a number") + .unwrap_or(DEFAULT_Q); + if quality == 0 || quality > 100 { + return Err("`resize_image`: `quality` must be in range 1-100".to_string().into()); } - taxonomies.insert(taxonomy.kind.name.clone(), items); + + let mut imageproc = self.imageproc.lock().unwrap(); + if !imageproc.source_exists(&path) { + return Err(format!("`resize_image`: Cannot find path: {}", path).into()); + } + + let imageop = imageproc::ImageOp::from_args(path, &op, width, height, &format, quality) + .map_err(|e| format!("`resize_image`: {}", e))?; + let url = imageproc.insert(imageop); + + to_value(url).map_err(|err| err.into()) } +} - Box::new(move |args| -> Result { +#[derive(Debug)] +pub struct GetTaxonomyUrl { + taxonomies: HashMap>, +} +impl GetTaxonomyUrl { + pub fn new(all_taxonomies: &[Taxonomy]) -> Self { + let mut taxonomies = HashMap::new(); + for taxonomy in all_taxonomies { + let mut items = HashMap::new(); + for item in &taxonomy.items { + items.insert(item.name.clone(), item.permalink.clone()); + } + taxonomies.insert(taxonomy.kind.name.clone(), items); + } + Self { taxonomies } + } +} +impl TeraFn for GetTaxonomyUrl { + fn call(&self, args: &HashMap) -> Result { let kind = required_arg!( String, args.get("kind"), @@ -173,7 +169,7 @@ pub fn make_get_taxonomy_url(all_taxonomies: &[Taxonomy]) -> GlobalFn { args.get("name"), "`get_taxonomy_url` requires a `name` argument with a string value" ); - let container = match taxonomies.get(&kind) { + let container = match self.taxonomies.get(&kind) { Some(c) => c, None => { return Err(format!( @@ -189,58 +185,112 @@ pub fn make_get_taxonomy_url(all_taxonomies: &[Taxonomy]) -> GlobalFn { } Err(format!("`get_taxonomy_url`: couldn't find `{}` in `{}` taxonomy", name, kind).into()) - }) + } } -pub fn make_resize_image(imageproc: Arc>) -> GlobalFn { - static DEFAULT_OP: &'static str = "fill"; - const DEFAULT_Q: u8 = 75; - - Box::new(move |args| -> Result { +#[derive(Debug)] +pub struct GetPage { + base_path: PathBuf, + library: Arc>, +} +impl GetPage { + pub fn new(base_path: PathBuf, library: Arc>) -> Self { + Self { base_path: base_path.join("content"), library } + } +} +impl TeraFn for GetPage { + fn call(&self, args: &HashMap) -> Result { let path = required_arg!( String, args.get("path"), - "`resize_image` requires a `path` argument with a string value" - ); - let width = optional_arg!( - u32, - args.get("width"), - "`resize_image`: `width` must be a non-negative integer" - ); - let height = optional_arg!( - u32, - args.get("height"), - "`resize_image`: `height` must be a non-negative integer" + "`get_page` requires a `path` argument with a string value" ); - let op = optional_arg!(String, args.get("op"), "`resize_image`: `op` must be a string") - .unwrap_or_else(|| DEFAULT_OP.to_string()); - let quality = - optional_arg!(u8, args.get("quality"), "`resize_image`: `quality` must be a number") - .unwrap_or(DEFAULT_Q); - if quality == 0 || quality > 100 { - return Err("`resize_image`: `quality` must be in range 1-100".to_string().into()); + let full_path = self.base_path.join(&path); + let library = self.library.read().unwrap(); + match library.get_page(&full_path) { + Some(p) => Ok(to_value(p.to_serialized(&library)).unwrap()), + None => Err(format!("Page `{}` not found.", path).into()), } + } +} - let mut imageproc = imageproc.lock().unwrap(); - if !imageproc.source_exists(&path) { - return Err(format!("`resize_image`: Cannot find path: {}", path).into()); +#[derive(Debug)] +pub struct GetSection { + base_path: PathBuf, + library: Arc>, +} +impl GetSection { + pub fn new(base_path: PathBuf, library: Arc>) -> Self { + Self { base_path: base_path.join("content"), library } + } +} +impl TeraFn for GetSection { + fn call(&self, args: &HashMap) -> Result { + let path = required_arg!( + String, + args.get("path"), + "`get_section` requires a `path` argument with a string value" + ); + + let metadata_only = args + .get("metadata_only") + .map_or(false, |c| from_value::(c.clone()).unwrap_or(false)); + + let full_path = self.base_path.join(&path); + let library = self.library.read().unwrap(); + + match library.get_section(&full_path) { + Some(s) => { + if metadata_only { + Ok(to_value(s.to_serialized_basic(&library)).unwrap()) + } else { + Ok(to_value(s.to_serialized(&library)).unwrap()) + } + } + None => Err(format!("Section `{}` not found.", path).into()), } + } +} - let imageop = imageproc::ImageOp::from_args(path, &op, width, height, quality) - .map_err(|e| format!("`resize_image`: {}", e))?; - let url = imageproc.insert(imageop); +#[derive(Debug)] +pub struct GetTaxonomy { + library: Arc>, + taxonomies: HashMap, +} +impl GetTaxonomy { + pub fn new(all_taxonomies: Vec, library: Arc>) -> Self { + let mut taxonomies = HashMap::new(); + for taxo in all_taxonomies { + taxonomies.insert(taxo.kind.name.clone(), taxo); + } + Self { taxonomies, library } + } +} +impl TeraFn for GetTaxonomy { + fn call(&self, args: &HashMap) -> Result { + let kind = required_arg!( + String, + args.get("kind"), + "`get_taxonomy` requires a `kind` argument with a string value" + ); - to_value(url).map_err(|err| err.into()) - }) + match self.taxonomies.get(&kind) { + Some(t) => Ok(to_value(t.to_serialized(&self.library.read().unwrap())).unwrap()), + None => { + Err(format!("`get_taxonomy` received an unknown taxonomy as kind: {}", kind).into()) + } + } + } } #[cfg(test)] mod tests { - use super::{make_get_taxonomy, make_get_taxonomy_url, make_get_url, make_trans}; + use super::{GetTaxonomy, GetTaxonomyUrl, GetUrl, Trans}; use std::collections::HashMap; + use std::sync::{Arc, RwLock}; - use tera::{to_value, Value}; + use tera::{to_value, Function, Value}; use config::{Config, Taxonomy as TaxonomyConfig}; use library::{Library, Taxonomy, TaxonomyItem}; @@ -248,56 +298,67 @@ mod tests { #[test] fn can_add_cachebust_to_url() { let config = Config::default(); - let static_fn = make_get_url(HashMap::new(), config); + let static_fn = GetUrl::new(config, HashMap::new()); let mut args = HashMap::new(); args.insert("path".to_string(), to_value("app.css").unwrap()); args.insert("cachebust".to_string(), to_value(true).unwrap()); - assert_eq!(static_fn(args).unwrap(), "http://a-website.com/app.css?t=1"); + assert_eq!(static_fn.call(&args).unwrap(), "http://a-website.com/app.css?t=1"); } #[test] fn can_add_trailing_slashes() { let config = Config::default(); - let static_fn = make_get_url(HashMap::new(), config); + let static_fn = GetUrl::new(config, HashMap::new()); let mut args = HashMap::new(); args.insert("path".to_string(), to_value("app.css").unwrap()); args.insert("trailing_slash".to_string(), to_value(true).unwrap()); - assert_eq!(static_fn(args).unwrap(), "http://a-website.com/app.css/"); + assert_eq!(static_fn.call(&args).unwrap(), "http://a-website.com/app.css/"); } #[test] fn can_add_slashes_and_cachebust() { let config = Config::default(); - let static_fn = make_get_url(HashMap::new(), config); + let static_fn = GetUrl::new(config, HashMap::new()); let mut args = HashMap::new(); args.insert("path".to_string(), to_value("app.css").unwrap()); args.insert("trailing_slash".to_string(), to_value(true).unwrap()); args.insert("cachebust".to_string(), to_value(true).unwrap()); - assert_eq!(static_fn(args).unwrap(), "http://a-website.com/app.css/?t=1"); + assert_eq!(static_fn.call(&args).unwrap(), "http://a-website.com/app.css/?t=1"); } #[test] fn can_link_to_some_static_file() { let config = Config::default(); - let static_fn = make_get_url(HashMap::new(), config); + let static_fn = GetUrl::new(config, HashMap::new()); let mut args = HashMap::new(); args.insert("path".to_string(), to_value("app.css").unwrap()); - assert_eq!(static_fn(args).unwrap(), "http://a-website.com/app.css"); + assert_eq!(static_fn.call(&args).unwrap(), "http://a-website.com/app.css"); } #[test] fn can_get_taxonomy() { - let taxo_config = TaxonomyConfig { name: "tags".to_string(), ..TaxonomyConfig::default() }; - let library = Library::new(0, 0); - let tag = TaxonomyItem::new("Programming", "tags", &Config::default(), vec![], &library); + let config = Config::default(); + let taxo_config = TaxonomyConfig { + name: "tags".to_string(), + lang: config.default_language.clone(), + ..TaxonomyConfig::default() + }; + let library = Arc::new(RwLock::new(Library::new(0, 0, false))); + let tag = TaxonomyItem::new( + "Programming", + &taxo_config, + &config, + vec![], + &library.read().unwrap(), + ); let tags = Taxonomy { kind: taxo_config, items: vec![tag] }; let taxonomies = vec![tags.clone()]; - let static_fn = make_get_taxonomy(&taxonomies, &library); + let static_fn = GetTaxonomy::new(taxonomies.clone(), library.clone()); // can find it correctly let mut args = HashMap::new(); args.insert("kind".to_string(), to_value("tags").unwrap()); - let res = static_fn(args).unwrap(); + let res = static_fn.call(&args).unwrap(); let res_obj = res.as_object().unwrap(); assert_eq!(res_obj["kind"], to_value(tags.kind).unwrap()); assert_eq!(res_obj["items"].clone().as_array().unwrap().len(), 1); @@ -321,31 +382,36 @@ mod tests { // and errors if it can't find it let mut args = HashMap::new(); args.insert("kind".to_string(), to_value("something-else").unwrap()); - assert!(static_fn(args).is_err()); + assert!(static_fn.call(&args).is_err()); } #[test] fn can_get_taxonomy_url() { - let taxo_config = TaxonomyConfig { name: "tags".to_string(), ..TaxonomyConfig::default() }; - let library = Library::new(0, 0); - let tag = TaxonomyItem::new("Programming", "tags", &Config::default(), vec![], &library); + let config = Config::default(); + let taxo_config = TaxonomyConfig { + name: "tags".to_string(), + lang: config.default_language.clone(), + ..TaxonomyConfig::default() + }; + let library = Library::new(0, 0, false); + let tag = TaxonomyItem::new("Programming", &taxo_config, &config, vec![], &library); let tags = Taxonomy { kind: taxo_config, items: vec![tag] }; let taxonomies = vec![tags.clone()]; - let static_fn = make_get_taxonomy_url(&taxonomies); + let static_fn = GetTaxonomyUrl::new(&taxonomies); // can find it correctly let mut args = HashMap::new(); args.insert("kind".to_string(), to_value("tags").unwrap()); args.insert("name".to_string(), to_value("Programming").unwrap()); assert_eq!( - static_fn(args).unwrap(), + static_fn.call(&args).unwrap(), to_value("http://a-website.com/tags/programming/").unwrap() ); // and errors if it can't find it let mut args = HashMap::new(); args.insert("kind".to_string(), to_value("tags").unwrap()); args.insert("name".to_string(), to_value("random").unwrap()); - assert!(static_fn(args).is_err()); + assert!(static_fn.call(&args).is_err()); } #[test] @@ -364,16 +430,16 @@ title = "A title" "#; let config = Config::parse(trans_config).unwrap(); - let static_fn = make_trans(config); + let static_fn = Trans::new(config); let mut args = HashMap::new(); args.insert("key".to_string(), to_value("title").unwrap()); - assert_eq!(static_fn(args.clone()).unwrap(), "Un titre"); + assert_eq!(static_fn.call(&args).unwrap(), "Un titre"); args.insert("lang".to_string(), to_value("en").unwrap()); - assert_eq!(static_fn(args.clone()).unwrap(), "A title"); + assert_eq!(static_fn.call(&args).unwrap(), "A title"); args.insert("lang".to_string(), to_value("fr").unwrap()); - assert_eq!(static_fn(args.clone()).unwrap(), "Un titre"); + assert_eq!(static_fn.call(&args).unwrap(), "Un titre"); } } diff --git a/components/templates/src/lib.rs b/components/templates/src/lib.rs index c9723fb..a1b6113 100644 --- a/components/templates/src/lib.rs +++ b/components/templates/src/lib.rs @@ -25,21 +25,34 @@ pub mod global_fns; use tera::{Context, Tera}; -use errors::{Result, ResultExt}; +use errors::{Error, Result}; lazy_static! { pub static ref ZOLA_TERA: Tera = { let mut tera = Tera::default(); tera.add_raw_templates(vec![ - ("404.html", include_str!("builtins/404.html")), - ("rss.xml", include_str!("builtins/rss.xml")), - ("sitemap.xml", include_str!("builtins/sitemap.xml")), - ("robots.txt", include_str!("builtins/robots.txt")), - ("anchor-link.html", include_str!("builtins/anchor-link.html")), - ("shortcodes/youtube.html", include_str!("builtins/shortcodes/youtube.html")), - ("shortcodes/vimeo.html", include_str!("builtins/shortcodes/vimeo.html")), - ("shortcodes/gist.html", include_str!("builtins/shortcodes/gist.html")), - ("shortcodes/streamable.html", include_str!("builtins/shortcodes/streamable.html")), + ("__zola_builtins/404.html", include_str!("builtins/404.html")), + ("__zola_builtins/rss.xml", include_str!("builtins/rss.xml")), + ("__zola_builtins/sitemap.xml", include_str!("builtins/sitemap.xml")), + ("__zola_builtins/robots.txt", include_str!("builtins/robots.txt")), + ( + "__zola_builtins/split_sitemap_index.xml", + include_str!("builtins/split_sitemap_index.xml"), + ), + ("__zola_builtins/anchor-link.html", include_str!("builtins/anchor-link.html")), + ( + "__zola_builtins/shortcodes/youtube.html", + include_str!("builtins/shortcodes/youtube.html"), + ), + ( + "__zola_builtins/shortcodes/vimeo.html", + include_str!("builtins/shortcodes/vimeo.html"), + ), + ("__zola_builtins/shortcodes/gist.html", include_str!("builtins/shortcodes/gist.html")), + ( + "__zola_builtins/shortcodes/streamable.html", + include_str!("builtins/shortcodes/streamable.html"), + ), ("internal/alias.html", include_str!("builtins/internal/alias.html")), ]) .unwrap(); @@ -56,6 +69,6 @@ pub fn render_redirect_template(url: &str, tera: &Tera) -> Result { let mut context = Context::new(); context.insert("url", &url); - tera.render("internal/alias.html", &context) - .chain_err(|| format!("Failed to render alias for '{}'", url)) + tera.render("internal/alias.html", context) + .map_err(|e| Error::chain(format!("Failed to render alias for '{}'", url), e)) } diff --git a/components/utils/Cargo.toml b/components/utils/Cargo.toml index e6c558d..9759871 100644 --- a/components/utils/Cargo.toml +++ b/components/utils/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Vincent Prouillet "] [dependencies] errors = { path = "../errors" } -tera = "0.11" +tera = "1.0.0-alpha.3" unicode-segmentation = "1.2" walkdir = "2" toml = "0.4" diff --git a/components/utils/src/fs.rs b/components/utils/src/fs.rs index 788dae1..f9eb4ea 100644 --- a/components/utils/src/fs.rs +++ b/components/utils/src/fs.rs @@ -4,7 +4,7 @@ use std::path::{Path, PathBuf}; use std::time::SystemTime; use walkdir::WalkDir; -use errors::{Result, ResultExt}; +use errors::{Error, Result}; pub fn is_path_in_directory(parent: &Path, path: &Path) -> Result { let canonical_path = path @@ -19,7 +19,8 @@ pub fn is_path_in_directory(parent: &Path, path: &Path) -> Result { /// Create a file with the content given pub fn create_file(path: &Path, content: &str) -> Result<()> { - let mut file = File::create(&path)?; + let mut file = + File::create(&path).map_err(|e| Error::chain(format!("Failed to create {:?}", path), e))?; file.write_all(content.as_bytes())?; Ok(()) } @@ -36,8 +37,9 @@ pub fn ensure_directory_exists(path: &Path) -> Result<()> { /// exists before creating it pub fn create_directory(path: &Path) -> Result<()> { if !path.exists() { - create_dir_all(path) - .chain_err(|| format!("Was not able to create folder {}", path.display()))?; + create_dir_all(path).map_err(|e| { + Error::chain(format!("Was not able to create folder {}", path.display()), e) + })?; } Ok(()) } @@ -46,7 +48,7 @@ pub fn create_directory(path: &Path) -> Result<()> { pub fn read_file(path: &Path) -> Result { let mut content = String::new(); File::open(path) - .chain_err(|| format!("Failed to open '{:?}'", path.display()))? + .map_err(|e| Error::chain(format!("Failed to open '{:?}'", path.display()), e))? .read_to_string(&mut content)?; // Remove utf-8 BOM if any. @@ -57,6 +59,19 @@ pub fn read_file(path: &Path) -> Result { Ok(content) } +/// Return the content of a file, with error handling added. +/// The default error message is overwritten by the message given. +/// That means it is allocation 2 strings, oh well +pub fn read_file_with_error(path: &Path, message: &str) -> Result { + let res = read_file(&path); + if res.is_ok() { + return res; + } + let mut err = Error::msg(message); + err.source = res.unwrap_err().source; + Err(err) +} + /// Looks into the current folder for the path and see if there's anything that is not a .md /// file. Those will be copied next to the rendered .html file pub fn find_related_assets(path: &Path) -> Vec { diff --git a/components/utils/src/lib.rs b/components/utils/src/lib.rs index 25581e8..8e462cc 100644 --- a/components/utils/src/lib.rs +++ b/components/utils/src/lib.rs @@ -14,3 +14,4 @@ pub mod fs; pub mod net; pub mod site; pub mod templates; +pub mod vec; diff --git a/components/utils/src/templates.rs b/components/utils/src/templates.rs index 2b0ee29..3b36698 100644 --- a/components/utils/src/templates.rs +++ b/components/utils/src/templates.rs @@ -11,7 +11,7 @@ macro_rules! render_default_tpl { let mut context = Context::new(); context.insert("filename", $filename); context.insert("url", $url); - Tera::one_off(DEFAULT_TPL, &context, true).map_err(|e| e.into()) + Tera::one_off(DEFAULT_TPL, context, true).map_err(|e| e.into()) }}; } @@ -22,15 +22,26 @@ macro_rules! render_default_tpl { pub fn render_template( name: &str, tera: &Tera, - context: &Context, + context: Context, theme: &Option, ) -> Result { + // check if it is in the templates if tera.templates.contains_key(name) { return tera.render(name, context).map_err(|e| e.into()); } + // check if it is part of a theme if let Some(ref t) = *theme { - return tera.render(&format!("{}/templates/{}", t, name), context).map_err(|e| e.into()); + let theme_template_name = format!("{}/templates/{}", t, name); + if tera.templates.contains_key(&theme_template_name) { + return tera.render(&theme_template_name, context).map_err(|e| e.into()); + } + } + + // check if it is part of ZOLA_TERA defaults + let default_name = format!("__zola_builtins/{}", name); + if tera.templates.contains_key(&default_name) { + return tera.render(&default_name, context).map_err(|e| e.into()); } // maybe it's a default one? diff --git a/components/utils/src/vec.rs b/components/utils/src/vec.rs new file mode 100644 index 0000000..346769c --- /dev/null +++ b/components/utils/src/vec.rs @@ -0,0 +1,44 @@ +pub trait InsertMany { + type Element; + fn insert_many(&mut self, elem_to_insert: Vec<(usize, Self::Element)>); +} + +impl InsertMany for Vec { + type Element = T; + + /// Efficiently insert multiple element in their specified index. + /// The elements should sorted in ascending order by their index. + /// + /// This is done in O(n) time. + fn insert_many(&mut self, elem_to_insert: Vec<(usize, T)>) { + let mut inserted = vec![]; + let mut last_idx = 0; + + for (idx, elem) in elem_to_insert.into_iter() { + let head_len = idx - last_idx; + inserted.extend(self.splice(0..head_len, std::iter::empty())); + inserted.push(elem); + last_idx = idx; + } + let len = self.len(); + inserted.extend(self.drain(0..len)); + + *self = inserted; + } +} + +#[cfg(test)] +mod test { + use super::InsertMany; + + #[test] + fn insert_many_works() { + let mut v = vec![1, 2, 3, 4, 5]; + v.insert_many(vec![(0, 0), (2, -1), (5, 6)]); + assert_eq!(v, &[0, 1, 2, -1, 3, 4, 5, 6]); + + let mut v2 = vec![1, 2, 3, 4, 5]; + v2.insert_many(vec![(0, 0), (2, -1)]); + assert_eq!(v2, &[0, 1, 2, -1, 3, 4, 5]); + } +} diff --git a/components/utils/test-files/test.css b/components/utils/test-files/test.css new file mode 100644 index 0000000..3a461fe --- /dev/null +++ b/components/utils/test-files/test.css @@ -0,0 +1 @@ +.hello {} diff --git a/components/utils/test-files/uneven_rows.csv b/components/utils/test-files/uneven_rows.csv new file mode 100644 index 0000000..f2f309c --- /dev/null +++ b/components/utils/test-files/uneven_rows.csv @@ -0,0 +1,4 @@ +Number,Title +1,Gutenberg +2,Printing +3,Typewriter,ExtraBadColumn diff --git a/docs/content/documentation/content/image-processing/01-zola.png b/docs/content/documentation/content/image-processing/01-zola.png new file mode 100644 index 0000000..4407256 Binary files /dev/null and b/docs/content/documentation/content/image-processing/01-zola.png differ diff --git a/docs/content/documentation/content/image-processing/02-zola-manet.png b/docs/content/documentation/content/image-processing/02-zola-manet.png new file mode 100644 index 0000000..b426192 Binary files /dev/null and b/docs/content/documentation/content/image-processing/02-zola-manet.png differ diff --git a/docs/content/documentation/content/image-processing/03-zola-cezanne.png b/docs/content/documentation/content/image-processing/03-zola-cezanne.png new file mode 100644 index 0000000..628823b Binary files /dev/null and b/docs/content/documentation/content/image-processing/03-zola-cezanne.png differ diff --git a/docs/content/documentation/content/image-processing/gutenberg.jpg b/docs/content/documentation/content/image-processing/04-gutenberg.jpg similarity index 100% rename from docs/content/documentation/content/image-processing/gutenberg.jpg rename to docs/content/documentation/content/image-processing/04-gutenberg.jpg diff --git a/docs/content/documentation/content/image-processing/example-00.jpg b/docs/content/documentation/content/image-processing/05-example.jpg similarity index 100% rename from docs/content/documentation/content/image-processing/example-00.jpg rename to docs/content/documentation/content/image-processing/05-example.jpg diff --git a/docs/content/documentation/content/image-processing/example-01.jpg b/docs/content/documentation/content/image-processing/06-example.jpg similarity index 100% rename from docs/content/documentation/content/image-processing/example-01.jpg rename to docs/content/documentation/content/image-processing/06-example.jpg diff --git a/docs/content/documentation/content/image-processing/example-02.jpg b/docs/content/documentation/content/image-processing/07-example.jpg similarity index 100% rename from docs/content/documentation/content/image-processing/example-02.jpg rename to docs/content/documentation/content/image-processing/07-example.jpg diff --git a/docs/content/documentation/content/image-processing/example-03.jpg b/docs/content/documentation/content/image-processing/08-example.jpg similarity index 100% rename from docs/content/documentation/content/image-processing/example-03.jpg rename to docs/content/documentation/content/image-processing/08-example.jpg diff --git a/docs/content/documentation/content/image-processing/index.md b/docs/content/documentation/content/image-processing/index.md index d3c4799..53958e7 100644 --- a/docs/content/documentation/content/image-processing/index.md +++ b/docs/content/documentation/content/image-processing/index.md @@ -16,10 +16,22 @@ resize_image(path, width, height, op, quality) - `path`: The path to the source image relative to the `content` directory in the [directory structure](./documentation/getting-started/directory-structure.md). - `width` and `height`: The dimensions in pixels of the resized image. Usage depends on the `op` argument. -- `op`: Resize operation. This can be one of five choices: `"scale"`, `"fit_width"`, `"fit_height"`, `"fit"`, or `"fill"`. - What each of these does is explained below. - This argument is optional, default value is `"fill"`. -- `quality`: JPEG quality of the resized image, in percents. Optional argument, default value is `75`. +- `op` (_optional_): Resize operation. This can be one of: + - `"scale"` + - `"fit_width"` + - `"fit_height"` + - `"fit"` + - `"fill"` + + What each of these does is explained below. The default is `"fill"`. +- `format` (_optional_): Encoding format of the resized image. May be one of: + - `"auto"` + - `"jpg"` + - `"png"` + + The default is `"auto"`, this means the format is chosen based on input image format. + JPEG is chosen for JPEGs and other lossy formats, while PNG is chosen for PNGs and other lossless formats. +- `quality` (_optional_): JPEG quality of the resized image, in percents. Only used when encoding JPEGs, default value is `75`. ### Image processing and return value @@ -29,7 +41,7 @@ Zola performs image processing during the build process and places the resized i static/processed_images/ ``` -Resized images are JPEGs. Filename of each resized image is a hash of the function arguments, +Filename of each resized image is a hash of the function arguments, which means that once an image is resized in a certain way, it will be stored in the above directory and will not need to be resized again during subsequent builds (unless the image itself, the dimensions, or other arguments are changed). Therefore, if you have a large number of images, they will only need to be resized once. @@ -40,14 +52,14 @@ The function returns a full URL to the resized image. The source for all examples is this 300 × 380 pixels image: -![gutenberg](gutenberg.jpg) +![zola](01-zola.png) ### **`"scale"`** Simply scales the image to the specified dimensions (`width` & `height`) irrespective of the aspect ratio. `resize_image(..., width=150, height=150, op="scale")` - {{ resize_image(path="documentation/content/image-processing/gutenberg.jpg", width=150, height=150, op="scale") }} + {{ resize_image(path="documentation/content/image-processing/01-zola.png", width=150, height=150, op="scale") }} ### **`"fit_width"`** Resizes the image such that the resulting width is `width` and height is whatever will preserve the aspect ratio. @@ -55,7 +67,7 @@ The source for all examples is this 300 × 380 pixels image: `resize_image(..., width=100, op="fit_width")` - {{ resize_image(path="documentation/content/image-processing/gutenberg.jpg", width=100, height=0, op="fit_width") }} + {{ resize_image(path="documentation/content/image-processing/01-zola.png", width=100, height=0, op="fit_width") }} ### **`"fit_height"`** Resizes the image such that the resulting height is `height` and width is whatever will preserve the aspect ratio. @@ -63,7 +75,7 @@ The source for all examples is this 300 × 380 pixels image: `resize_image(..., height=150, op="fit_height")` - {{ resize_image(path="documentation/content/image-processing/gutenberg.jpg", width=0, height=150, op="fit_height") }} + {{ resize_image(path="documentation/content/image-processing/01-zola.png", width=0, height=150, op="fit_height") }} ### **`"fit"`** Like `"fit_width"` and `"fit_height"` combined. @@ -72,7 +84,7 @@ The source for all examples is this 300 × 380 pixels image: `resize_image(..., width=150, height=150, op="fit")` - {{ resize_image(path="documentation/content/image-processing/gutenberg.jpg", width=150, height=150, op="fit") }} + {{ resize_image(path="documentation/content/image-processing/01-zola.png", width=150, height=150, op="fit") }} ### **`"fill"`** This is the default operation. It takes the image's center part with the same aspect ratio as the `width` & `height` given and resizes that @@ -80,7 +92,7 @@ The source for all examples is this 300 × 380 pixels image: `resize_image(..., width=150, height=150, op="fill")` - {{ resize_image(path="documentation/content/image-processing/gutenberg.jpg", width=150, height=150, op="fill") }} + {{ resize_image(path="documentation/content/image-processing/01-zola.png", width=150, height=150, op="fill") }} ## Using `resize_image` in markdown via shortcodes @@ -96,11 +108,11 @@ The examples above were generated using a shortcode file named `resize_image.htm ## Creating picture galleries -The `resize_image()` can be used multiple times and/or in loops as it is designed to handle this efficiently. +The `resize_image()` can be used multiple times and/or in loops. It is designed to handle this efficiently. This can be used along with `assets` [page metadata](./documentation/templates/pages-sections.md) to create picture galleries. The `assets` variable holds paths to all assets in the directory of a page with resources -(see [Assets colocation](./documentation/content/overview.md#assets-colocation)): if you have files other than images you +(see [assets colocation](./documentation/content/overview.md#assets-colocation)): if you have files other than images you will need to filter them out in the loop first like in the example below. This can be used in shortcodes. For example, we can create a very simple html-only clickable @@ -108,7 +120,7 @@ picture gallery with the following shortcode named `gallery.html`: ```jinja2 {% for asset in page.assets %} - {% if asset is ending_with(".jpg") %} + {% if asset is matching("[.](jpg|png)$") %} @@ -117,7 +129,8 @@ picture gallery with the following shortcode named `gallery.html`: {% endfor %} ``` -As you can notice, we didn't specify an `op` argument, which means it'll default to `"fill"`. Similarly, the JPEG quality will default to `75`. +As you can notice, we didn't specify an `op` argument, which means it'll default to `"fill"`. Similarly, the format will default to +`"auto"` (choosing PNG or JPEG as appropriate) and the JPEG quality will default to `75`. To call it from a markdown file, simply do: @@ -130,5 +143,5 @@ Here is the result: {{ gallery() }} - Image attribution: example-01: Willi Heidelbach, example-02: Daniel Ullrich, others: public domain. + Image attribution: Public domain, except: _06-example.jpg_: Willi Heidelbach, _07-example.jpg_: Daniel Ullrich. diff --git a/docs/content/documentation/content/multilingual.md b/docs/content/documentation/content/multilingual.md new file mode 100644 index 0000000..d788972 --- /dev/null +++ b/docs/content/documentation/content/multilingual.md @@ -0,0 +1,37 @@ ++++ +title = "Multilingual sites" +weight = 130 ++++ + +Zola supports having a site in multiple languages. + +## Configuration +To get started, you will need to add the languages you want to support +to your `config.toml`. For example: + +```toml +languages = [ + {code = "fr", rss = true}, # there will be a RSS feed for French content + {code = "it"}, # there won't be a RSS feed for Italian content +] +``` + +If you want to use per-language taxonomies, ensure you set the `lang` field in their +configuration. + +## Content +Once the languages are added in, you can start to translate your content. Zola +uses the filename to detect the language: + +- `content/an-article.md`: this will be the default language +- `content/an-article.fr.md`: this will be in French + +If the language code in the filename does not correspond to one of the languages configured, +an error will be shown. + +If your default language has an `_index.md` in a directory, you will need to add a `_index.{code}.md` +file with the desired front-matter options as there is no language fallback. + +## Output +Zola outputs the translated content with a base URL of `{base_url}/{code}/`. +The only exception to that is if you are setting a translated page `path` directly in the front-matter. diff --git a/docs/content/documentation/content/page.md b/docs/content/documentation/content/page.md index 45a6626..fef7f3a 100644 --- a/docs/content/documentation/content/page.md +++ b/docs/content/documentation/content/page.md @@ -102,6 +102,6 @@ where you want the summary to end and the content up to that point will be also available separately in the [template](./documentation/templates/pages-sections.md#page-variables). -An anchor link to this position named `continue-reading` is created so you can link -directly to it if needed for example: +An anchor link to this position named `continue-reading` is created, wrapped in a paragraph +with a `zola-continue-reading` id, so you can link directly to it if needed for example: `Continue Reading` diff --git a/docs/content/documentation/content/shortcodes.md b/docs/content/documentation/content/shortcodes.md index edcdbc6..290c2c3 100644 --- a/docs/content/documentation/content/shortcodes.md +++ b/docs/content/documentation/content/shortcodes.md @@ -36,6 +36,10 @@ That's it, Zola will now recognise this template as a shortcode named `youtube` The markdown renderer will wrap an inline HTML node like `` or `` into a paragraph. If you want to disable that, simply wrap your shortcode in a `div`. +Shortcodes are rendered before parsing the markdown so it doesn't have access to the table of contents. Because of that, +you also cannot use the `get_page`/`get_section`/`get_taxonomy` global function. It might work while running `zola serve` because +it has been loaded but it will fail during `zola build`. + ## Using shortcodes There are two kinds of shortcodes: diff --git a/docs/content/documentation/content/syntax-highlighting.md b/docs/content/documentation/content/syntax-highlighting.md index bfb7f63..a824bf1 100644 --- a/docs/content/documentation/content/syntax-highlighting.md +++ b/docs/content/documentation/content/syntax-highlighting.md @@ -105,6 +105,7 @@ Here is a full list of the supported languages and the short names you can use: - Textile -> ["textile"] - XML -> ["xml", "xsd", "xslt", "tld", "dtml", "rss", "opml", "svg"] - YAML -> ["yaml", "yml", "sublime-syntax"] +- PowerShell -> ["ps1", "psm1", "psd1"] - SWI-Prolog -> ["pro"] - Reason -> ["re", "rei"] - CMake C Header -> ["h.in"] diff --git a/docs/content/documentation/content/taxonomies.md b/docs/content/documentation/content/taxonomies.md index 20c0f98..b446fa0 100644 --- a/docs/content/documentation/content/taxonomies.md +++ b/docs/content/documentation/content/taxonomies.md @@ -7,13 +7,14 @@ Zola has built-in support for taxonomies. The first step is to define the taxonomies in your [config.toml](./documentation/getting-started/configuration.md). -A taxonomy has 4 variables: +A taxonomy has 5 variables: - `name`: a required string that will be used in the URLs, usually the plural version (i.e. tags, categories etc) - `paginate_by`: if this is set to a number, each term page will be paginated by this much. - `paginate_path`: if set, will be the path used by paginated page and the page number will be appended after it. For example the default would be page/1 - `rss`: if set to `true`, a RSS feed will be generated for each individual term. +- `lang`: only set this if you are making a multilingual site and want to indicate which language this taxonomy is for Once this is done, you can then set taxonomies in your content and Zola will pick them up: diff --git a/docs/content/documentation/deployment/github-pages.md b/docs/content/documentation/deployment/github-pages.md index 6ca934d..ecf273f 100644 --- a/docs/content/documentation/deployment/github-pages.md +++ b/docs/content/documentation/deployment/github-pages.md @@ -7,6 +7,13 @@ By default, GitHub Pages uses Jekyll (A ruby based static site generator), but you can use whatever you want provided you have an `index.html` file in the root of a branch called `gh-pages`. That branch name can also be manually changed in the settings of a repository. +We can use any CI server to build and deploy our site. For example: + + * [Github Actions](https://github.com/shalzz/zola-deploy-action) + * [Travis CI](#travis-ci) + +## Travis CI + We are going to use [TravisCI](https://travis-ci.org) to automatically publish the site. If you are not using Travis already, you will need to login with the GitHub OAuth and activate Travis for the repository. Don't forget to also check if your repository allows GitHub Pages in its settings. diff --git a/docs/content/documentation/getting-started/cli-usage.md b/docs/content/documentation/getting-started/cli-usage.md index e74a5ac..4b247ca 100644 --- a/docs/content/documentation/getting-started/cli-usage.md +++ b/docs/content/documentation/getting-started/cli-usage.md @@ -21,7 +21,7 @@ zola. ## build -This will build the whole site in the `public` directory. +This will build the whole site in the `public` directory after deleting it. ```bash $ zola build @@ -36,6 +36,14 @@ $ zola build --base-url $DEPLOY_URL This is useful for example when you want to deploy previews of a site to a dynamic URL, such as Netlify deploy previews. +You can override the default `base_path` by passing a new directory to the `base-path` flag. If no `base-path` flag +is provided, zola defaults to your current working directory. This is useful if your zola project is located in +a different directory from where you're executing zola from. + +```bash +$ zola build --base-path /path/to/zola/site +``` + You can override the default output directory 'public' by passing a other value to the `output-dir` flag. ```bash @@ -58,6 +66,8 @@ if you are running zola in a Docker container. In the event you don't want zola to run a local webserver, you can use the `--watch-only` flag. +Before starting, it will delete the public directory to ensure it starts from a clean slate. + ```bash $ zola serve $ zola serve --port 2000 @@ -65,6 +75,7 @@ $ zola serve --interface 0.0.0.0 $ zola serve --interface 0.0.0.0 --port 2000 $ zola serve --interface 0.0.0.0 --base-url 127.0.0.1 $ zola serve --interface 0.0.0.0 --port 2000 --output-dir www/public +$ zola serve --interface 0.0.0.0 --port 2000 --base-path mysite/ --output-dir mysite/www/public $ zola serve --watch-only ``` diff --git a/docs/content/documentation/getting-started/configuration.md b/docs/content/documentation/getting-started/configuration.md index 97e633e..0ce7b32 100644 --- a/docs/content/documentation/getting-started/configuration.md +++ b/docs/content/documentation/getting-started/configuration.md @@ -21,7 +21,7 @@ base_url = "mywebsite.com" # Used in RSS by default title = "" description = "" -# the default language, used in RSS and coming i18n +# The default language, used in RSS default_language = "en" # Theme name to use @@ -51,6 +51,15 @@ generate_rss = false # taxonomies = [] +# The additional languages for that site +# Example: +# languages = [ +# {code = "fr", rss = true}, # there will be a RSS feed for French content +# {code = "it"}, # there won't be a RSS feed for Italian content +# ] +# +languages = [] + # Whether to compile the Sass files found in the `sass` directory compile_sass = false @@ -99,6 +108,7 @@ Zola currently has the following highlight themes available: - [classic-modified](https://tmtheme-editor.herokuapp.com/#!/editor/theme/Classic%20Modified) - [demain](https://tmtheme-editor.herokuapp.com/#!/editor/theme/Demain) - [dimmed-fluid](https://tmtheme-editor.herokuapp.com/#!/editor/theme/Dimmed%20Fluid) +- [dracula](https://draculatheme.com/) - [gray-matter-dark](https://tmtheme-editor.herokuapp.com/#!/editor/theme/Gray%20Matter%20Dark) - [gruvbox-dark](https://github.com/morhetz/gruvbox) - [gruvbox-light](https://github.com/morhetz/gruvbox) diff --git a/docs/content/documentation/getting-started/installation.md b/docs/content/documentation/getting-started/installation.md index 03c3600..353cae7 100644 --- a/docs/content/documentation/getting-started/installation.md +++ b/docs/content/documentation/getting-started/installation.md @@ -45,7 +45,7 @@ $ choco install zola ``` ## From source -To build it from source, you will need to have Git, [Rust (at least 1.30) and Cargo](https://www.rust-lang.org/) +To build it from source, you will need to have Git, [Rust (at least 1.31) and Cargo](https://www.rust-lang.org/) installed. You will also need additional dependencies to compile [libsass](https://github.com/sass/libsass): - OSX, Linux and other Unix: `make` (`gmake` on BSDs), `g++`, `libssl-dev` diff --git a/docs/content/documentation/templates/overview.md b/docs/content/documentation/templates/overview.md index 97ca289..be7992d 100644 --- a/docs/content/documentation/templates/overview.md +++ b/docs/content/documentation/templates/overview.md @@ -18,6 +18,7 @@ A few variables are available on all templates minus RSS and sitemap: - `config`: the [configuration](./documentation/getting-started/configuration.md) without any modifications - `current_path`: the path (full URL without the `base_url`) of the current page, never starting with a `/` - `current_url`: the full URL for that page +- `lang`: the language for that page, `null` if the page/section doesn't have a language set ## Standard Templates By default, Zola will look for three templates: `index.html`, which is applied @@ -145,34 +146,37 @@ Gets the whole taxonomy of a specific kind. ``` ### `load_data` -Loads data from a file or URL. Supported file types include *toml*, *json* and *csv*. +Loads data from a file or URL. Supported file types include *toml*, *json* and *csv*. +Any other file type will be loaded as plain text. -The `path` argument specifies the path to the data file relative to your content directory. +The `path` argument specifies the path to the data file relative to your base directory, where your `config.toml` is. As a security precaution, If this file is outside of the main site directory, your site will fail to build. ```jinja2 -{% set data = load_data(path="blog/story/data.toml") %} +{% set data = load_data(path="content/blog/story/data.toml") %} ``` The optional `format` argument allows you to specify and override which data type is contained -within the file specified in the `path` argument. Valid entries are *"toml"*, *"json"*, *"csv"* -or *"plain"*. If the `format` argument isn't specified, then the paths extension is used. +within the file specified in the `path` argument. Valid entries are `toml`, `json`, `csv` +or `plain`. If the `format` argument isn't specified, then the paths extension is used. ```jinja2 -{% set data = load_data(path="blog/story/data.txt", format="json") %} +{% set data = load_data(path="content/blog/story/data.txt", format="json") %} ``` +Use the `plain` format for when your file has a toml/json/csv extension but you want to load it as plain text. + For *toml* and *json* the data is loaded into a structure matching the original data file, -however for *csv* there is no native notion of such a structure. Instead the data is seperated +however for *csv* there is no native notion of such a structure. Instead the data is separated into a data structure containing *headers* and *records*. See the example below to see how this works. In the template: ```jinja2 -{% set data = load_data(path="blog/story/data.csv") %} +{% set data = load_data(path="content/blog/story/data.csv") %} ``` -In the *blog/story/data.csv* file: +In the *content/blog/story/data.csv* file: ```csv Number, Title 1,Gutenberg @@ -210,7 +214,9 @@ By default, the response body will be returned with no parsing. This can be chan #### Data Caching -Data file loading and remote requests are cached in memory during build, so multiple requests aren't made to the same endpoint. URLs are cached based on the URL, and data files are cached based on the files modified time. The format is also taken into account when caching, so a request will be sent twice if it's loaded with 2 different formats. +Data file loading and remote requests are cached in memory during build, so multiple requests aren't made to the same endpoint. +URLs are cached based on the URL, and data files are cached based on the files modified time. +The format is also taken into account when caching, so a request will be sent twice if it's loaded with 2 different formats. ### `trans` Gets the translation of the given `key`, for the `default_language` or the `lang`uage given diff --git a/docs/content/documentation/templates/pages-sections.md b/docs/content/documentation/templates/pages-sections.md index e12ae71..3a8440c 100644 --- a/docs/content/documentation/templates/pages-sections.md +++ b/docs/content/documentation/templates/pages-sections.md @@ -32,13 +32,13 @@ word_count: Number; // Based on https://help.medium.com/hc/en-us/articles/214991667-Read-time reading_time: Number; // `earlier` and `later` are only populated if the section variable `sort_by` is set to `date` +// and only set when rendering the page itself earlier: Page?; later: Page?; // `heavier` and `lighter` are only populated if the section variable `sort_by` is set to `weight` +// and only set when rendering the page itself heavier: Page?; lighter: Page?; -// See the Table of contents section below for more details -toc: Array
; // Year/month/day is only set if the page has a date and month/day are 1-indexed year: Number?; month: Number?; @@ -51,6 +51,10 @@ assets: Array; ancestors: Array; // The relative path from the `content` directory to the markdown file relative_path: String; +// The language for the page if there is one. Default to the config `default_language` +lang: String; +// Information about all the available languages for that content +translations: Array; ``` ## Section variables @@ -66,8 +70,6 @@ with the following fields: content: String; title: String?; description: String?; -date: String?; -slug: String; path: String; // the path, split on '/' components: Array; @@ -83,8 +85,6 @@ subsections: Array; word_count: Number; // Based on https://help.medium.com/hc/en-us/articles/214991667-Read-time reading_time: Number; -// See the Table of contents section below for more details -toc: Array
; // Paths of colocated assets, relative to the content directory assets: Array; // The relative paths of the parent sections until the index onef for use with the `get_section` Tera function @@ -93,11 +93,15 @@ assets: Array; ancestors: Array; // The relative path from the `content` directory to the markdown file relative_path: String; +// The language for the section if there is one. Default to the config `default_language` +lang: String; +// Information about all the available languages for that content +translations: Array; ``` ## Table of contents -Both page and section have a `toc` field which corresponds to an array of `Header`. +Both page and section templates have a `toc` variable which corresponds to an array of `Header`. A `Header` has the following fields: ```ts @@ -112,3 +116,19 @@ permalink: String; // All lower level headers below this header children: Array
; ``` + +## Translated content + +Both page and section have a `translations` field which corresponds to an array of `TranslatedContent`. If your site is not using multiple languages, +this will always be an empty array. +A `TranslatedContent` has the following fields: + +```ts +// The language code for that content, empty if it is the default language +lang: String?; +// The title of that content if there is one +title: String?; +// A permalink to that content +permalink: String; +``` + diff --git a/docs/content/documentation/templates/sitemap.md b/docs/content/documentation/templates/sitemap.md index 86f510c..74f0f5a 100644 --- a/docs/content/documentation/templates/sitemap.md +++ b/docs/content/documentation/templates/sitemap.md @@ -6,20 +6,28 @@ weight = 60 Zola will look for a `sitemap.xml` file in the `templates` directory or use the built-in one. +If your site has more than 30 000 pages, it will automatically split +the links into multiple sitemaps as recommended by [Google](https://support.google.com/webmasters/answer/183668?hl=en): -The sitemap template gets four variables in addition of the config: +> All formats limit a single sitemap to 50MB (uncompressed) and 50,000 URLs. +> If you have a larger file or more URLs, you will have to break your list into multiple sitemaps. +> You can optionally create a sitemap index file (a file that points to a list of sitemaps) and submit that single index file to Google. -- `pages`: all pages of the site -- `sections`: all sections of the site, including an index section -- `tags`: links the tags page and individual tag page, empty if no tags -- `categories`: links the categories page and individual category page, empty if no categories +In such a case, Zola will use a template called `split_sitemap_index.xml` to render the index sitemap. -As the sitemap only requires a link and an optional date for the `lastmod` field, -all the variables above are arrays of `SitemapEntry` with the following type: + +The `sitemap.xml` template gets a single variable: + +- `entries`: all pages of the site, as a list of `SitemapEntry` + +A `SitemapEntry` has the following fields: ```ts permalink: String; date: String?; +extra: Hashmap?; ``` -All `SitemapEntry` are sorted in each variable by their permalink. +The `split_sitemap_index.xml` also gets a single variable: + +- `sitemaps`: a list of permalinks to the sitemaps diff --git a/docs/static/processed_images/0478482c742970ac00.jpg b/docs/static/processed_images/0478482c742970ac00.jpg deleted file mode 100644 index 0096f3e..0000000 Binary files a/docs/static/processed_images/0478482c742970ac00.jpg and /dev/null differ diff --git a/docs/static/processed_images/1794115ed20fc20b00.jpg b/docs/static/processed_images/1794115ed20fc20b00.jpg new file mode 100644 index 0000000..f9d239c Binary files /dev/null and b/docs/static/processed_images/1794115ed20fc20b00.jpg differ diff --git a/docs/static/processed_images/1cec18975099962e00.png b/docs/static/processed_images/1cec18975099962e00.png new file mode 100644 index 0000000..157b1b4 Binary files /dev/null and b/docs/static/processed_images/1cec18975099962e00.png differ diff --git a/docs/static/processed_images/2b6a3e5a28bab1f100.jpg b/docs/static/processed_images/2b6a3e5a28bab1f100.jpg deleted file mode 100644 index 30e1fdb..0000000 Binary files a/docs/static/processed_images/2b6a3e5a28bab1f100.jpg and /dev/null differ diff --git a/docs/static/processed_images/3dba59a146f3bc0900.jpg b/docs/static/processed_images/3dba59a146f3bc0900.jpg deleted file mode 100644 index 944db1d..0000000 Binary files a/docs/static/processed_images/3dba59a146f3bc0900.jpg and /dev/null differ diff --git a/docs/static/processed_images/4c2ee08a8b7c98fd00.png b/docs/static/processed_images/4c2ee08a8b7c98fd00.png new file mode 100644 index 0000000..0c4486b Binary files /dev/null and b/docs/static/processed_images/4c2ee08a8b7c98fd00.png differ diff --git a/docs/static/processed_images/5e399fa94c88057a00.jpg b/docs/static/processed_images/5e399fa94c88057a00.jpg deleted file mode 100644 index 202a9c2..0000000 Binary files a/docs/static/processed_images/5e399fa94c88057a00.jpg and /dev/null differ diff --git a/docs/static/processed_images/60097aeed903cf3b00.png b/docs/static/processed_images/60097aeed903cf3b00.png new file mode 100644 index 0000000..ba71c06 Binary files /dev/null and b/docs/static/processed_images/60097aeed903cf3b00.png differ diff --git a/docs/static/processed_images/60327c08d512e16800.png b/docs/static/processed_images/60327c08d512e16800.png new file mode 100644 index 0000000..c49703f Binary files /dev/null and b/docs/static/processed_images/60327c08d512e16800.png differ diff --git a/docs/static/processed_images/63d5c27341a9885c00.jpg b/docs/static/processed_images/63d5c27341a9885c00.jpg deleted file mode 100644 index 7073c0a..0000000 Binary files a/docs/static/processed_images/63d5c27341a9885c00.jpg and /dev/null differ diff --git a/docs/static/processed_images/63fe884d13fd318d00.jpg b/docs/static/processed_images/63fe884d13fd318d00.jpg deleted file mode 100644 index 72cc2c5..0000000 Binary files a/docs/static/processed_images/63fe884d13fd318d00.jpg and /dev/null differ diff --git a/docs/static/processed_images/67f2ebdd806283e900.jpg b/docs/static/processed_images/67f2ebdd806283e900.jpg new file mode 100644 index 0000000..32275e3 Binary files /dev/null and b/docs/static/processed_images/67f2ebdd806283e900.jpg differ diff --git a/docs/static/processed_images/70513837257b310c00.jpg b/docs/static/processed_images/70513837257b310c00.jpg new file mode 100644 index 0000000..d12333f Binary files /dev/null and b/docs/static/processed_images/70513837257b310c00.jpg differ diff --git a/docs/static/processed_images/7459e23e962c9d2f00.png b/docs/static/processed_images/7459e23e962c9d2f00.png new file mode 100644 index 0000000..c6afbf9 Binary files /dev/null and b/docs/static/processed_images/7459e23e962c9d2f00.png differ diff --git a/docs/static/processed_images/8b446e542d0b692d00.jpg b/docs/static/processed_images/8b446e542d0b692d00.jpg deleted file mode 100644 index 7073c0a..0000000 Binary files a/docs/static/processed_images/8b446e542d0b692d00.jpg and /dev/null differ diff --git a/docs/static/processed_images/a9f5475850972f8500.png b/docs/static/processed_images/a9f5475850972f8500.png new file mode 100644 index 0000000..e73e43a Binary files /dev/null and b/docs/static/processed_images/a9f5475850972f8500.png differ diff --git a/docs/static/processed_images/ab39b603591b3e3300.jpg b/docs/static/processed_images/ab39b603591b3e3300.jpg deleted file mode 100644 index d52d902..0000000 Binary files a/docs/static/processed_images/ab39b603591b3e3300.jpg and /dev/null differ diff --git a/docs/static/processed_images/aebd0f00cf9232d000.jpg b/docs/static/processed_images/aebd0f00cf9232d000.jpg new file mode 100644 index 0000000..a6fffd1 Binary files /dev/null and b/docs/static/processed_images/aebd0f00cf9232d000.jpg differ diff --git a/docs/static/processed_images/baf5a4139772f2c700.png b/docs/static/processed_images/baf5a4139772f2c700.png new file mode 100644 index 0000000..e73e43a Binary files /dev/null and b/docs/static/processed_images/baf5a4139772f2c700.png differ diff --git a/docs/static/processed_images/d364fb703e1e0b3200.jpg b/docs/static/processed_images/d364fb703e1e0b3200.jpg new file mode 100644 index 0000000..1a6ae00 Binary files /dev/null and b/docs/static/processed_images/d364fb703e1e0b3200.jpg differ diff --git a/docs/static/processed_images/d91d0751df06edce00.jpg b/docs/static/processed_images/d91d0751df06edce00.jpg deleted file mode 100644 index 8c7b2e7..0000000 Binary files a/docs/static/processed_images/d91d0751df06edce00.jpg and /dev/null differ diff --git a/docs/static/processed_images/e1f961e8b8cb30b500.png b/docs/static/processed_images/e1f961e8b8cb30b500.png new file mode 100644 index 0000000..5e3482a Binary files /dev/null and b/docs/static/processed_images/e1f961e8b8cb30b500.png differ diff --git a/docs/static/processed_images/e690cdfaf053bbd700.jpg b/docs/static/processed_images/e690cdfaf053bbd700.jpg deleted file mode 100644 index 81faf5f..0000000 Binary files a/docs/static/processed_images/e690cdfaf053bbd700.jpg and /dev/null differ diff --git a/docs/templates/shortcodes/gallery.html b/docs/templates/shortcodes/gallery.html index a9261e9..e50f823 100644 --- a/docs/templates/shortcodes/gallery.html +++ b/docs/templates/shortcodes/gallery.html @@ -1,7 +1,7 @@ {% for asset in page.assets -%} - {%- if asset is ending_with(".jpg") -%} - - + {%- if asset is matching("[.](jpg|png)$") -%} + +   {%- endif %} diff --git a/src/cli.rs b/src/cli.rs index ca40aaa..500aa04 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -23,13 +23,18 @@ pub fn build_cli() -> App<'static, 'static> { .help("Name of the project. Will create a new directory with that name in the current directory") ), SubCommand::with_name("build") - .about("Builds the site") + .about("Deletes the output directory if there is one and builds the site") .args(&[ Arg::with_name("base_url") .short("u") .long("base-url") .takes_value(true) .help("Force the base URL to be that value (default to the one in config.toml)"), + Arg::with_name("base_path") + .short("b") + .long("base-path") + .takes_value(true) + .help("Force the base site path to a certain directory [default: the current working directory]"), Arg::with_name("output_dir") .short("o") .long("output-dir") @@ -56,6 +61,11 @@ pub fn build_cli() -> App<'static, 'static> { .default_value("public") .takes_value(true) .help("Outputs the generated site in the given path"), + Arg::with_name("base_path") + .short("b") + .long("base-path") + .takes_value(true) + .help("Force the base site path to a certain directory [default: the current working directory]"), Arg::with_name("base_url") .short("u") .long("base-url") diff --git a/src/cmd/build.rs b/src/cmd/build.rs index aca974e..6bc8dc8 100644 --- a/src/cmd/build.rs +++ b/src/cmd/build.rs @@ -1,12 +1,19 @@ use std::env; +use std::path::PathBuf; use errors::Result; use site::Site; use console; -pub fn build(config_file: &str, base_url: Option<&str>, output_dir: &str) -> Result<()> { - let mut site = Site::new(env::current_dir().unwrap(), config_file)?; +pub fn build( + config_file: &str, + base_path: Option<&str>, + base_url: Option<&str>, + output_dir: &str, +) -> Result<()> { + let bp = base_path.map(PathBuf::from).unwrap_or(env::current_dir().unwrap()); + let mut site = Site::new(bp, config_file)?; site.set_output_path(output_dir); if let Some(b) = base_url { site.set_base_url(b.to_string()); diff --git a/src/cmd/init.rs b/src/cmd/init.rs index c47ef5d..b263482 100644 --- a/src/cmd/init.rs +++ b/src/cmd/init.rs @@ -41,7 +41,7 @@ pub fn create_new_project(name: &str) -> Result<()> { let search = ask_bool("> Do you want to build a search index of the content?", false)?; let config = CONFIG - .trim_left() + .trim_start() .replace("%BASE_URL%", &base_url) .replace("%COMPILE_SASS%", &format!("{}", compile_sass)) .replace("%SEARCH%", &format!("{}", search)) diff --git a/src/cmd/serve.rs b/src/cmd/serve.rs index d675834..50894c3 100644 --- a/src/cmd/serve.rs +++ b/src/cmd/serve.rs @@ -36,7 +36,7 @@ use ctrlc; use notify::{watcher, RecursiveMode, Watcher}; use ws::{Message, Sender, WebSocket}; -use errors::{Result, ResultExt}; +use errors::{Error as ZolaError, Result}; use site::Site; use utils::fs::copy_file; @@ -90,36 +90,39 @@ fn livereload_handler(_: &HttpRequest) -> &'static str { LIVE_RELOAD } -fn rebuild_done_handling(broadcaster: &Sender, res: Result<()>, reload_path: &str) { +fn rebuild_done_handling(broadcaster: &Option, res: Result<()>, reload_path: &str) { match res { Ok(_) => { - broadcaster - .send(format!( - r#" - {{ - "command": "reload", - "path": "{}", - "originalPath": "", - "liveCSS": true, - "liveImg": true, - "protocol": ["http://livereload.com/protocols/official-7"] - }}"#, - reload_path - )) - .unwrap(); + if let Some(broadcaster) = broadcaster.as_ref() { + broadcaster + .send(format!( + r#" + {{ + "command": "reload", + "path": "{}", + "originalPath": "", + "liveCSS": true, + "liveImg": true, + "protocol": ["http://livereload.com/protocols/official-7"] + }}"#, + reload_path + )) + .unwrap(); + } } Err(e) => console::unravel_errors("Failed to build the site", &e), } } -fn create_new_site( +fn create_new_site>( interface: &str, port: u16, output_dir: &str, + base_path: P, base_url: &str, config_file: &str, ) -> Result<(Site, String)> { - let mut site = Site::new(env::current_dir().unwrap(), config_file)?; + let mut site = Site::new(base_path, config_file)?; let base_address = format!("{}:{}", base_url, port); let address = format!("{}:{}", interface, port); @@ -164,42 +167,45 @@ pub fn serve( interface: &str, port: u16, output_dir: &str, + base_path: Option<&str>, base_url: &str, config_file: &str, watch_only: bool, ) -> Result<()> { let start = Instant::now(); - let (mut site, address) = create_new_site(interface, port, output_dir, base_url, config_file)?; + let bp = base_path.map(PathBuf::from).unwrap_or(env::current_dir().unwrap()); + let (mut site, address) = + create_new_site(interface, port, output_dir, bp.clone(), base_url, config_file)?; console::report_elapsed_time(start); // Setup watchers let mut watching_static = false; let mut watching_templates = false; let (tx, rx) = channel(); - let mut watcher = watcher(tx, Duration::from_secs(2)).unwrap(); + let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap(); watcher - .watch("content/", RecursiveMode::Recursive) - .chain_err(|| "Can't watch the `content` folder. Does it exist?")?; + .watch(bp.join("content/"), RecursiveMode::Recursive) + .map_err(|e| ZolaError::chain("Can't watch the `content` folder. Does it exist?", e))?; watcher - .watch(config_file, RecursiveMode::Recursive) - .chain_err(|| "Can't watch the `config` file. Does it exist?")?; + .watch(bp.join(config_file), RecursiveMode::Recursive) + .map_err(|e| ZolaError::chain("Can't watch the `config` file. Does it exist?", e))?; - if Path::new("static").exists() { + if bp.join("static").exists() { watching_static = true; watcher - .watch("static/", RecursiveMode::Recursive) - .chain_err(|| "Can't watch the `static` folder.")?; + .watch(bp.join("static/"), RecursiveMode::Recursive) + .map_err(|e| ZolaError::chain("Can't watch the `static` folder.", e))?; } - if Path::new("templates").exists() { + if bp.join("templates").exists() { watching_templates = true; watcher - .watch("templates/", RecursiveMode::Recursive) - .chain_err(|| "Can't watch the `templates` folder.")?; + .watch(bp.join("templates/"), RecursiveMode::Recursive) + .map_err(|e| ZolaError::chain("Can't watch the `templates` folder.", e))?; } // Sass support is optional so don't make it an error to no have a sass folder - let _ = watcher.watch("sass/", RecursiveMode::Recursive); + let _ = watcher.watch(bp.join("sass/"), RecursiveMode::Recursive); let ws_address = format!("{}:{}", interface, site.live_reload.unwrap()); let output_path = Path::new(output_dir).to_path_buf(); @@ -256,8 +262,6 @@ pub fn serve( None }; - let pwd = env::current_dir().unwrap(); - let mut watchers = vec!["content", "config.toml"]; if watching_static { watchers.push("static"); @@ -271,7 +275,7 @@ pub fn serve( println!( "Listening for changes in {}{}{{{}}}", - pwd.display(), + bp.display(), MAIN_SEPARATOR, watchers.join(", ") ); @@ -293,14 +297,8 @@ pub fn serve( format!("-> Template changed {}", path.display()) }; console::info(&msg); - if let Some(ref broadcaster) = broadcaster { - // Force refresh - rebuild_done_handling( - broadcaster, - rebuild::after_template_change(site, &path), - "/x.js", - ); - } + // Force refresh + rebuild_done_handling(&broadcaster, rebuild::after_template_change(site, &path), "/x.js"); }; let reload_sass = |site: &Site, path: &Path, partial_path: &Path| { @@ -310,13 +308,11 @@ pub fn serve( format!("-> Sass file changed {}", path.display()) }; console::info(&msg); - if let Some(ref broadcaster) = broadcaster { - rebuild_done_handling( - &broadcaster, - site.compile_sass(&site.base_path), - &partial_path.to_string_lossy(), - ); - } + rebuild_done_handling( + &broadcaster, + site.compile_sass(&site.base_path), + &partial_path.to_string_lossy(), + ); }; let copy_static = |site: &Site, path: &Path, partial_path: &Path| { @@ -332,20 +328,18 @@ pub fn serve( }; console::info(&msg); - if let Some(ref broadcaster) = broadcaster { - if path.is_dir() { - rebuild_done_handling( - broadcaster, - site.copy_static_directories(), - &path.to_string_lossy(), - ); - } else { - rebuild_done_handling( - broadcaster, - copy_file(&path, &site.output_path, &site.static_path), - &partial_path.to_string_lossy(), - ); - } + if path.is_dir() { + rebuild_done_handling( + &broadcaster, + site.copy_static_directories(), + &path.to_string_lossy(), + ); + } else { + rebuild_done_handling( + &broadcaster, + copy_file(&path, &site.output_path, &site.static_path), + &partial_path.to_string_lossy(), + ); } }; @@ -357,7 +351,8 @@ pub fn serve( if path.is_file() && is_temp_file(&path) { continue; } - let (change_kind, partial_path) = detect_change_kind(&pwd, &path); + let (change_kind, partial_path) = + detect_change_kind(&bp.canonicalize().unwrap(), &path); // We only care about changes in non-empty folders if path.is_dir() && is_folder_empty(&path) { @@ -373,14 +368,12 @@ pub fn serve( match change_kind { ChangeKind::Content => { console::info(&format!("-> Content renamed {}", path.display())); - if let Some(ref broadcaster) = broadcaster { - // Force refresh - rebuild_done_handling( - broadcaster, - rebuild::after_content_rename(&mut site, &old_path, &path), - "/x.js", - ); - } + // Force refresh + rebuild_done_handling( + &broadcaster, + rebuild::after_content_rename(&mut site, &old_path, &path), + "/x.js", + ); } ChangeKind::Templates => reload_templates(&mut site, &path), ChangeKind::StaticFiles => copy_static(&site, &path, &partial_path), @@ -391,6 +384,7 @@ pub fn serve( interface, port, output_dir, + bp.clone(), base_url, config_file, ) @@ -411,17 +405,15 @@ pub fn serve( ); let start = Instant::now(); - match detect_change_kind(&pwd, &path) { + match detect_change_kind(&bp.canonicalize().unwrap(), &path) { (ChangeKind::Content, _) => { console::info(&format!("-> Content changed {}", path.display())); - if let Some(ref broadcaster) = broadcaster { - // Force refresh - rebuild_done_handling( - broadcaster, - rebuild::after_content_change(&mut site, &path), - "/x.js", - ); - } + // Force refresh + rebuild_done_handling( + &broadcaster, + rebuild::after_content_change(&mut site, &path), + "/x.js", + ); } (ChangeKind::Templates, _) => reload_templates(&mut site, &path), (ChangeKind::StaticFiles, p) => copy_static(&site, &path, &p), @@ -432,6 +424,7 @@ pub fn serve( interface, port, output_dir, + bp.clone(), base_url, config_file, ) @@ -502,12 +495,9 @@ fn detect_change_kind(pwd: &Path, path: &Path) -> (ChangeKind, PathBuf) { /// Check if the directory at path contains any file fn is_folder_empty(dir: &Path) -> bool { // Can panic if we don't have the rights I guess? - for _ in read_dir(dir).expect("Failed to read a directory to see if it was empty") { - // If we get there, that means we have a file - return false; - } - - true + let files: Vec<_> = + read_dir(dir).expect("Failed to read a directory to see if it was empty").collect(); + files.is_empty() } #[cfg(test)] diff --git a/src/console.rs b/src/console.rs index 4d1ba19..0241cf3 100644 --- a/src/console.rs +++ b/src/console.rs @@ -1,4 +1,5 @@ use std::env; +use std::error::Error as StdError; use std::io::Write; use std::time::Instant; @@ -47,24 +48,23 @@ fn colorize(message: &str, color: &ColorSpec) { /// Display in the console the number of pages/sections in the site pub fn notify_site_size(site: &Site) { + let library = site.library.read().unwrap(); println!( "-> Creating {} pages ({} orphan), {} sections, and processing {} images", - site.library.pages().len(), - site.get_all_orphan_pages().len(), - site.library.sections().len() - 1, // -1 since we do not the index as a section + library.pages().len(), + site.get_number_orphan_pages(), + library.sections().len() - 1, // -1 since we do not the index as a section site.num_img_ops(), ); } /// Display a warning in the console if there are ignored pages in the site pub fn warn_about_ignored_pages(site: &Site) { - let ignored_pages: Vec<_> = site - .library + let library = site.library.read().unwrap(); + let ignored_pages: Vec<_> = library .sections_values() .iter() - .flat_map(|s| { - s.ignored_pages.iter().map(|k| site.library.get_page_by_key(*k).file.path.clone()) - }) + .flat_map(|s| s.ignored_pages.iter().map(|k| library.get_page_by_key(*k).file.path.clone())) .collect(); if !ignored_pages.is_empty() { @@ -96,8 +96,10 @@ pub fn unravel_errors(message: &str, error: &Error) { self::error(message); } self::error(&format!("Error: {}", error)); - for e in error.iter().skip(1) { + let mut cause = error.source(); + while let Some(e) = cause { self::error(&format!("Reason: {}", e)); + cause = e.source(); } } diff --git a/src/main.rs b/src/main.rs index 987e08b..2ad43ba 100644 --- a/src/main.rs +++ b/src/main.rs @@ -46,7 +46,12 @@ fn main() { console::info("Building site..."); let start = Instant::now(); let output_dir = matches.value_of("output_dir").unwrap(); - match cmd::build(config_file, matches.value_of("base_url"), output_dir) { + match cmd::build( + config_file, + matches.value_of("base_path"), + matches.value_of("base_url"), + output_dir, + ) { Ok(()) => console::report_elapsed_time(start), Err(e) => { console::unravel_errors("Failed to build the site", &e); @@ -79,9 +84,18 @@ fn main() { } let watch_only = matches.is_present("watch_only"); let output_dir = matches.value_of("output_dir").unwrap(); + let base_path = matches.value_of("base_path"); let base_url = matches.value_of("base_url").unwrap(); console::info("Building site..."); - match cmd::serve(interface, port, output_dir, base_url, config_file, watch_only) { + match cmd::serve( + interface, + port, + output_dir, + base_path, + base_url, + config_file, + watch_only, + ) { Ok(()) => (), Err(e) => { console::unravel_errors("", &e); diff --git a/sublime_syntaxes/PowerShell.sublime-syntax b/sublime_syntaxes/PowerShell.sublime-syntax new file mode 100644 index 0000000..6348823 --- /dev/null +++ b/sublime_syntaxes/PowerShell.sublime-syntax @@ -0,0 +1,466 @@ +%YAML 1.2 +--- +# http://www.sublimetext.com/docs/3/syntax.html +name: PowerShell +file_extensions: + - ps1 + - psm1 + - psd1 +scope: source.powershell +contexts: + main: + - match: "<#" + captures: + 0: punctuation.definition.comment.block.begin.powershell + push: + - meta_scope: comment.block.powershell + - match: "#>" + captures: + 0: punctuation.definition.comment.block.end.powershell + pop: true + - include: commentEmbeddedDocs + - match: '[2-6]>&1|>>|>|<<|<|>|>\||[1-6]>|[1-6]>>' + scope: keyword.operator.redirection.powershell + - include: commands + - include: commentLine + - include: variable + - include: interpolatedStringContent + - include: function + - include: attribute + - include: UsingDirective + - include: type + - include: hashtable + - include: doubleQuotedString + - include: scriptblock + - include: doubleQuotedStringEscapes + - match: (? + + + + + + + name + Dracula + settings + + + settings + + background + #282a36 + caret + #f8f8f0 + foreground + #f8f8f2 + invisibles + #3B3A32 + lineHighlight + #44475a + selection + #44475a + findHighlight + #effb7b + findHighlightForeground + #000000 + selectionBorder + #222218 + activeGuide + #9D550FB0 + bracketsForeground + #F8F8F2A5 + bracketsOptions + underline + bracketContentsForeground + #F8F8F2A5 + bracketContentsOptions + underline + tagsOptions + stippled_underline + + + + name + Comment + scope + comment + settings + + foreground + #6272a4 + fontStyle + + + + + name + String + scope + string + settings + + foreground + #f1fa8c + + + + name + Number + scope + constant.numeric + settings + + foreground + #bd93f9 + + + + name + Built-in constant + scope + constant.language + settings + + foreground + #bd93f9 + + + + name + User-defined constant + scope + constant.character, constant.other + settings + + foreground + #bd93f9 + + + + name + Variable + scope + variable + settings + + fontStyle + + + + + name + Ruby's @variable + scope + variable.other.readwrite.instance + settings + + fontStyle + + foreground + #ffb86c + + + + name + String interpolation + scope + constant.character.escaped, constant.character.escape, string source, string source.ruby + settings + + fontStyle + + foreground + #ff79c6 + + + + name + Ruby Regexp + scope + source.ruby string.regexp.classic.ruby,source.ruby string.regexp.mod-r.ruby + settings + + fontStyle + + foreground + #ff5555 + + + + name + Keyword + scope + keyword + settings + + foreground + #ff79c6 + + + + name + Storage + scope + storage + settings + + fontStyle + + foreground + #ff79c6 + + + + name + Storage type + scope + storage.type + settings + + fontStyle + italic + foreground + #8be9fd + + + + name + Storage Type Namespace + scope + storage.type.namespace + settings + + fontStyle + italic + foreground + #8be9fd + + + + name + Storage Type Class + scope + storage.type.class + settings + + fontStyle + italic + foreground + #ff79c6 + + + + name + Class name + scope + entity.name.class + settings + + fontStyle + underline + foreground + #8be9fd + + + + name + Meta Path + scope + meta.path + settings + + fontStyle + underline + foreground + #66d9ef + + + + name + Inherited class + scope + entity.other.inherited-class + settings + + fontStyle + italic underline + foreground + #8be9fd + + + + name + Function name + scope + entity.name.function + settings + + fontStyle + + foreground + #50fa7b + + + + name + Function argument + scope + variable.parameter + settings + + fontStyle + italic + foreground + #ffb86c + + + + name + Tag name + scope + entity.name.tag + settings + + fontStyle + + foreground + #ff79c6 + + + + name + Tag attribute + scope + entity.other.attribute-name + settings + + fontStyle + + foreground + #50fa7b + + + + name + Library function + scope + support.function + settings + + fontStyle + + foreground + #8be9fd + + + + name + Library constant + scope + support.constant + settings + + fontStyle + + foreground + #6be5fd + + + + name + Library class/type + scope + support.type, support.class + settings + + fontStyle + italic + foreground + #66d9ef + + + + name + Library variable + scope + support.other.variable + settings + + fontStyle + + + + + name + Support Other Namespace + scope + support.other.namespace + settings + + fontStyle + italic + foreground + #66d9ef + + + + name + Invalid + scope + invalid + settings + + background + #ff79c6 + fontStyle + + foreground + #F8F8F0 + + + + name + Invalid deprecated + scope + invalid.deprecated + settings + + background + #bd93f9 + foreground + #F8F8F0 + + + + name + JSON String + scope + meta.structure.dictionary.json string.quoted.double.json + settings + + foreground + #CFCFC2 + + + + name + diff.header + scope + meta.diff, meta.diff.header + settings + + foreground + #6272a4 + + + + name + diff.deleted + scope + markup.deleted + settings + + foreground + #ff79c6 + + + + name + diff.inserted + scope + markup.inserted + settings + + foreground + #50fa7b + + + + name + diff.changed + scope + markup.changed + settings + + foreground + #E6DB74 + + + + scope + constant.numeric.line-number.find-in-files - match + settings + + foreground + #bd93f9 + + + + scope + entity.name.filename + settings + + foreground + #E6DB74 + + + + scope + message.error + settings + + foreground + #F83333 + + + + name + JSON Punctuation + scope + punctuation.definition.string.begin.json - meta.structure.dictionary.value.json, punctuation.definition.string.end.json - meta.structure.dictionary.value.json + settings + + foreground + #EEEEEE + + + + name + JSON Structure + scope + meta.structure.dictionary.json string.quoted.double.json + settings + + foreground + #8be9fd + + + + name + JSON String + scope + meta.structure.dictionary.value.json string.quoted.double.json + settings + + foreground + #f1fa8c + + + + name + JSON: 6 deep + scope + meta meta meta meta meta meta meta.structure.dictionary.value string + settings + + foreground + #50fa7b + + + + name + JSON: 5 deep + scope + meta meta meta meta meta meta.structure.dictionary.value string + settings + + foreground + #ffb86c + + + + name + JSON: 4 deep + scope + meta meta meta meta meta.structure.dictionary.value string + settings + + foreground + #ff79c6 + + + + name + JSON: 3 deep + scope + meta meta meta meta.structure.dictionary.value string + settings + + foreground + #bd93f9 + + + + name + JSON: 2 deep + scope + meta meta meta.structure.dictionary.value string + settings + + foreground + #50fa7b + + + + name + JSON: 1 deep + scope + meta meta.structure.dictionary.value string + settings + + foreground + #ffb86c + + + + + + name + Markup: strike + scope + markup.strike + settings + + fontStyle + italic + foreground + #FFB86C + + + + name + Markup: bold + scope + markup.bold + settings + + fontStyle + bold + foreground + #FFB86C + + + + name + Markup: italic + scope + markup.italic + settings + + fontStyle + italic + foreground + #FFB86C + + + + name + Markdown: heading + scope + markup.heading + settings + + foreground + #8BE9FD + + + + name + Markdown: List Items Punctuation + scope + punctuation.definition.list_item.markdown + settings + + foreground + #FF79C6 + + + + name + Markdown: Blockquote + scope + markup.quote + settings + + fontStyle + italic + foreground + #6272A4 + + + + name + Markdown: Blockquote Punctuation + scope + punctuation.definition.blockquote.markdown + settings + + fontStyle + italic + background + #6272A4 + foreground + #6272A4 + + + + name + Markdown: Separator + scope + meta.separator + settings + + foreground + #6272A4 + + + + name + Markup: raw inline + scope + text.html.markdown markup.raw.inline + settings + + foreground + #50FA7B + + + + name + Markup: underline + scope + markup.underline + settings + + fontStyle + underline + foreground + #BD93F9 + + + + name + Markup: Raw block + scope + markup.raw.block + settings + + foreground + #CFCFC2 + + + + name + Markdown: Raw Block fenced source + scope + markup.raw.block.fenced.markdown source + settings + + foreground + #F8F8F2 + + + + name + Markdown: Fenced Bode Block + scope + punctuation.definition.fenced.markdown, variable.language.fenced.markdown + settings + + fontStyle + italic + foreground + #6272A4 + + + + name + Markdown: Fenced Language + scope + variable.language.fenced.markdown + settings + + fontStyle + italic + foreground + #6272A4 + + + + name + Punctuation Accessor + scope + punctuation.accessor + settings + + foreground + #FF79C6 + + + + name + Meta Function Return Type + scope + meta.function.return-type + settings + + foreground + #FF79C6 + + + + name + Punctuation Section Block Begin + scope + punctuation.section.block.begin + settings + + foreground + #ffffff + + + + name + Punctuation Section Block End + scope + punctuation.section.block.end + settings + + foreground + #ffffff + + + + name + Punctuation Section Embedded Begin + scope + punctuation.section.embedded.begin + settings + + foreground + #ff79c6 + + + + name + Punctuation Section Embedded End + scope + punctuation.section.embedded.end + settings + + foreground + #ff79c6 + + + + name + Punctuation Separator Namespace + scope + punctuation.separator.namespace + settings + + foreground + #ff79c6 + + + + name + Variable Function + scope + variable.function + settings + + foreground + #50fa7b + + + + name + Variable Other + scope + variable.other + settings + + foreground + #ffffff + + + + name + Variable Language + scope + variable.language + settings + + foreground + #bd93f9 + + + + name + Entity Name Module Ruby + scope + entity.name.module.ruby + settings + + foreground + #8be9fd + + + + name + Entity Name Constant Ruby + scope + entity.name.constant.ruby + settings + + foreground + #bd93f9 + + + + name + Support Function Builtin Ruby + scope + support.function.builtin.ruby + settings + + foreground + #ffffff + + + + name + Storage Type Namespace CS + scope + storage.type.namespace.cs + settings + + foreground + #ff79c6 + + + + name + Entity Name Namespace CS + scope + entity.name.namespace.cs + settings + + foreground + #8be9fd + + + + uuid + 83091B89-765E-4F0D-9275-0EC6CB084126 + colorSpaceName + sRGB + semanticClass + theme.dracula + author + Zeno Rocha + + diff --git a/test_site/content/_index.md b/test_site/content/_index.md new file mode 100644 index 0000000..ac36e06 --- /dev/null +++ b/test_site/content/_index.md @@ -0,0 +1,2 @@ ++++ ++++ diff --git a/test_site/templates/page.html b/test_site/templates/page.html index 275de86..d0e0f3e 100644 --- a/test_site/templates/page.html +++ b/test_site/templates/page.html @@ -3,6 +3,7 @@ {% block content %} {{ page.content | safe }} {{ page.relative_path | safe }} + {{ toc }} {% if page.earlier %}Previous article: {{ page.earlier.permalink }}{% endif %} {% if page.later %}Next article: {{ page.later.permalink }}{% endif %} diff --git a/test_site/themes/sample/templates/404.html b/test_site/themes/sample/templates/404.html new file mode 100644 index 0000000..8d430af --- /dev/null +++ b/test_site/themes/sample/templates/404.html @@ -0,0 +1 @@ +Oops \ No newline at end of file diff --git a/test_site_i18n/config.toml b/test_site_i18n/config.toml new file mode 100644 index 0000000..98be3c6 --- /dev/null +++ b/test_site_i18n/config.toml @@ -0,0 +1,27 @@ +# The URL the site will be built for +base_url = "https://example.com" + +# Whether to automatically compile all Sass files in the sass directory +compile_sass = false + +# Whether to do syntax highlighting +# Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola +highlight_code = false + +# Whether to build a search index to be used later on by a JavaScript library +build_search_index = false + +generate_rss = true + +taxonomies = [ + {name = "authors", rss = true}, + {name = "auteurs", lang = "fr"}, +] + +languages = [ + {code = "fr", rss = true}, + {code = "it", rss = false}, +] + +[extra] +# Put all your custom variables here diff --git a/test_site_i18n/content/base.fr.md b/test_site_i18n/content/base.fr.md new file mode 100644 index 0000000..2ca2fc7 --- /dev/null +++ b/test_site_i18n/content/base.fr.md @@ -0,0 +1,5 @@ ++++ +title = "Une page" ++++ + +Une page en Français diff --git a/test_site_i18n/content/base.md b/test_site_i18n/content/base.md new file mode 100644 index 0000000..b759c2d --- /dev/null +++ b/test_site_i18n/content/base.md @@ -0,0 +1,5 @@ ++++ +title = "A page" ++++ + +A page in english diff --git a/test_site_i18n/content/blog/_index.fr.md b/test_site_i18n/content/blog/_index.fr.md new file mode 100644 index 0000000..2f97f2f --- /dev/null +++ b/test_site_i18n/content/blog/_index.fr.md @@ -0,0 +1,5 @@ ++++ +title = "Mon blog" +sort_by = "date" +insert_anchors = "right" ++++ diff --git a/test_site_i18n/content/blog/_index.it.md b/test_site_i18n/content/blog/_index.it.md new file mode 100644 index 0000000..5e00882 --- /dev/null +++ b/test_site_i18n/content/blog/_index.it.md @@ -0,0 +1,5 @@ ++++ +title = "Il mio blog" +sort_by = "date" +insert_anchors = "right" ++++ diff --git a/test_site_i18n/content/blog/_index.md b/test_site_i18n/content/blog/_index.md new file mode 100644 index 0000000..a0b4240 --- /dev/null +++ b/test_site_i18n/content/blog/_index.md @@ -0,0 +1,5 @@ ++++ +title = "My blog" +sort_by = "date" +insert_anchors = "left" ++++ diff --git a/test_site_i18n/content/blog/fixed-slug.fr.md b/test_site_i18n/content/blog/fixed-slug.fr.md new file mode 100644 index 0000000..c9732a8 --- /dev/null +++ b/test_site_i18n/content/blog/fixed-slug.fr.md @@ -0,0 +1,7 @@ ++++ +title = "Un slug fixe" +slug = "something-else" +date = 2017-01-01 ++++ + +Une page qui definit son slug dans le front-matter diff --git a/test_site_i18n/content/blog/fixed-slug.it.md b/test_site_i18n/content/blog/fixed-slug.it.md new file mode 100644 index 0000000..fcd918e --- /dev/null +++ b/test_site_i18n/content/blog/fixed-slug.it.md @@ -0,0 +1,7 @@ ++++ +title = "Un slug fixe" +slug = "something-else" +date = 2017-01-01 ++++ + +Una pagina che definisce il suo slug nel front-matter diff --git a/test_site_i18n/content/blog/fixed-slug.md b/test_site_i18n/content/blog/fixed-slug.md new file mode 100644 index 0000000..f51ed29 --- /dev/null +++ b/test_site_i18n/content/blog/fixed-slug.md @@ -0,0 +1,12 @@ ++++ +title = "Fixed slug" +slug = "something-else" +date = 2017-01-01 ++++ + +A simple page with a slug defined + +# Title + +Hey + diff --git a/test_site_i18n/content/blog/not-translated.md b/test_site_i18n/content/blog/not-translated.md new file mode 100644 index 0000000..8c06035 --- /dev/null +++ b/test_site_i18n/content/blog/not-translated.md @@ -0,0 +1,5 @@ ++++ +date = 2018-08-19 ++++ + +Something not translated diff --git a/test_site_i18n/content/blog/something.fr.md b/test_site_i18n/content/blog/something.fr.md new file mode 100644 index 0000000..22579a8 --- /dev/null +++ b/test_site_i18n/content/blog/something.fr.md @@ -0,0 +1,9 @@ ++++ +title = "Quelque chose" +date = 2018-10-09 + +[taxonomies] +auteurs = ["Vincent Prouillet"] ++++ + +Un article diff --git a/test_site_i18n/content/blog/something.md b/test_site_i18n/content/blog/something.md new file mode 100644 index 0000000..587edd8 --- /dev/null +++ b/test_site_i18n/content/blog/something.md @@ -0,0 +1,9 @@ ++++ +title = "Something" +date = 2018-10-09 + +[taxonomies] +authors = ["Queen Elizabeth"] ++++ + +A blog post diff --git a/test_site_i18n/content/blog/with-assets/index.fr.md b/test_site_i18n/content/blog/with-assets/index.fr.md new file mode 100644 index 0000000..550ca78 --- /dev/null +++ b/test_site_i18n/content/blog/with-assets/index.fr.md @@ -0,0 +1,5 @@ ++++ +date = 2018-11-10 ++++ + +Avec des fichiers diff --git a/test_site_i18n/content/blog/with-assets/index.md b/test_site_i18n/content/blog/with-assets/index.md new file mode 100644 index 0000000..ada03fa --- /dev/null +++ b/test_site_i18n/content/blog/with-assets/index.md @@ -0,0 +1,5 @@ ++++ +date = 2018-11-10 ++++ + +With assets diff --git a/test_site_i18n/content/blog/with-assets/some.js b/test_site_i18n/content/blog/with-assets/some.js new file mode 100644 index 0000000..e69de29 diff --git a/test_site_i18n/templates/auteurs/list.html b/test_site_i18n/templates/auteurs/list.html new file mode 100644 index 0000000..5dfaaf8 --- /dev/null +++ b/test_site_i18n/templates/auteurs/list.html @@ -0,0 +1,3 @@ +{% for author in terms %} + {{ author.name }} {{ author.slug }} {{ author.pages | length }} +{% endfor %} diff --git a/test_site_i18n/templates/auteurs/single.html b/test_site_i18n/templates/auteurs/single.html new file mode 100644 index 0000000..0c3f8fb --- /dev/null +++ b/test_site_i18n/templates/auteurs/single.html @@ -0,0 +1,21 @@ +{% if not paginator %} + Tag: {{ term.name }} + + {% for page in term.pages %} + + {% endfor %} +{% else %} + Tag: {{ term.name }} + {% for page in paginator.pages %} + {{page.title|safe}} + {% endfor %} + Num pagers: {{ paginator.number_pagers }} + Page size: {{ paginator.paginate_by }} + Current index: {{ paginator.current_index }} + First: {{ paginator.first | safe }} + Last: {{ paginator.last | safe }} + {% if paginator.previous %}has_prev{% endif%} + {% if paginator.next %}has_next{% endif%} +{% endif %} diff --git a/test_site_i18n/templates/authors/list.html b/test_site_i18n/templates/authors/list.html new file mode 100644 index 0000000..3b8116f --- /dev/null +++ b/test_site_i18n/templates/authors/list.html @@ -0,0 +1,3 @@ +{% for term in terms %} + {{ term.name }} {{ term.slug }} {{ term.pages | length }} +{% endfor %} diff --git a/test_site_i18n/templates/authors/single.html b/test_site_i18n/templates/authors/single.html new file mode 100644 index 0000000..0c3f8fb --- /dev/null +++ b/test_site_i18n/templates/authors/single.html @@ -0,0 +1,21 @@ +{% if not paginator %} + Tag: {{ term.name }} + + {% for page in term.pages %} + + {% endfor %} +{% else %} + Tag: {{ term.name }} + {% for page in paginator.pages %} + {{page.title|safe}} + {% endfor %} + Num pagers: {{ paginator.number_pagers }} + Page size: {{ paginator.paginate_by }} + Current index: {{ paginator.current_index }} + First: {{ paginator.first | safe }} + Last: {{ paginator.last | safe }} + {% if paginator.previous %}has_prev{% endif%} + {% if paginator.next %}has_next{% endif%} +{% endif %} diff --git a/test_site_i18n/templates/index.html b/test_site_i18n/templates/index.html new file mode 100644 index 0000000..e1b47f1 --- /dev/null +++ b/test_site_i18n/templates/index.html @@ -0,0 +1,4 @@ +{% for page in section.pages %} + {{page.title}} +{% endfor %} +Language: {{lang}} diff --git a/test_site_i18n/templates/page.html b/test_site_i18n/templates/page.html new file mode 100644 index 0000000..585468d --- /dev/null +++ b/test_site_i18n/templates/page.html @@ -0,0 +1,8 @@ +{{page.title}} +{{page.content | safe}} +Language: {{lang}} + +{% for t in page.translations %} +Translated in {{t.lang|default(value=config.default_language)}}: {{t.title}} {{t.permalink|safe}} +{% endfor %} + diff --git a/test_site_i18n/templates/section.html b/test_site_i18n/templates/section.html new file mode 100644 index 0000000..1f5e03c --- /dev/null +++ b/test_site_i18n/templates/section.html @@ -0,0 +1,8 @@ +{% for page in section.pages %} + {{page.title}} +{% endfor %} +Language: {{lang}} + +{% for t in section.translations %} +Translated in {{t.lang|default(value=config.default_language)}}: {{t.title}} {{t.permalink|safe}} +{% endfor %}