@@ -5,7 +5,7 @@ | |||||
- Fix XML templates overriding and reloading | - Fix XML templates overriding and reloading | ||||
- `title` and `description` are now optional in the front matter | - `title` and `description` are now optional in the front matter | ||||
- Add GenericConfig, Vim syntax | - Add GenericConfig, Vim syntax | ||||
- Add `_index.md` for homepage as well | |||||
- Add `_index.md` for homepage as well and make that into a normal section | |||||
- Allow sorting by `none`, `date` and `order` for sections | - Allow sorting by `none`, `date` and `order` for sections | ||||
- Add pagination | - Add pagination | ||||
@@ -2,9 +2,9 @@ | |||||
name = "gutenberg" | name = "gutenberg" | ||||
version = "0.0.4" | version = "0.0.4" | ||||
dependencies = [ | dependencies = [ | ||||
"base64 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"chrono 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", | "chrono 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"clap 2.23.3 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"clap 2.24.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", | "error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", | "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", | "iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -19,7 +19,7 @@ dependencies = [ | |||||
"staticfile 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | "staticfile 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"syntect 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | "syntect 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", | "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"tera 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"tera 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"term-painter 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", | "term-painter 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"toml 0.4.0 (git+https://github.com/alexcrichton/toml-rs)", | "toml 0.4.0 (git+https://github.com/alexcrichton/toml-rs)", | ||||
"walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", | "walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -74,7 +74,7 @@ dependencies = [ | |||||
[[package]] | [[package]] | ||||
name = "base64" | name = "base64" | ||||
version = "0.5.1" | |||||
version = "0.5.2" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
dependencies = [ | dependencies = [ | ||||
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", | "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -155,7 +155,7 @@ dependencies = [ | |||||
[[package]] | [[package]] | ||||
name = "clap" | name = "clap" | ||||
version = "2.23.3" | |||||
version = "2.24.1" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
dependencies = [ | dependencies = [ | ||||
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", | "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -282,15 +282,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
[[package]] | [[package]] | ||||
name = "hyper" | name = "hyper" | ||||
version = "0.10.9" | |||||
version = "0.10.10" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
dependencies = [ | dependencies = [ | ||||
"base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"httparse 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", | "httparse 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", | "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", | "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"mime 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", | "mime 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"num_cpus 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | "num_cpus 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", | "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", | "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | "traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -333,7 +333,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
dependencies = [ | dependencies = [ | ||||
"conduit-mime-types 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", | "conduit-mime-types 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"error 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", | "error 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"hyper 0.10.9 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"hyper 0.10.10 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", | "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", | "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"modifier 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | "modifier 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -421,7 +421,7 @@ dependencies = [ | |||||
"libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", | "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"miow 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", | "miow 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"net2 0.2.27 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"net2 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"nix 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", | "nix 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", | "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", | "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -439,7 +439,7 @@ dependencies = [ | |||||
"libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", | "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", | "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"miow 0.2.1 (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.27 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"net2 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
] | ] | ||||
@@ -450,7 +450,7 @@ version = "0.1.5" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
dependencies = [ | dependencies = [ | ||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"net2 0.2.27 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"net2 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
] | ] | ||||
@@ -461,7 +461,7 @@ version = "0.2.1" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
dependencies = [ | dependencies = [ | ||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"net2 0.2.27 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"net2 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
] | ] | ||||
@@ -482,7 +482,7 @@ dependencies = [ | |||||
[[package]] | [[package]] | ||||
name = "net2" | name = "net2" | ||||
version = "0.2.27" | |||||
version = "0.2.29" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
dependencies = [ | dependencies = [ | ||||
"cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -726,7 +726,7 @@ dependencies = [ | |||||
[[package]] | [[package]] | ||||
name = "serde_json" | name = "serde_json" | ||||
version = "1.0.1" | |||||
version = "1.0.2" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
dependencies = [ | dependencies = [ | ||||
"dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", | "dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -820,7 +820,7 @@ dependencies = [ | |||||
[[package]] | [[package]] | ||||
name = "tera" | name = "tera" | ||||
version = "0.10.1" | |||||
version = "0.10.3" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
dependencies = [ | dependencies = [ | ||||
"chrono 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", | "chrono 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -831,7 +831,7 @@ dependencies = [ | |||||
"pest 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", | "pest 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | "regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"serde 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", | "serde 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"serde_json 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"slug 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", | "slug 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | "url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
] | ] | ||||
@@ -1072,7 +1072,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
"checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159" | "checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159" | ||||
"checksum backtrace 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f551bc2ddd53aea015d453ef0b635af89444afa5ed2405dd0b2062ad5d600d80" | "checksum backtrace 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f551bc2ddd53aea015d453ef0b635af89444afa5ed2405dd0b2062ad5d600d80" | ||||
"checksum backtrace-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d192fd129132fbc97497c1f2ec2c2c5174e376b95f535199ef4fe0a293d33842" | "checksum backtrace-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d192fd129132fbc97497c1f2ec2c2c5174e376b95f535199ef4fe0a293d33842" | ||||
"checksum base64 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "124e5332dfc4e387b4ca058909aa175c0c3eccf03846b7c1a969b9ad067b8df2" | |||||
"checksum base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30e93c03064e7590d0466209155251b90c22e37fab1daf2771582598b5827557" | |||||
"checksum bincode 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "55eb0b7fd108527b0c77860f75eca70214e11a8b4c6ef05148c54c05a25d48ad" | "checksum bincode 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "55eb0b7fd108527b0c77860f75eca70214e11a8b4c6ef05148c54c05a25d48ad" | ||||
"checksum bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dead7461c1127cf637931a1e50934eb6eee8bff2f74433ac7909e9afcee04a3" | "checksum bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dead7461c1127cf637931a1e50934eb6eee8bff2f74433ac7909e9afcee04a3" | ||||
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" | "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" | ||||
@@ -1084,7 +1084,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c" | "checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c" | ||||
"checksum chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9213f7cd7c27e95c2b57c49f0e69b1ea65b27138da84a170133fd21b07659c00" | "checksum chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9213f7cd7c27e95c2b57c49f0e69b1ea65b27138da84a170133fd21b07659c00" | ||||
"checksum chrono 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d9123be86fd2a8f627836c235ecdf331fdd067ecf7ac05aa1a68fbcf2429f056" | "checksum chrono 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d9123be86fd2a8f627836c235ecdf331fdd067ecf7ac05aa1a68fbcf2429f056" | ||||
"checksum clap 2.23.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f57e9b63057a545ad2ecd773ea61e49422ed1b1d63d74d5da5ecaee55b3396cd" | |||||
"checksum clap 2.24.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7541069be0b8aec41030802abe8b5cdef0490070afaa55418adea93b1e431e0" | |||||
"checksum cmake 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "d18d68987ed4c516dcc3e7913659bfa4076f5182eea4a7e0038bb060953e76ac" | "checksum cmake 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "d18d68987ed4c516dcc3e7913659bfa4076f5182eea4a7e0038bb060953e76ac" | ||||
"checksum conduit-mime-types 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "95ca30253581af809925ef68c2641cc140d6183f43e12e0af4992d53768bd7b8" | "checksum conduit-mime-types 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "95ca30253581af809925ef68c2641cc140d6183f43e12e0af4992d53768bd7b8" | ||||
"checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850" | "checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850" | ||||
@@ -1101,7 +1101,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" | "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" | ||||
"checksum httparse 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77f756bed9ee3a83ce98774f4155b42a31b787029013f3a7d83eca714e500e21" | "checksum httparse 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77f756bed9ee3a83ce98774f4155b42a31b787029013f3a7d83eca714e500e21" | ||||
"checksum humansize 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "92d211e6e70b05749dce515b47684f29a3c8c38bbbb21c50b30aff9eca1b0bd3" | "checksum humansize 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "92d211e6e70b05749dce515b47684f29a3c8c38bbbb21c50b30aff9eca1b0bd3" | ||||
"checksum hyper 0.10.9 (registry+https://github.com/rust-lang/crates.io-index)" = "94da93321c171e26481afeebe8288757b0501901b7c5492648163d8ec4942ec5" | |||||
"checksum hyper 0.10.10 (registry+https://github.com/rust-lang/crates.io-index)" = "36e108e0b1fa2d17491cbaac4bc460dc0956029d10ccf83c913dd0e5db3e7f07" | |||||
"checksum idna 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6ac85ec3f80c8e4e99d9325521337e14ec7555c458a14e377d189659a427f375" | "checksum idna 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6ac85ec3f80c8e4e99d9325521337e14ec7555c458a14e377d189659a427f375" | ||||
"checksum inotify 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887fcc180136e77a85e6a6128579a719027b1bab9b1c38ea4444244fe262c20c" | "checksum inotify 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887fcc180136e77a85e6a6128579a719027b1bab9b1c38ea4444244fe262c20c" | ||||
"checksum iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29d062ee61fccdf25be172e70f34c9f6efc597e1fb8f6526e8437b2046ab26be" | "checksum iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29d062ee61fccdf25be172e70f34c9f6efc597e1fb8f6526e8437b2046ab26be" | ||||
@@ -1123,7 +1123,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" | "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" | ||||
"checksum modifier 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41f5c9112cb662acd3b204077e0de5bc66305fa8df65c8019d5adb10e9ab6e58" | "checksum modifier 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41f5c9112cb662acd3b204077e0de5bc66305fa8df65c8019d5adb10e9ab6e58" | ||||
"checksum mount 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "32245731923cd096899502fc4c4317cfd09f121e80e73f7f576cf3777a824256" | "checksum mount 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "32245731923cd096899502fc4c4317cfd09f121e80e73f7f576cf3777a824256" | ||||
"checksum net2 0.2.27 (registry+https://github.com/rust-lang/crates.io-index)" = "18b9642ad6222faf5ce46f6966f59b71b9775ad5758c9e09fcf0a6c8061972b4" | |||||
"checksum net2 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)" = "bc01404e7568680f1259aa5729539f221cb1e6d047a0d9053cab4be8a73b5d67" | |||||
"checksum nix 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bfb3ddedaa14746434a02041940495bf11325c22f6d36125d3bdd56090d50a79" | "checksum nix 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bfb3ddedaa14746434a02041940495bf11325c22f6d36125d3bdd56090d50a79" | ||||
"checksum notify 4.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "298d4401ff2c6cebb7f8944c90288647c89ce59029d43b439444cf1067df55e1" | "checksum notify 4.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "298d4401ff2c6cebb7f8944c90288647c89ce59029d43b439444cf1067df55e1" | ||||
"checksum num 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "98b15ba84e910ea7a1973bccd3df7b31ae282bf9d8bd2897779950c9b8303d40" | "checksum num 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "98b15ba84e910ea7a1973bccd3df7b31ae282bf9d8bd2897779950c9b8303d40" | ||||
@@ -1154,7 +1154,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
"checksum serde 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3b46a59dd63931010fdb1d88538513f3279090d88b5c22ef4fe8440cfffcc6e3" | "checksum serde 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3b46a59dd63931010fdb1d88538513f3279090d88b5c22ef4fe8440cfffcc6e3" | ||||
"checksum serde_derive 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6c06b68790963518008b8ae0152d48be4bbbe77015d2c717f6282eea1824be9a" | "checksum serde_derive 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6c06b68790963518008b8ae0152d48be4bbbe77015d2c717f6282eea1824be9a" | ||||
"checksum serde_derive_internals 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "021c338d22c7e30f957a6ab7e388cb6098499dda9fd4ba1661ee074ca7a180d1" | "checksum serde_derive_internals 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "021c338d22c7e30f957a6ab7e388cb6098499dda9fd4ba1661ee074ca7a180d1" | ||||
"checksum serde_json 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1c62115693d0a9ed8c32d1c760f0fdbe7d4b05cb13c135b9b54137ac0d59fccb" | |||||
"checksum serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "48b04779552e92037212c3615370f6bd57a40ebba7f20e554ff9f55e41a69a7b" | |||||
"checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c" | "checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c" | ||||
"checksum slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d807fd58c4181bbabed77cb3b891ba9748241a552bcc5be698faaebefc54f46e" | "checksum slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d807fd58c4181bbabed77cb3b891ba9748241a552bcc5be698faaebefc54f46e" | ||||
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" | "checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" | ||||
@@ -1165,7 +1165,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" | "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" | ||||
"checksum syntect 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24204b1f4bdd49f84e5f4b219d0bf1dc45ac2fd7fc46320ab6627b537d6d4b69" | "checksum syntect 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24204b1f4bdd49f84e5f4b219d0bf1dc45ac2fd7fc46320ab6627b537d6d4b69" | ||||
"checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6" | "checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6" | ||||
"checksum tera 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "462408d239ffff6439089628a5fa7fec1bce31e6a633780baea93d31c64070af" | |||||
"checksum tera 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "86bc1156f5502b5eb3904348f4bea155d728e51fec7c981c44b3f1d10b8e574b" | |||||
"checksum term 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d168af3930b369cfe245132550579d47dfd873d69470755a19c2c6568dbbd989" | "checksum term 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d168af3930b369cfe245132550579d47dfd873d69470755a19c2c6568dbbd989" | ||||
"checksum term-painter 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ab900bf2f05175932b13d4fc12f8ff09ef777715b04998791ab2c930841e496b" | "checksum term-painter 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ab900bf2f05175932b13d4fc12f8ff09ef777715b04998791ab2c930841e496b" | ||||
"checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209" | "checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209" | ||||
@@ -52,7 +52,7 @@ Each kind of page get their own variables: | |||||
// TODO: detail the schema of the variables | // TODO: detail the schema of the variables | ||||
- index.html: gets `pages` that contain all pages in the site | |||||
- index.html: gets `section` representing the index section and all `sections` | |||||
- page.html: gets `page` that contains the data for that page | - page.html: gets `page` that contains the data for that page | ||||
- section.html: gets `section` that contains the data for pages in it and its subsections | - section.html: gets `section` that contains the data for pages in it and its subsections | ||||
- tags.html: gets `tags` | - tags.html: gets `tags` | ||||
@@ -41,8 +41,7 @@ fn bench_populate_previous_and_next_pages(b: &mut test::Bencher) { | |||||
path.push("test_site"); | path.push("test_site"); | ||||
let mut site = Site::new(&path, "config.toml").unwrap(); | let mut site = Site::new(&path, "config.toml").unwrap(); | ||||
site.load().unwrap(); | site.load().unwrap(); | ||||
let mut pages = site.pages.values().cloned().collect::<Vec<_>>(); | |||||
pages.sort_by(|a, b| a.partial_cmp(b).unwrap()); | |||||
let pages = site.pages.values().cloned().collect::<Vec<_>>(); | |||||
b.iter(|| populate_previous_and_next_pages(pages.as_slice())); | b.iter(|| populate_previous_and_next_pages(pages.as_slice())); | ||||
} | } |
@@ -7,6 +7,7 @@ use gutenberg::Site; | |||||
pub fn build(config_file: &str) -> Result<()> { | pub fn build(config_file: &str) -> Result<()> { | ||||
let mut site = Site::new(env::current_dir().unwrap(), config_file)?; | let mut site = Site::new(env::current_dir().unwrap(), config_file)?; | ||||
site.load()?; | site.load()?; | ||||
println!("-> Creating {} pages and {} sections", site.pages.len(), site.sections.len()); | |||||
super::notify_site_size(&site); | |||||
super::warn_about_ignored_pages(&site); | |||||
site.build() | site.build() | ||||
} | } |
@@ -5,3 +5,29 @@ mod serve; | |||||
pub use self::init::create_new_project; | pub use self::init::create_new_project; | ||||
pub use self::build::build; | pub use self::build::build; | ||||
pub use self::serve::serve; | pub use self::serve::serve; | ||||
use gutenberg::Site; | |||||
use console::warn; | |||||
fn notify_site_size(site: &Site) { | |||||
println!( | |||||
"-> Creating {} pages ({} orphan) and {} sections", | |||||
site.pages.len(), | |||||
site.get_all_orphan_pages().len(), | |||||
site.sections.len() | |||||
); | |||||
} | |||||
fn warn_about_ignored_pages(site: &Site) { | |||||
let ignored_pages = site.get_ignored_pages(); | |||||
if !ignored_pages.is_empty() { | |||||
warn(&format!( | |||||
"{} page(s) ignored (missing date or order in a sorted section):", | |||||
ignored_pages.len() | |||||
)); | |||||
for path in site.get_ignored_pages() { | |||||
warn(&format!("- {}", path.display())); | |||||
} | |||||
} | |||||
} |
@@ -67,7 +67,8 @@ pub fn serve(interface: &str, port: &str, config_file: &str) -> Result<()> { | |||||
site.load()?; | site.load()?; | ||||
site.enable_live_reload(); | site.enable_live_reload(); | ||||
println!("-> Creating {} pages and {} sections", site.pages.len(), site.sections.len()); | |||||
super::notify_site_size(&site); | |||||
super::warn_about_ignored_pages(&site); | |||||
site.build()?; | site.build()?; | ||||
report_elapsed_time(start); | report_elapsed_time(start); | ||||
@@ -6,6 +6,10 @@ pub fn info(message: &str) { | |||||
println!("{}", NotSet.bold().paint(message)); | println!("{}", NotSet.bold().paint(message)); | ||||
} | } | ||||
pub fn warn(message: &str) { | |||||
println!("{}", Yellow.bold().paint(message)); | |||||
} | |||||
pub fn success(message: &str) { | pub fn success(message: &str) { | ||||
println!("{}", Green.bold().paint(message)); | println!("{}", Green.bold().paint(message)); | ||||
} | } | ||||
@@ -14,7 +14,7 @@ lazy_static! { | |||||
static ref PAGE_RE: Regex = Regex::new(r"^\r?\n?\+\+\+\r?\n((?s).*?(?-s))\+\+\+\r?\n?((?s).*(?-s))$").unwrap(); | static ref PAGE_RE: Regex = Regex::new(r"^\r?\n?\+\+\+\r?\n((?s).*?(?-s))\+\+\+\r?\n?((?s).*(?-s))$").unwrap(); | ||||
} | } | ||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] | |||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] | |||||
#[serde(rename_all = "lowercase")] | #[serde(rename_all = "lowercase")] | ||||
pub enum SortBy { | pub enum SortBy { | ||||
Date, | Date, | ||||
@@ -58,16 +58,15 @@ pub struct FrontMatter { | |||||
/// Path to be used by pagination: the page number will be appended after it. Defaults to `page`. | /// Path to be used by pagination: the page number will be appended after it. Defaults to `page`. | ||||
#[serde(skip_serializing)] | #[serde(skip_serializing)] | ||||
pub paginate_path: Option<String>, | pub paginate_path: Option<String>, | ||||
/// Whether to render that page/section or not. Defaults to `true`. | |||||
#[serde(skip_serializing)] | |||||
pub render: Option<bool>, | |||||
/// Any extra parameter present in the front matter | /// Any extra parameter present in the front matter | ||||
pub extra: Option<HashMap<String, Value>>, | pub extra: Option<HashMap<String, Value>>, | ||||
} | } | ||||
impl FrontMatter { | impl FrontMatter { | ||||
pub fn parse(toml: &str) -> Result<FrontMatter> { | pub fn parse(toml: &str) -> Result<FrontMatter> { | ||||
if toml.trim() == "" { | |||||
bail!("Front matter of file is missing"); | |||||
} | |||||
let mut f: FrontMatter = match toml::from_str(toml) { | let mut f: FrontMatter = match toml::from_str(toml) { | ||||
Ok(d) => d, | Ok(d) => d, | ||||
Err(e) => bail!(e), | Err(e) => bail!(e), | ||||
@@ -89,7 +88,9 @@ impl FrontMatter { | |||||
f.paginate_path = Some("page".to_string()); | f.paginate_path = Some("page".to_string()); | ||||
} | } | ||||
if f.render.is_none() { | |||||
f.render = Some(true); | |||||
} | |||||
Ok(f) | Ok(f) | ||||
} | } | ||||
@@ -112,10 +113,11 @@ impl FrontMatter { | |||||
self.order.unwrap() | self.order.unwrap() | ||||
} | } | ||||
/// Returns the current sorting method, defaults to `None` (== no sorting) | |||||
pub fn sort_by(&self) -> SortBy { | pub fn sort_by(&self) -> SortBy { | ||||
match self.sort_by { | match self.sort_by { | ||||
Some(ref s) => s.clone(), | |||||
None => SortBy::Date, | |||||
Some(ref s) => *s, | |||||
None => SortBy::None, | |||||
} | } | ||||
} | } | ||||
@@ -126,6 +128,10 @@ impl FrontMatter { | |||||
None => false | None => false | ||||
} | } | ||||
} | } | ||||
pub fn should_render(&self) -> bool { | |||||
self.render.unwrap() | |||||
} | |||||
} | } | ||||
impl Default for FrontMatter { | impl Default for FrontMatter { | ||||
@@ -144,6 +150,7 @@ impl Default for FrontMatter { | |||||
template: None, | template: None, | ||||
paginate_by: None, | paginate_by: None, | ||||
paginate_path: None, | paginate_path: None, | ||||
render: None, | |||||
extra: None, | extra: None, | ||||
} | } | ||||
} | } | ||||
@@ -1,5 +1,4 @@ | |||||
/// A page, can be a blog post or a basic page | /// A page, can be a blog post or a basic page | ||||
use std::cmp::Ordering; | |||||
use std::collections::HashMap; | use std::collections::HashMap; | ||||
use std::fs::{read_dir}; | use std::fs::{read_dir}; | ||||
use std::path::{Path, PathBuf}; | use std::path::{Path, PathBuf}; | ||||
@@ -13,7 +12,6 @@ use slug::slugify; | |||||
use errors::{Result, ResultExt}; | use errors::{Result, ResultExt}; | ||||
use config::Config; | use config::Config; | ||||
use front_matter::{FrontMatter, SortBy, split_content}; | use front_matter::{FrontMatter, SortBy, split_content}; | ||||
use section::Section; | |||||
use markdown::markdown_to_html; | use markdown::markdown_to_html; | ||||
use utils::{read_file, find_content_components}; | use utils::{read_file, find_content_components}; | ||||
@@ -145,23 +143,27 @@ impl Page { | |||||
// 4. Find sections | // 4. Find sections | ||||
// Pages with custom urls exists outside of sections | // Pages with custom urls exists outside of sections | ||||
let mut path_set = false; | |||||
if let Some(ref u) = page.meta.url { | if let Some(ref u) = page.meta.url { | ||||
page.path = u.trim().to_string(); | page.path = u.trim().to_string(); | ||||
} else if !page.components.is_empty() { | |||||
path_set = true; | |||||
} | |||||
if !page.components.is_empty() { | |||||
// If we have a folder with an asset, don't consider it as a component | // If we have a folder with an asset, don't consider it as a component | ||||
if page.file_name == "index" { | if page.file_name == "index" { | ||||
page.components.pop(); | page.components.pop(); | ||||
// also set parent_path to grandparent instead | // also set parent_path to grandparent instead | ||||
page.parent_path = page.parent_path.parent().unwrap().to_path_buf(); | page.parent_path = page.parent_path.parent().unwrap().to_path_buf(); | ||||
} | } | ||||
// Don't add a trailing slash to sections | |||||
page.path = format!("{}/{}", page.components.join("/"), page.slug); | |||||
} else { | |||||
if !path_set { | |||||
// Don't add a trailing slash to sections | |||||
page.path = format!("{}/{}", page.components.join("/"), page.slug); | |||||
} | |||||
} else if !path_set { | |||||
page.path = page.slug.clone(); | page.path = page.slug.clone(); | ||||
} | } | ||||
page.permalink = config.make_permalink(&page.path); | page.permalink = config.make_permalink(&page.path); | ||||
Ok(page) | Ok(page) | ||||
@@ -243,13 +245,7 @@ impl ser::Serialize for Page { | |||||
/// | /// | ||||
/// Any pages that doesn't have a date when the sorting method is date or order | /// Any pages that doesn't have a date when the sorting method is date or order | ||||
/// when the sorting method is order will be ignored. | /// when the sorting method is order will be ignored. | ||||
pub fn sort_pages(pages: Vec<Page>, section: Option<&Section>) -> (Vec<Page>, Vec<Page>) { | |||||
let sort_by = if let Some(s) = section { | |||||
s.meta.sort_by() | |||||
} else { | |||||
SortBy::None | |||||
}; | |||||
pub fn sort_pages(pages: Vec<Page>, sort_by: SortBy) -> (Vec<Page>, Vec<Page>) { | |||||
match sort_by { | match sort_by { | ||||
SortBy::Date => { | SortBy::Date => { | ||||
let mut can_be_sorted = vec![]; | let mut can_be_sorted = vec![]; | ||||
@@ -290,32 +286,6 @@ pub fn sort_pages(pages: Vec<Page>, section: Option<&Section>) -> (Vec<Page>, Ve | |||||
} | } | ||||
} | } | ||||
/// Used only by the RSS feed (I think) | |||||
impl PartialOrd for Page { | |||||
fn partial_cmp(&self, other: &Page) -> Option<Ordering> { | |||||
if self.meta.date.is_none() { | |||||
return Some(Ordering::Less); | |||||
} | |||||
if other.meta.date.is_none() { | |||||
return Some(Ordering::Greater); | |||||
} | |||||
let this_date = self.meta.date().unwrap(); | |||||
let other_date = other.meta.date().unwrap(); | |||||
if this_date > other_date { | |||||
return Some(Ordering::Less); | |||||
} | |||||
if this_date < other_date { | |||||
return Some(Ordering::Greater); | |||||
} | |||||
Some(Ordering::Equal) | |||||
} | |||||
} | |||||
/// Horribly inefficient way to set previous and next on each pages | /// Horribly inefficient way to set previous and next on each pages | ||||
/// So many clones | /// So many clones | ||||
pub fn populate_previous_and_next_pages(input: &[Page]) -> Vec<Page> { | pub fn populate_previous_and_next_pages(input: &[Page]) -> Vec<Page> { | ||||
@@ -347,10 +317,8 @@ mod tests { | |||||
use tempdir::TempDir; | use tempdir::TempDir; | ||||
use std::fs::File; | use std::fs::File; | ||||
use std::path::Path; | |||||
use front_matter::{FrontMatter, SortBy}; | use front_matter::{FrontMatter, SortBy}; | ||||
use section::Section; | |||||
use super::{Page, find_related_assets, sort_pages, populate_previous_and_next_pages}; | use super::{Page, find_related_assets, sort_pages, populate_previous_and_next_pages}; | ||||
fn create_page_with_date(date: &str) -> Page { | fn create_page_with_date(date: &str) -> Page { | ||||
@@ -381,20 +349,6 @@ mod tests { | |||||
assert_eq!(assets.iter().filter(|p| p.file_name().unwrap() == "fail.png").count(), 1); | assert_eq!(assets.iter().filter(|p| p.file_name().unwrap() == "fail.png").count(), 1); | ||||
} | } | ||||
#[test] | |||||
fn test_can_default_sort() { | |||||
let input = vec![ | |||||
create_page_with_date("2018-01-01"), | |||||
create_page_with_date("2017-01-01"), | |||||
create_page_with_date("2019-01-01"), | |||||
]; | |||||
let (pages, _) = sort_pages(input, None); | |||||
// Should be sorted by date | |||||
assert_eq!(pages[0].clone().meta.date.unwrap(), "2018-01-01"); | |||||
assert_eq!(pages[1].clone().meta.date.unwrap(), "2017-01-01"); | |||||
assert_eq!(pages[2].clone().meta.date.unwrap(), "2019-01-01"); | |||||
} | |||||
#[test] | #[test] | ||||
fn test_can_sort_dates() { | fn test_can_sort_dates() { | ||||
let input = vec![ | let input = vec![ | ||||
@@ -402,10 +356,7 @@ mod tests { | |||||
create_page_with_date("2017-01-01"), | create_page_with_date("2017-01-01"), | ||||
create_page_with_date("2019-01-01"), | create_page_with_date("2019-01-01"), | ||||
]; | ]; | ||||
let mut front_matter = FrontMatter::default(); | |||||
front_matter.sort_by = Some(SortBy::Date); | |||||
let section = Section::new(Path::new("hey"), front_matter); | |||||
let (pages, _) = sort_pages(input, Some(§ion)); | |||||
let (pages, _) = sort_pages(input, SortBy::Date); | |||||
// Should be sorted by date | // Should be sorted by date | ||||
assert_eq!(pages[0].clone().meta.date.unwrap(), "2019-01-01"); | assert_eq!(pages[0].clone().meta.date.unwrap(), "2019-01-01"); | ||||
assert_eq!(pages[1].clone().meta.date.unwrap(), "2018-01-01"); | assert_eq!(pages[1].clone().meta.date.unwrap(), "2018-01-01"); | ||||
@@ -419,10 +370,7 @@ mod tests { | |||||
create_page_with_order(3), | create_page_with_order(3), | ||||
create_page_with_order(1), | create_page_with_order(1), | ||||
]; | ]; | ||||
let mut front_matter = FrontMatter::default(); | |||||
front_matter.sort_by = Some(SortBy::Order); | |||||
let section = Section::new(Path::new("hey"), front_matter); | |||||
let (pages, _) = sort_pages(input, Some(§ion)); | |||||
let (pages, _) = sort_pages(input, SortBy::Order); | |||||
// Should be sorted by date | // Should be sorted by date | ||||
assert_eq!(pages[0].clone().meta.order.unwrap(), 3); | assert_eq!(pages[0].clone().meta.order.unwrap(), 3); | ||||
assert_eq!(pages[1].clone().meta.order.unwrap(), 2); | assert_eq!(pages[1].clone().meta.order.unwrap(), 2); | ||||
@@ -436,10 +384,7 @@ mod tests { | |||||
create_page_with_order(3), | create_page_with_order(3), | ||||
create_page_with_order(1), | create_page_with_order(1), | ||||
]; | ]; | ||||
let mut front_matter = FrontMatter::default(); | |||||
front_matter.sort_by = Some(SortBy::None); | |||||
let section = Section::new(Path::new("hey"), front_matter); | |||||
let (pages, _) = sort_pages(input, Some(§ion)); | |||||
let (pages, _) = sort_pages(input, SortBy::None); | |||||
// Should be sorted by date | // Should be sorted by date | ||||
assert_eq!(pages[0].clone().meta.order.unwrap(), 2); | assert_eq!(pages[0].clone().meta.order.unwrap(), 2); | ||||
assert_eq!(pages[1].clone().meta.order.unwrap(), 3); | assert_eq!(pages[1].clone().meta.order.unwrap(), 3); | ||||
@@ -453,10 +398,7 @@ mod tests { | |||||
create_page_with_order(3), | create_page_with_order(3), | ||||
create_page_with_date("2019-01-01"), | create_page_with_date("2019-01-01"), | ||||
]; | ]; | ||||
let mut front_matter = FrontMatter::default(); | |||||
front_matter.sort_by = Some(SortBy::Order); | |||||
let section = Section::new(Path::new("hey"), front_matter); | |||||
let (pages, unsorted) = sort_pages(input, Some(§ion)); | |||||
let (pages, unsorted) = sort_pages(input, SortBy::Order); | |||||
assert_eq!(pages.len(), 2); | assert_eq!(pages.len(), 2); | ||||
assert_eq!(unsorted.len(), 1); | assert_eq!(unsorted.len(), 1); | ||||
} | } | ||||
@@ -66,10 +66,10 @@ impl<'a> Paginator<'a> { | |||||
} | } | ||||
let mut pagers = vec![]; | let mut pagers = vec![]; | ||||
for index in 0..pages.len() { | |||||
for (index, page) in pages.iter().enumerate() { | |||||
// First page has no pagination path | // First page has no pagination path | ||||
if index == 0 { | if index == 0 { | ||||
pagers.push(Pager::new(1, pages[index].clone(), section.permalink.clone(), section.path.clone())); | |||||
pagers.push(Pager::new(1, page.clone(), section.permalink.clone(), section.path.clone())); | |||||
continue; | continue; | ||||
} | } | ||||
@@ -81,14 +81,12 @@ impl<'a> Paginator<'a> { | |||||
}; | }; | ||||
pagers.push(Pager::new( | pagers.push(Pager::new( | ||||
index + 1, | index + 1, | ||||
pages[index].clone(), | |||||
page.clone(), | |||||
permalink, | permalink, | ||||
if section.is_index() { format!("{}", page_path) } else { format!("{}/{}", section.path, page_path) } | |||||
if section.is_index() { page_path } else { format!("{}/{}", section.path, page_path) } | |||||
)); | )); | ||||
} | } | ||||
//println!("{:?}", pagers); | |||||
Paginator { | Paginator { | ||||
all_pages: all_pages, | all_pages: all_pages, | ||||
pagers: pagers, | pagers: pagers, | ||||
@@ -1,3 +1,4 @@ | |||||
use std::collections::HashMap; | |||||
use std::path::{Path, PathBuf}; | use std::path::{Path, PathBuf}; | ||||
use std::result::Result as StdResult; | use std::result::Result as StdResult; | ||||
@@ -8,7 +9,7 @@ use config::Config; | |||||
use front_matter::{FrontMatter, split_content}; | use front_matter::{FrontMatter, split_content}; | ||||
use errors::{Result, ResultExt}; | use errors::{Result, ResultExt}; | ||||
use utils::{read_file, find_content_components}; | use utils::{read_file, find_content_components}; | ||||
use page::{Page, sort_pages}; | |||||
use page::{Page}; | |||||
#[derive(Clone, Debug, PartialEq)] | #[derive(Clone, Debug, PartialEq)] | ||||
@@ -29,6 +30,8 @@ pub struct Section { | |||||
pub meta: FrontMatter, | pub meta: FrontMatter, | ||||
/// All direct pages of that section | /// All direct pages of that section | ||||
pub pages: Vec<Page>, | pub pages: Vec<Page>, | ||||
/// All pages that cannot be sorted in this section | |||||
pub ignored_pages: Vec<Page>, | |||||
/// All direct subsections | /// All direct subsections | ||||
pub subsections: Vec<Section>, | pub subsections: Vec<Section>, | ||||
} | } | ||||
@@ -46,6 +49,7 @@ impl Section { | |||||
permalink: "".to_string(), | permalink: "".to_string(), | ||||
meta: meta, | meta: meta, | ||||
pages: vec![], | pages: vec![], | ||||
ignored_pages: vec![], | |||||
subsections: vec![], | subsections: vec![], | ||||
} | } | ||||
} | } | ||||
@@ -56,7 +60,7 @@ impl Section { | |||||
section.components = find_content_components(§ion.file_path); | section.components = find_content_components(§ion.file_path); | ||||
section.path = section.components.join("/"); | section.path = section.components.join("/"); | ||||
section.permalink = config.make_permalink(§ion.path); | section.permalink = config.make_permalink(§ion.path); | ||||
if section.components.len() == 0 { | |||||
if section.components.is_empty() { | |||||
section.relative_path = "_index.md".to_string(); | section.relative_path = "_index.md".to_string(); | ||||
} else { | } else { | ||||
section.relative_path = format!("{}/_index.md", section.components.join("/")); | section.relative_path = format!("{}/_index.md", section.components.join("/")); | ||||
@@ -86,7 +90,7 @@ impl Section { | |||||
} | } | ||||
/// Renders the page using the default layout, unless specified in front-matter | /// Renders the page using the default layout, unless specified in front-matter | ||||
pub fn render_html(&self, tera: &Tera, config: &Config) -> Result<String> { | |||||
pub fn render_html(&self, sections: &HashMap<String, Section>, tera: &Tera, config: &Config) -> Result<String> { | |||||
let tpl_name = self.get_template_name(); | let tpl_name = self.get_template_name(); | ||||
let mut context = Context::new(); | let mut context = Context::new(); | ||||
@@ -94,13 +98,24 @@ impl Section { | |||||
context.add("section", self); | context.add("section", self); | ||||
context.add("current_url", &self.permalink); | context.add("current_url", &self.permalink); | ||||
context.add("current_path", &self.path); | context.add("current_path", &self.path); | ||||
if self.is_index() { | |||||
context.add("sections", sections); | |||||
} | |||||
tera.render(&tpl_name, &context) | tera.render(&tpl_name, &context) | ||||
.chain_err(|| format!("Failed to render section '{}'", self.file_path.display())) | .chain_err(|| format!("Failed to render section '{}'", self.file_path.display())) | ||||
} | } | ||||
/// Is this the index section? | |||||
pub fn is_index(&self) -> bool { | pub fn is_index(&self) -> bool { | ||||
self.components.len() == 0 | |||||
self.components.is_empty() | |||||
} | |||||
pub fn all_pages_path(&self) -> Vec<PathBuf> { | |||||
let mut paths = vec![]; | |||||
paths.extend(self.pages.iter().map(|p| p.file_path.clone())); | |||||
paths.extend(self.ignored_pages.iter().map(|p| p.file_path.clone())); | |||||
paths | |||||
} | } | ||||
} | } | ||||
@@ -111,8 +126,7 @@ impl ser::Serialize for Section { | |||||
state.serialize_field("description", &self.meta.description)?; | state.serialize_field("description", &self.meta.description)?; | ||||
state.serialize_field("path", &format!("/{}", self.path))?; | state.serialize_field("path", &format!("/{}", self.path))?; | ||||
state.serialize_field("permalink", &self.permalink)?; | state.serialize_field("permalink", &self.permalink)?; | ||||
let (sorted_pages, _) = sort_pages(self.pages.clone(), Some(self)); | |||||
state.serialize_field("pages", &sorted_pages)?; | |||||
state.serialize_field("pages", &self.pages)?; | |||||
state.serialize_field("subsections", &self.subsections)?; | state.serialize_field("subsections", &self.subsections)?; | ||||
state.end() | state.end() | ||||
} | } | ||||
@@ -1,4 +1,4 @@ | |||||
use std::collections::{BTreeMap, HashMap}; | |||||
use std::collections::{HashMap}; | |||||
use std::iter::FromIterator; | use std::iter::FromIterator; | ||||
use std::fs::{remove_dir_all, copy, create_dir_all}; | use std::fs::{remove_dir_all, copy, create_dir_all}; | ||||
use std::path::{Path, PathBuf}; | use std::path::{Path, PathBuf}; | ||||
@@ -14,6 +14,7 @@ use page::{Page, populate_previous_and_next_pages, sort_pages}; | |||||
use pagination::Paginator; | use pagination::Paginator; | ||||
use utils::{create_file, create_directory}; | use utils::{create_file, create_directory}; | ||||
use section::{Section}; | use section::{Section}; | ||||
use front_matter::{SortBy}; | |||||
use filters; | use filters; | ||||
@@ -76,8 +77,7 @@ pub struct Site { | |||||
pub base_path: PathBuf, | pub base_path: PathBuf, | ||||
pub config: Config, | pub config: Config, | ||||
pub pages: HashMap<PathBuf, Page>, | pub pages: HashMap<PathBuf, Page>, | ||||
pub sections: BTreeMap<PathBuf, Section>, | |||||
pub index: Option<Section>, | |||||
pub sections: HashMap<PathBuf, Section>, | |||||
pub tera: Tera, | pub tera: Tera, | ||||
live_reload: bool, | live_reload: bool, | ||||
output_path: PathBuf, | output_path: PathBuf, | ||||
@@ -104,8 +104,7 @@ impl Site { | |||||
base_path: path.to_path_buf(), | base_path: path.to_path_buf(), | ||||
config: get_config(path, config_file), | config: get_config(path, config_file), | ||||
pages: HashMap::new(), | pages: HashMap::new(), | ||||
sections: BTreeMap::new(), | |||||
index: None, | |||||
sections: HashMap::new(), | |||||
tera: tera, | tera: tera, | ||||
live_reload: false, | live_reload: false, | ||||
output_path: path.join("public"), | output_path: path.join("public"), | ||||
@@ -123,6 +122,32 @@ impl Site { | |||||
self.live_reload = true; | self.live_reload = true; | ||||
} | } | ||||
/// Gets the path of all ignored pages in the site | |||||
pub fn get_ignored_pages(&self) -> Vec<PathBuf> { | |||||
self.sections | |||||
.values() | |||||
.flat_map(|s| s.ignored_pages.iter().map(|p| p.file_path.clone())) | |||||
.collect() | |||||
} | |||||
/// Get all the orphan (== without section) pages in the site | |||||
pub fn get_all_orphan_pages(&self) -> Vec<&Page> { | |||||
let mut pages_in_sections = vec![]; | |||||
let mut orphans = vec![]; | |||||
for s in self.sections.values() { | |||||
pages_in_sections.extend(s.all_pages_path()); | |||||
} | |||||
for page in self.pages.values() { | |||||
if !pages_in_sections.contains(&page.file_path) { | |||||
orphans.push(page); | |||||
} | |||||
} | |||||
orphans | |||||
} | |||||
/// Used by tests to change the output path to a tmp dir | /// Used by tests to change the output path to a tmp dir | ||||
#[doc(hidden)] | #[doc(hidden)] | ||||
pub fn set_output_path<P: AsRef<Path>>(&mut self, path: P) { | pub fn set_output_path<P: AsRef<Path>>(&mut self, path: P) { | ||||
@@ -140,13 +165,7 @@ impl Site { | |||||
for entry in glob(&content_glob).unwrap().filter_map(|e| e.ok()) { | for entry in glob(&content_glob).unwrap().filter_map(|e| e.ok()) { | ||||
let path = entry.as_path(); | let path = entry.as_path(); | ||||
if path.file_name().unwrap() == "_index.md" { | if path.file_name().unwrap() == "_index.md" { | ||||
// Index section | |||||
if path.parent().unwrap() == self.base_path.join("content") { | |||||
self.index = Some(Section::from_file(path, &self.config)?); | |||||
} else { | |||||
// all the other sections | |||||
self.add_section(path)?; | |||||
} | |||||
self.add_section(path)?; | |||||
} else { | } else { | ||||
self.add_page(path)?; | self.add_page(path)?; | ||||
} | } | ||||
@@ -214,8 +233,10 @@ impl Site { | |||||
for (parent_path, section) in &mut self.sections { | for (parent_path, section) in &mut self.sections { | ||||
// TODO: avoid this clone | // TODO: avoid this clone | ||||
let (sorted_pages, _) = sort_pages(section.pages.clone(), Some(section)); | |||||
let (mut sorted_pages, cannot_be_sorted_pages) = sort_pages(section.pages.clone(), section.meta.sort_by()); | |||||
sorted_pages = populate_previous_and_next_pages(&sorted_pages); | |||||
section.pages = sorted_pages; | section.pages = sorted_pages; | ||||
section.ignored_pages = cannot_be_sorted_pages; | |||||
match grandparent_paths.get(parent_path) { | match grandparent_paths.get(parent_path) { | ||||
Some(paths) => section.subsections.extend(paths.clone()), | Some(paths) => section.subsections.extend(paths.clone()), | ||||
@@ -257,6 +278,14 @@ impl Site { | |||||
html | html | ||||
} | } | ||||
pub fn ensure_public_directory_exists(&self) -> Result<()> { | |||||
let public = self.output_path.clone(); | |||||
if !public.exists() { | |||||
create_directory(&public)?; | |||||
} | |||||
Ok(()) | |||||
} | |||||
/// Copy static file to public directory. | /// Copy static file to public directory. | ||||
pub fn copy_static_file<P: AsRef<Path>>(&self, path: P) -> Result<()> { | pub fn copy_static_file<P: AsRef<Path>>(&self, path: P) -> Result<()> { | ||||
let relative_path = path.as_ref().strip_prefix(&self.static_path).unwrap(); | let relative_path = path.as_ref().strip_prefix(&self.static_path).unwrap(); | ||||
@@ -298,39 +327,29 @@ impl Site { | |||||
pub fn rebuild_after_content_change(&mut self, path: &Path) -> Result<()> { | pub fn rebuild_after_content_change(&mut self, path: &Path) -> Result<()> { | ||||
let is_section = path.ends_with("_index.md"); | let is_section = path.ends_with("_index.md"); | ||||
let is_index_section = if is_section { | |||||
path.parent().unwrap() == self.base_path.join("content") | |||||
} else { | |||||
false | |||||
}; | |||||
if path.exists() { | if path.exists() { | ||||
// file exists, either a new one or updating content | // file exists, either a new one or updating content | ||||
if is_section { | if is_section { | ||||
if is_index_section { | |||||
self.index = Some(Section::from_file(path, &self.config)?); | |||||
} else { | |||||
self.add_section(path)?; | |||||
} | |||||
self.add_section(path)?; | |||||
} else { | } else { | ||||
// probably just an update so just re-parse that page | // probably just an update so just re-parse that page | ||||
// TODO: we can compare the frontmatter of the existing and new one | |||||
// to see if we need to update re-build the whole thing or just that | |||||
// page | |||||
self.add_page_and_render(path)?; | self.add_page_and_render(path)?; | ||||
} | } | ||||
} else if is_section { | } else if is_section { | ||||
// File doesn't exist -> a deletion so we remove it from everything | // File doesn't exist -> a deletion so we remove it from everything | ||||
if !is_index_section { | |||||
let relative_path = self.sections[path].relative_path.clone(); | |||||
self.sections.remove(path); | |||||
self.permalinks.remove(&relative_path); | |||||
} else { | |||||
self.index = None; | |||||
} | |||||
let relative_path = self.sections[path].relative_path.clone(); | |||||
self.sections.remove(path); | |||||
self.permalinks.remove(&relative_path); | |||||
} else { | } else { | ||||
let relative_path = self.pages[path].relative_path.clone(); | let relative_path = self.pages[path].relative_path.clone(); | ||||
self.pages.remove(path); | self.pages.remove(path); | ||||
self.permalinks.remove(&relative_path); | self.permalinks.remove(&relative_path); | ||||
} | } | ||||
// TODO: probably no need to do that, we should be able to only re-render a page or a section. | |||||
self.populate_sections(); | self.populate_sections(); | ||||
self.populate_tags_and_categories(); | self.populate_tags_and_categories(); | ||||
self.build() | self.build() | ||||
@@ -341,19 +360,16 @@ impl Site { | |||||
match path.file_name().unwrap().to_str().unwrap() { | match path.file_name().unwrap().to_str().unwrap() { | ||||
"sitemap.xml" => self.render_sitemap(), | "sitemap.xml" => self.render_sitemap(), | ||||
"rss.xml" => self.render_rss_feed(), | "rss.xml" => self.render_rss_feed(), | ||||
_ => self.build_pages() | |||||
_ => self.build() // TODO: change that | |||||
} | } | ||||
} | } | ||||
/// Renders a single content page | /// Renders a single content page | ||||
pub fn render_page(&self, page: &Page) -> Result<()> { | pub fn render_page(&self, page: &Page) -> Result<()> { | ||||
let public = self.output_path.clone(); | |||||
if !public.exists() { | |||||
create_directory(&public)?; | |||||
} | |||||
self.ensure_public_directory_exists()?; | |||||
// Copy the nesting of the content directory if we have sections for that page | // Copy the nesting of the content directory if we have sections for that page | ||||
let mut current_path = public.to_path_buf(); | |||||
let mut current_path = self.output_path.to_path_buf(); | |||||
for component in page.path.split('/') { | for component in page.path.split('/') { | ||||
current_path.push(component); | current_path.push(component); | ||||
@@ -379,76 +395,29 @@ impl Site { | |||||
Ok(()) | Ok(()) | ||||
} | } | ||||
/// Renders all content, categories, tags and index pages | |||||
pub fn build_pages(&self) -> Result<()> { | |||||
let public = self.output_path.clone(); | |||||
if !public.exists() { | |||||
create_directory(&public)?; | |||||
} | |||||
// Sort the pages first | |||||
// TODO: avoid the clone() | |||||
let (mut sorted_pages, cannot_sort_pages) = sort_pages(self.pages.values().cloned().collect(), self.index.as_ref()); | |||||
sorted_pages = populate_previous_and_next_pages(&sorted_pages); | |||||
for page in &sorted_pages { | |||||
self.render_page(page)?; | |||||
} | |||||
for page in &cannot_sort_pages { | |||||
self.render_page(page)?; | |||||
} | |||||
// Outputting categories and pages | |||||
if self.config.generate_categories_pages.unwrap() { | |||||
self.render_categories_and_tags(RenderList::Categories)?; | |||||
} | |||||
if self.config.generate_tags_pages.unwrap() { | |||||
self.render_categories_and_tags(RenderList::Tags)?; | |||||
} | |||||
// And finally the index page | |||||
let mut rendered_index = false; | |||||
// Try to render the index as a paginated page first if needed | |||||
if let Some(ref i) = self.index { | |||||
if i.meta.is_paginated() { | |||||
self.render_paginated(&self.output_path, i)?; | |||||
rendered_index = true; | |||||
} | |||||
} | |||||
// Otherwise render the default index page | |||||
if !rendered_index { | |||||
let mut context = Context::new(); | |||||
context.add("pages", &sorted_pages); | |||||
context.add("sections", &self.sections.values().collect::<Vec<&Section>>()); | |||||
context.add("config", &self.config); | |||||
context.add("current_url", &self.config.base_url); | |||||
context.add("current_path", &""); | |||||
let index = self.tera.render("index.html", &context)?; | |||||
create_file(public.join("index.html"), &self.inject_livereload(index))?; | |||||
} | |||||
Ok(()) | |||||
} | |||||
/// Builds the site to the `public` directory after deleting it | /// Builds the site to the `public` directory after deleting it | ||||
pub fn build(&self) -> Result<()> { | pub fn build(&self) -> Result<()> { | ||||
self.clean()?; | self.clean()?; | ||||
self.build_pages()?; | |||||
self.render_sections()?; | |||||
self.render_orphan_pages()?; | |||||
self.render_sitemap()?; | self.render_sitemap()?; | ||||
if self.config.generate_rss.unwrap() { | if self.config.generate_rss.unwrap() { | ||||
self.render_rss_feed()?; | self.render_rss_feed()?; | ||||
} | } | ||||
self.render_robots()?; | self.render_robots()?; | ||||
if self.config.generate_categories_pages.unwrap() { | |||||
self.render_categories_and_tags(RenderList::Categories)?; | |||||
} | |||||
if self.config.generate_tags_pages.unwrap() { | |||||
self.render_categories_and_tags(RenderList::Tags)?; | |||||
} | |||||
self.render_sections()?; | |||||
self.copy_static_directory() | self.copy_static_directory() | ||||
} | } | ||||
/// Renders robots.txt | /// Renders robots.txt | ||||
fn render_robots(&self) -> Result<()> { | fn render_robots(&self) -> Result<()> { | ||||
self.ensure_public_directory_exists()?; | |||||
create_file( | create_file( | ||||
self.output_path.join("robots.txt"), | self.output_path.join("robots.txt"), | ||||
&self.tera.render("robots.txt", &Context::new())? | &self.tera.render("robots.txt", &Context::new())? | ||||
@@ -472,6 +441,7 @@ impl Site { | |||||
} else { | } else { | ||||
("tags.html", "tag.html", "tags", "tag") | ("tags.html", "tag.html", "tags", "tag") | ||||
}; | }; | ||||
self.ensure_public_directory_exists()?; | |||||
// Create the categories/tags directory first | // Create the categories/tags directory first | ||||
let public = self.output_path.clone(); | let public = self.output_path.clone(); | ||||
@@ -497,7 +467,7 @@ impl Site { | |||||
// Now, each individual item | // Now, each individual item | ||||
for (item_name, pages_paths) in items.iter() { | for (item_name, pages_paths) in items.iter() { | ||||
let mut pages: Vec<&Page> = self.pages | |||||
let pages: Vec<&Page> = self.pages | |||||
.iter() | .iter() | ||||
.filter(|&(path, _)| pages_paths.contains(path)) | .filter(|&(path, _)| pages_paths.contains(path)) | ||||
.map(|(_, page)| page) | .map(|(_, page)| page) | ||||
@@ -505,8 +475,7 @@ impl Site { | |||||
// TODO: how to sort categories and tag content? | // TODO: how to sort categories and tag content? | ||||
// Have a setting in config.toml or a _category.md and _tag.md | // Have a setting in config.toml or a _category.md and _tag.md | ||||
// The latter is more in line with the rest of Gutenberg but order ordering | // The latter is more in line with the rest of Gutenberg but order ordering | ||||
// doesn't really work across sections so default to partial ordering for now (date) | |||||
pages.sort_by(|a, b| a.partial_cmp(b).unwrap()); | |||||
// doesn't really work across sections. | |||||
let mut context = Context::new(); | let mut context = Context::new(); | ||||
let slug = slugify(&item_name); | let slug = slugify(&item_name); | ||||
@@ -529,6 +498,7 @@ impl Site { | |||||
} | } | ||||
fn render_sitemap(&self) -> Result<()> { | fn render_sitemap(&self) -> Result<()> { | ||||
self.ensure_public_directory_exists()?; | |||||
let mut context = Context::new(); | let mut context = Context::new(); | ||||
context.add("pages", &self.pages.values().collect::<Vec<&Page>>()); | context.add("pages", &self.pages.values().collect::<Vec<&Page>>()); | ||||
context.add("sections", &self.sections.values().collect::<Vec<&Section>>()); | context.add("sections", &self.sections.values().collect::<Vec<&Section>>()); | ||||
@@ -563,20 +533,22 @@ impl Site { | |||||
} | } | ||||
fn render_rss_feed(&self) -> Result<()> { | fn render_rss_feed(&self) -> Result<()> { | ||||
self.ensure_public_directory_exists()?; | |||||
let mut context = Context::new(); | let mut context = Context::new(); | ||||
let mut pages = self.pages.values() | |||||
let pages = self.pages.values() | |||||
.filter(|p| p.meta.date.is_some()) | .filter(|p| p.meta.date.is_some()) | ||||
.take(15) // limit to the last 15 elements | .take(15) // limit to the last 15 elements | ||||
.collect::<Vec<&Page>>(); | |||||
.map(|p| p.clone()) | |||||
.collect::<Vec<Page>>(); | |||||
// Don't generate a RSS feed if none of the pages has a date | // Don't generate a RSS feed if none of the pages has a date | ||||
if pages.is_empty() { | if pages.is_empty() { | ||||
return Ok(()); | return Ok(()); | ||||
} | } | ||||
pages.sort_by(|a, b| a.partial_cmp(b).unwrap()); | |||||
context.add("pages", &pages); | |||||
context.add("last_build_date", &pages[0].meta.date); | context.add("last_build_date", &pages[0].meta.date); | ||||
let (sorted_pages, _) = sort_pages(pages, SortBy::Date); | |||||
context.add("pages", &sorted_pages); | |||||
context.add("config", &self.config); | context.add("config", &self.config); | ||||
let rss_feed_url = if self.config.base_url.ends_with('/') { | let rss_feed_url = if self.config.base_url.ends_with('/') { | ||||
@@ -594,9 +566,17 @@ impl Site { | |||||
} | } | ||||
fn render_sections(&self) -> Result<()> { | fn render_sections(&self) -> Result<()> { | ||||
self.ensure_public_directory_exists()?; | |||||
let public = self.output_path.clone(); | let public = self.output_path.clone(); | ||||
let sections: HashMap<String, Section> = self.sections | |||||
.values() | |||||
.map(|s| (s.components.join("/"), s.clone())) | |||||
.collect(); | |||||
for section in self.sections.values() { | for section in self.sections.values() { | ||||
if !section.meta.should_render() { | |||||
continue; | |||||
} | |||||
let mut output_path = public.to_path_buf(); | let mut output_path = public.to_path_buf(); | ||||
for component in §ion.components { | for component in §ion.components { | ||||
output_path.push(component); | output_path.push(component); | ||||
@@ -609,9 +589,28 @@ impl Site { | |||||
if section.meta.is_paginated() { | if section.meta.is_paginated() { | ||||
self.render_paginated(&output_path, section)?; | self.render_paginated(&output_path, section)?; | ||||
} else { | } else { | ||||
let output = section.render_html(&self.tera, &self.config)?; | |||||
let output = section.render_html( | |||||
§ions, | |||||
&self.tera, | |||||
&self.config, | |||||
)?; | |||||
create_file(output_path.join("index.html"), &self.inject_livereload(output))?; | create_file(output_path.join("index.html"), &self.inject_livereload(output))?; | ||||
} | } | ||||
for page in §ion.pages { | |||||
self.render_page(page)?; | |||||
} | |||||
} | |||||
Ok(()) | |||||
} | |||||
/// Renders all pages that do not belong to any sections | |||||
fn render_orphan_pages(&self) -> Result<()> { | |||||
self.ensure_public_directory_exists()?; | |||||
for page in self.get_all_orphan_pages() { | |||||
self.render_page(page)?; | |||||
} | } | ||||
Ok(()) | Ok(()) | ||||
@@ -619,20 +618,14 @@ impl Site { | |||||
/// Renders a list of pages when the section/index is wanting pagination. | /// Renders a list of pages when the section/index is wanting pagination. | ||||
fn render_paginated(&self, output_path: &Path, section: &Section) -> Result<()> { | fn render_paginated(&self, output_path: &Path, section: &Section) -> Result<()> { | ||||
self.ensure_public_directory_exists()?; | |||||
let paginate_path = match section.meta.paginate_path { | let paginate_path = match section.meta.paginate_path { | ||||
Some(ref s) => s.clone(), | Some(ref s) => s.clone(), | ||||
None => unreachable!() | None => unreachable!() | ||||
}; | }; | ||||
// this will sort too many times! | |||||
// TODO: make sorting happen once for everything so we don't need to sort all the time | |||||
let sorted_pages = if section.is_index() { | |||||
sort_pages(self.pages.values().cloned().collect(), self.index.as_ref()).0 | |||||
} else { | |||||
sort_pages(section.pages.clone(), Some(section)).0 | |||||
}; | |||||
let paginator = Paginator::new(&sorted_pages, section); | |||||
let paginator = Paginator::new(§ion.pages, section); | |||||
for (i, pager) in paginator.pagers.iter().enumerate() { | for (i, pager) in paginator.pagers.iter().enumerate() { | ||||
let folder_path = output_path.join(&paginate_path); | let folder_path = output_path.join(&paginate_path); | ||||
@@ -0,0 +1,2 @@ | |||||
+++ | |||||
+++ |
@@ -15,7 +15,7 @@ | |||||
<div class="content"> | <div class="content"> | ||||
{% block content %} | {% block content %} | ||||
<div class="list-posts"> | <div class="list-posts"> | ||||
{% for page in pages %} | |||||
{% for page in section.pages %} | |||||
<article> | <article> | ||||
<h3 class="post__title"><a href="{{ page.permalink }}">{{ page.title }}</a></h3> | <h3 class="post__title"><a href="{{ page.permalink }}">{{ page.title }}</a></h3> | ||||
</article> | </article> | ||||
@@ -7,7 +7,7 @@ | |||||
{% for pager in paginator.pagers %} | {% for pager in paginator.pagers %} | ||||
{{pager.index}}: {{pager.path | safe }} | {{pager.index}}: {{pager.path | safe }} | ||||
{% endfor %} | {% endfor %} | ||||
Num pages: {{ paginator.pages | length }} | |||||
Num pagers: {{ paginator.pagers | length }} | |||||
Page size: {{ paginator.paginate_by }} | Page size: {{ paginator.paginate_by }} | ||||
Current index: {{ paginator.current_index }} | Current index: {{ paginator.current_index }} | ||||
First: {{ paginator.first | safe }} | First: {{ paginator.first | safe }} | ||||
@@ -74,10 +74,10 @@ url = "hello-world""#; | |||||
} | } | ||||
#[test] | #[test] | ||||
fn test_errors_with_empty_front_matter() { | |||||
fn test_is_ok_with_empty_front_matter() { | |||||
let content = r#" "#; | let content = r#" "#; | ||||
let res = FrontMatter::parse(content); | let res = FrontMatter::parse(content); | ||||
assert!(res.is_err()); | |||||
assert!(res.is_ok()); | |||||
} | } | ||||
#[test] | #[test] | ||||
@@ -3,7 +3,7 @@ extern crate tera; | |||||
extern crate tempdir; | extern crate tempdir; | ||||
use std::collections::HashMap; | use std::collections::HashMap; | ||||
use std::fs::File; | |||||
use std::fs::{File, create_dir}; | |||||
use std::path::Path; | use std::path::Path; | ||||
use tempdir::TempDir; | use tempdir::TempDir; | ||||
@@ -252,6 +252,29 @@ Hey there | |||||
assert!(page.content.starts_with("<pre")); | assert!(page.content.starts_with("<pre")); | ||||
} | } | ||||
#[test] | |||||
fn test_page_with_assets_gets_right_parent_path() { | |||||
let tmp_dir = TempDir::new("example").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("assets"); | |||||
create_dir(&nested_path).expect("create nested temp dir"); | |||||
File::create(nested_path.join("index.md")).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::parse( | |||||
&nested_path.join("index.md").as_path(), | |||||
"+++\nurl=\"hey\"+++\n", | |||||
&Config::default() | |||||
); | |||||
assert!(res.is_ok()); | |||||
let page = res.unwrap(); | |||||
assert_eq!(page.parent_path, path.join("content").join("posts")); | |||||
} | |||||
#[test] | #[test] | ||||
fn test_file_not_named_index_with_assets() { | fn test_file_not_named_index_with_assets() { | ||||
let tmp_dir = TempDir::new("example").expect("create temp dir"); | let tmp_dir = TempDir::new("example").expect("create temp dir"); | ||||
@@ -19,12 +19,9 @@ fn test_can_parse_site() { | |||||
site.load().unwrap(); | site.load().unwrap(); | ||||
// Correct number of pages (sections are pages too) | // Correct number of pages (sections are pages too) | ||||
assert_eq!(site.pages.len(), 10); | |||||
assert_eq!(site.pages.len(), 11); | |||||
let posts_path = path.join("content").join("posts"); | let posts_path = path.join("content").join("posts"); | ||||
// We have an index page | |||||
assert!(site.index.is_some()); | |||||
// Make sure we remove all the pwd + content from the sections | // Make sure we remove all the pwd + content from the sections | ||||
let basic = &site.pages[&posts_path.join("simple.md")]; | let basic = &site.pages[&posts_path.join("simple.md")]; | ||||
assert_eq!(basic.components, vec!["posts".to_string()]); | assert_eq!(basic.components, vec!["posts".to_string()]); | ||||
@@ -38,12 +35,16 @@ fn test_can_parse_site() { | |||||
assert_eq!(asset_folder_post.components, vec!["posts".to_string()]); | assert_eq!(asset_folder_post.components, vec!["posts".to_string()]); | ||||
// That we have the right number of sections | // That we have the right number of sections | ||||
assert_eq!(site.sections.len(), 4); | |||||
assert_eq!(site.sections.len(), 5); | |||||
// And that the sections are correct | // And that the sections are correct | ||||
let index_section = &site.sections[&path.join("content")]; | |||||
assert_eq!(index_section.subsections.len(), 1); | |||||
assert_eq!(index_section.pages.len(), 1); | |||||
let posts_section = &site.sections[&posts_path]; | let posts_section = &site.sections[&posts_path]; | ||||
assert_eq!(posts_section.subsections.len(), 1); | assert_eq!(posts_section.subsections.len(), 1); | ||||
assert_eq!(posts_section.pages.len(), 4); | |||||
assert_eq!(posts_section.pages.len(), 5); | |||||
let tutorials_section = &site.sections[&posts_path.join("tutorials")]; | let tutorials_section = &site.sections[&posts_path.join("tutorials")]; | ||||
assert_eq!(tutorials_section.subsections.len(), 2); | assert_eq!(tutorials_section.subsections.len(), 2); | ||||
@@ -108,6 +109,7 @@ fn test_can_build_site_without_live_reload() { | |||||
assert!(file_exists!(public, "posts/python/index.html")); | assert!(file_exists!(public, "posts/python/index.html")); | ||||
assert!(file_exists!(public, "posts/tutorials/devops/nix/index.html")); | assert!(file_exists!(public, "posts/tutorials/devops/nix/index.html")); | ||||
assert!(file_exists!(public, "posts/with-assets/index.html")); | assert!(file_exists!(public, "posts/with-assets/index.html")); | ||||
assert!(file_exists!(public, "posts/no-section/simple/index.html")); | |||||
// Sections | // Sections | ||||
assert!(file_exists!(public, "posts/index.html")); | assert!(file_exists!(public, "posts/index.html")); | ||||
@@ -126,9 +128,6 @@ fn test_can_build_site_without_live_reload() { | |||||
// Both pages and sections are in the sitemap | // Both pages and sections are in the sitemap | ||||
assert!(file_contains!(public, "sitemap.xml", "<loc>https://replace-this-with-your-url.com/posts/simple</loc>")); | assert!(file_contains!(public, "sitemap.xml", "<loc>https://replace-this-with-your-url.com/posts/simple</loc>")); | ||||
assert!(file_contains!(public, "sitemap.xml", "<loc>https://replace-this-with-your-url.com/posts</loc>")); | assert!(file_contains!(public, "sitemap.xml", "<loc>https://replace-this-with-your-url.com/posts</loc>")); | ||||
assert!(file_contains!(public, "a-fixed-url/index.html", "Previous article: ")); | |||||
assert!(file_contains!(public, "a-fixed-url/index.html", "Next article: ")); | |||||
} | } | ||||
#[test] | #[test] | ||||
@@ -323,22 +322,22 @@ fn test_can_build_site_with_pagination_for_section() { | |||||
"posts/page/1/index.html", | "posts/page/1/index.html", | ||||
"http-equiv=\"refresh\" content=\"0;url=https://replace-this-with-your-url.com/posts\"" | "http-equiv=\"refresh\" content=\"0;url=https://replace-this-with-your-url.com/posts\"" | ||||
)); | )); | ||||
assert!(file_contains!(public, "posts/index.html", "Num pages: 2")); | |||||
assert!(file_contains!(public, "posts/index.html", "Num pagers: 3")); | |||||
assert!(file_contains!(public, "posts/index.html", "Page size: 2")); | assert!(file_contains!(public, "posts/index.html", "Page size: 2")); | ||||
assert!(file_contains!(public, "posts/index.html", "Current index: 1")); | assert!(file_contains!(public, "posts/index.html", "Current index: 1")); | ||||
assert!(file_contains!(public, "posts/index.html", "has_next")); | assert!(file_contains!(public, "posts/index.html", "has_next")); | ||||
assert!(file_contains!(public, "posts/index.html", "First: https://replace-this-with-your-url.com/posts")); | assert!(file_contains!(public, "posts/index.html", "First: https://replace-this-with-your-url.com/posts")); | ||||
assert!(file_contains!(public, "posts/index.html", "Last: https://replace-this-with-your-url.com/posts/page/2")); | |||||
assert!(file_contains!(public, "posts/index.html", "Last: https://replace-this-with-your-url.com/posts/page/3")); | |||||
assert_eq!(file_contains!(public, "posts/index.html", "has_prev"), false); | assert_eq!(file_contains!(public, "posts/index.html", "has_prev"), false); | ||||
assert!(file_exists!(public, "posts/page/2/index.html")); | assert!(file_exists!(public, "posts/page/2/index.html")); | ||||
assert!(file_contains!(public, "posts/page/2/index.html", "Num pages: 2")); | |||||
assert!(file_contains!(public, "posts/page/2/index.html", "Num pagers: 3")); | |||||
assert!(file_contains!(public, "posts/page/2/index.html", "Page size: 2")); | assert!(file_contains!(public, "posts/page/2/index.html", "Page size: 2")); | ||||
assert!(file_contains!(public, "posts/page/2/index.html", "Current index: 2")); | assert!(file_contains!(public, "posts/page/2/index.html", "Current index: 2")); | ||||
assert!(file_contains!(public, "posts/page/2/index.html", "has_prev")); | assert!(file_contains!(public, "posts/page/2/index.html", "has_prev")); | ||||
assert_eq!(file_contains!(public, "posts/page/2/index.html", "has_next"), false); | |||||
assert!(file_contains!(public, "posts/page/2/index.html", "has_next")); | |||||
assert!(file_contains!(public, "posts/page/2/index.html", "First: https://replace-this-with-your-url.com/posts")); | assert!(file_contains!(public, "posts/page/2/index.html", "First: https://replace-this-with-your-url.com/posts")); | ||||
assert!(file_contains!(public, "posts/page/2/index.html", "Last: https://replace-this-with-your-url.com/posts/page/2")); | |||||
assert!(file_contains!(public, "posts/page/2/index.html", "Last: https://replace-this-with-your-url.com/posts/page/3")); | |||||
} | } | ||||
#[test] | #[test] | ||||
@@ -347,10 +346,11 @@ fn test_can_build_site_with_pagination_for_index() { | |||||
path.push("test_site"); | path.push("test_site"); | ||||
let mut site = Site::new(&path, "config.toml").unwrap(); | let mut site = Site::new(&path, "config.toml").unwrap(); | ||||
site.load().unwrap(); | site.load().unwrap(); | ||||
let mut index = site.index.unwrap(); | |||||
index.meta.paginate_by = Some(2); | |||||
index.meta.template = Some("index_paginated.html".to_string()); | |||||
site.index = Some(index); | |||||
{ | |||||
let mut index = site.sections.get_mut(&path.join("content")).unwrap(); | |||||
index.meta.paginate_by = Some(2); | |||||
index.meta.template = Some("index_paginated.html".to_string()); | |||||
} | |||||
let tmp_dir = TempDir::new("example").expect("create temp dir"); | let tmp_dir = TempDir::new("example").expect("create temp dir"); | ||||
let public = &tmp_dir.path().join("public"); | let public = &tmp_dir.path().join("public"); | ||||
site.set_output_path(&public); | site.set_output_path(&public); | ||||
@@ -374,11 +374,11 @@ fn test_can_build_site_with_pagination_for_index() { | |||||
"page/1/index.html", | "page/1/index.html", | ||||
"http-equiv=\"refresh\" content=\"0;url=https://replace-this-with-your-url.com/\"" | "http-equiv=\"refresh\" content=\"0;url=https://replace-this-with-your-url.com/\"" | ||||
)); | )); | ||||
assert!(file_contains!(public, "index.html", "Num pages: 2")); | |||||
assert!(file_contains!(public, "index.html", "Num pages: 1")); | |||||
assert!(file_contains!(public, "index.html", "Current index: 1")); | assert!(file_contains!(public, "index.html", "Current index: 1")); | ||||
assert!(file_contains!(public, "index.html", "has_next")); | |||||
assert!(file_contains!(public, "index.html", "First: https://replace-this-with-your-url.com/")); | assert!(file_contains!(public, "index.html", "First: https://replace-this-with-your-url.com/")); | ||||
assert!(file_contains!(public, "index.html", "Last: https://replace-this-with-your-url.com/page/2")); | |||||
assert!(file_contains!(public, "index.html", "Last: https://replace-this-with-your-url.com/")); | |||||
assert_eq!(file_contains!(public, "index.html", "has_prev"), false); | assert_eq!(file_contains!(public, "index.html", "has_prev"), false); | ||||
assert_eq!(file_contains!(public, "index.html", "has_next"), false); | |||||
} | } |