diff --git a/Cargo.lock b/Cargo.lock index 943335c34..b7aed3450 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,14 +13,6 @@ name = "adler32" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "aho-corasick" -version = "0.6.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "aho-corasick" version = "0.7.10" @@ -155,6 +147,25 @@ dependencies = [ "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "bodyparser" version = "0.8.0" @@ -197,6 +208,11 @@ name = "byte-tools" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "byteorder" version = "1.3.4" @@ -249,6 +265,15 @@ dependencies = [ "time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "chrono-tz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "parse-zoneinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "clap" version = "2.33.1" @@ -354,6 +379,7 @@ dependencies = [ name = "cratesfyi" version = "0.6.0" dependencies = [ + "arc-swap 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "badge 0.2.0", "comrak 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "crates-index-diff 7.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -363,7 +389,6 @@ dependencies = [ "failure 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "git2 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)", - "handlebars-iron 0.25.2 (registry+https://github.com/rust-lang/crates.io-index)", "html5ever 0.22.5 (registry+https://github.com/rust-lang/crates.io-index)", "iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "kuchiki 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -398,6 +423,7 @@ dependencies = [ "structopt 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "systemstat 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tera 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -577,6 +603,14 @@ dependencies = [ "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "dirs" version = "1.0.5" @@ -799,6 +833,14 @@ dependencies = [ "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "getrandom" version = "0.1.14" @@ -829,48 +871,42 @@ dependencies = [ ] [[package]] -name = "h2" -version = "0.1.26" +name = "globset" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", + "bstr 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", - "indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "handlebars" -version = "0.29.1" +name = "globwalk" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "pest 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ignore 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "handlebars-iron" -version = "0.25.2" +name = "h2" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "handlebars 0.29.1 (registry+https://github.com/rust-lang/crates.io-index)", - "iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (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.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -965,6 +1001,11 @@ name = "httparse" version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "humansize" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "humantime" version = "1.3.0" @@ -1052,6 +1093,23 @@ dependencies = [ "unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ignore" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "globset 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "indexmap" version = "1.3.2" @@ -1222,6 +1280,11 @@ name = "mac" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "markup5ever" version = "0.7.5" @@ -1571,6 +1634,11 @@ name = "oorandom" version = "11.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "openssl" version = "0.10.29" @@ -1670,6 +1738,14 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parse-zoneinfo" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "path-slash" version = "0.1.1" @@ -1696,13 +1772,16 @@ dependencies = [ [[package]] name = "pest" -version = "0.3.3" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "pest" -version = "1.0.6" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ucd-trie 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "pest_derive" @@ -1714,6 +1793,37 @@ dependencies = [ "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_generator 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pest_generator" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_meta 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pest_meta" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "phf" version = "0.7.24" @@ -2213,18 +2323,6 @@ dependencies = [ "rust-argon2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "regex" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.5.6 (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.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "regex" version = "1.3.7" @@ -2244,14 +2342,6 @@ dependencies = [ "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "regex-syntax" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ucd-util 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "regex-syntax" version = "0.6.17" @@ -2467,15 +2557,6 @@ name = "safemem" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "same-file" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "same-file" version = "1.0.6" @@ -2649,6 +2730,17 @@ dependencies = [ "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "sha2" version = "0.7.1" @@ -2965,6 +3057,27 @@ dependencies = [ "utf-8 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tera" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono-tz 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "globwalk 0.8.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.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", + "slug 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-segment 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "termcolor" version = "1.1.0" @@ -2986,14 +3099,6 @@ name = "thin-slice" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "thread_local" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "thread_local" version = "1.0.1" @@ -3291,10 +3396,54 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "ucd-util" -version = "0.1.8" +name = "ucd-trie" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unic-char-range 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unic-segment" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unic-ucd-segment 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unic-ucd-segment" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "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.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unic-common 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "unicase" version = "1.4.2" @@ -3406,11 +3555,6 @@ name = "utf-8" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "utf8-ranges" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "uuid" version = "0.7.4" @@ -3444,16 +3588,6 @@ name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "walkdir" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "walkdir" version = "2.3.1" @@ -3610,7 +3744,6 @@ dependencies = [ [metadata] "checksum addr2line 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a49806b9dadc843c61e7c97e72490ad7f7220ae249012fbda9ad0609457c0543" "checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" -"checksum aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5" "checksum aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" @@ -3628,11 +3761,14 @@ dependencies = [ "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" "checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" +"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" "checksum bodyparser 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f023abfa58aad6f6bc4ae0630799e24d5ee0ab8bb2e49f651d9b1f9aa4f52f30" "checksum bstr 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931" "checksum buf_redux 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b9279646319ff816b05fb5897883ece50d7d854d12b59992683d4f8a71b0f949" "checksum bumpalo 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5356f1d23ee24a1f785a56d1d1a5f0fd5b0f6a0c0fb2412ce11da71649ab78f6" "checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" +"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" "checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" "checksum bytesize 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "716960a18f978640f25101b5cbf1c6f6b0d3192fab36a2d98ca96f0ecbe41010" @@ -3640,6 +3776,7 @@ dependencies = [ "checksum cc 1.0.54 (registry+https://github.com/rust-lang/crates.io-index)" = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" +"checksum chrono-tz 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e0e430fad0384e4defc3dc6b1223d1b886087a8bf9b7080e5ae027f73851ea15" "checksum clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum comrak 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a9bd60be8877a3343d25b9a3dadbaf7f02f9ac843b54e663ecef73e29e8b9c6b" @@ -3666,6 +3803,7 @@ dependencies = [ "checksum csv-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" "checksum derive_more 0.99.7 (registry+https://github.com/rust-lang/crates.io-index)" = "2127768764f1556535c01b5326ef94bd60ff08dcfbdc544d53e69ed155610f5d" "checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" +"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" "checksum dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" "checksum dotenv 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" "checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" @@ -3694,13 +3832,14 @@ dependencies = [ "checksum futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" "checksum fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" "checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" "checksum gimli 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c" "checksum git2 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)" = "11e4b2082980e751c4bf4273e9cbb4a02c655729c8ee8a79f66cad03c8f4d31e" +"checksum globset 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7ad1da430bd7281dde2576f44c84cc3f0f7b475e7202cd503042dff01a8c8120" +"checksum globwalk 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "178270263374052c40502e9f607134947de75302c1348d1a0e31db67c1691446" "checksum h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" -"checksum handlebars 0.29.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fb04af2006ea09d985fef82b81e0eb25337e51b691c76403332378a53d521edc" -"checksum handlebars-iron 0.25.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7b8ad7259b5bfcc65da1f1f3525eb5e4d5c4c6c7ce2d3b9c9945165e7e083c9c" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" "checksum hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa" @@ -3712,12 +3851,14 @@ dependencies = [ "checksum http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" "checksum http-body 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" "checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" +"checksum humansize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6cab2627acfc432780848602f3f558f7e9dd427352224b0d9324025796d2a5e" "checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" "checksum hyper 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)" = "0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273" "checksum hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)" = "9dbe6ed1438e1f8ad955a4701e9a944938e9519f6888d12d8558b645e247d5f6" "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 idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +"checksum ignore 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)" = "128b9e89d15a3faa642ee164c998fd4fae3d89d054463cddb2c25a7baad3a352" "checksum indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" "checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" "checksum iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2440ae846e7a8c7f9b401db8f6e31b4ea5e7d3688b91761337da7e054520c75b" @@ -3739,6 +3880,7 @@ dependencies = [ "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" +"checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" "checksum markup5ever 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aae38d669396ca9b707bfc3db254bc382ddb94f57cc5c235f34623a669a01dab" "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" @@ -3777,6 +3919,7 @@ dependencies = [ "checksum object 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9cbca9424c482ee628fa549d9c812e2cd22f1180b9222c9200fdfa6eb31aecb2" "checksum once_cell 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" "checksum oorandom 11.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "94af325bc33c7f60191be4e2c984d48aaa21e2854f473b85398344b60c9b6358" +"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" "checksum openssl 0.10.29 (registry+https://github.com/rust-lang/crates.io-index)" = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" "checksum openssl-sys 0.9.56 (registry+https://github.com/rust-lang/crates.io-index)" = "f02309a7f127000ed50594f0b50ecc69e7c654e16d41b4e8156d1b3df8e0b52e" @@ -3786,13 +3929,17 @@ dependencies = [ "checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" "checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" "checksum parking_lot_core 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" +"checksum parse-zoneinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "feece9d0113b400182a7d00adcff81ccf29158c49c5abd11e2eed8589bf6ff07" "checksum path-slash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a0858af4d9136275541f4eac7be1af70add84cf356d901799b065ac1b8ff6e2f" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" "checksum persistent 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8e8fa0009c4f3d350281309909c618abddf10bb7e3145f28410782f6a5ec74c5" -"checksum pest 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0a6dda33d67c26f0aac90d324ab2eb7239c819fc7b2552fe9faa4fe88441edc8" "checksum pest 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0fce5d8b5cc33983fc74f78ad552b5522ab41442c4ca91606e4236eb4b5ceefc" +"checksum pest 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" "checksum pest_derive 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3294f437119209b084c797604295f40227cffa35c57220b1e99a6ff3bf8ee4" +"checksum pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +"checksum pest_generator 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +"checksum pest_meta 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" "checksum phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" "checksum phf 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" "checksum phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" @@ -3846,10 +3993,8 @@ dependencies = [ "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" "checksum redox_users 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" -"checksum regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384" "checksum regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692" "checksum regex-automata 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" -"checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" "checksum regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" "checksum reqwest 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "f88643aea3c1343c804950d7bf983bd2067f5ab59db6d613a08e05572f2714ab" @@ -3869,7 +4014,6 @@ dependencies = [ "checksum ryu 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" "checksum safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" -"checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7" "checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" "checksum sass-rs 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cabcf7c6e55053f359911187ac401409aad2dc14338cae972dec266fee486abd" "checksum sass-sys 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)" = "dd454d3c8fa19fe6c66df5d6ced4933f3a40b29d5875114eacc469451136226d" @@ -3889,6 +4033,7 @@ dependencies = [ "checksum serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)" = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" "checksum serde_urlencoded 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "642dd69105886af2efd227f75a520ec9b44a820d65bc133a9131f7d229fd165a" "checksum servo_arc 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +"checksum sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" "checksum sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0" "checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" "checksum signal-hook-registry 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41" @@ -3924,10 +4069,10 @@ dependencies = [ "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum tendril 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "707feda9f2582d5d680d733e38755547a3e8fb471e7ba11452ecfd9ce93a5d3b" +"checksum tera 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44567278e3f16c6f888f4a1426d1af33827e6bffbe3911fe24aec2c594f0dfcb" "checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum thin-slice 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" -"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" "checksum time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" "checksum tinytemplate 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "45e4bc5ac99433e0dcb8b9f309dd271a165ae37dde129b9e0ce1bfdd8bfe4891" @@ -3956,7 +4101,13 @@ dependencies = [ "checksum typed-arena 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9b2228007eba4120145f785df0f6c92ea538f5a3635a612ecf4e334c8c1446d" "checksum typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6" "checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" -"checksum ucd-util 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c85f514e095d348c279b1e5cd76795082cf15bd59b93207832abe0b1d8fed236" +"checksum ucd-trie 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" +"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.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" @@ -3973,14 +4124,12 @@ dependencies = [ "checksum url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" "checksum urlencoded 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0a52f50139118b60ae91af08bf15ed158817d34b91b9d24c11ffbe21195d33e3" "checksum utf-8 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" -"checksum utf8-ranges 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba" "checksum uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a" "checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" "checksum vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" "checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "bb08f9e670fab86099470b97cd2b252d6527f0b3cc1401acdb595ffc9dd288ff" "checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" "checksum want 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" "checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" diff --git a/Cargo.toml b/Cargo.toml index 4bb824643..f8cd3a436 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,11 +47,16 @@ serde_json = "1.0" # iron dependencies iron = "0.5" router = "0.5" -handlebars-iron = "0.25" params = "0.8" staticfile = { version = "0.4", features = [ "cache" ] } tempfile = "3.1.0" +# Templating +tera = { version = "1.3.0", features = ["builtins"] } + +# Template hot-reloading +arc-swap = "0.4.6" + [target.'cfg(not(windows))'.dependencies] libc = "0.2" @@ -71,6 +76,7 @@ kuchiki = "0.8" criterion = "0.3" rand = "0.7.3" + [[bench]] name = "html5ever" harness = false diff --git a/src/bin/cratesfyi.rs b/src/bin/cratesfyi.rs index 2a75ac729..35b8729a1 100644 --- a/src/bin/cratesfyi.rs +++ b/src/bin/cratesfyi.rs @@ -52,6 +52,9 @@ enum CommandLine { StartWebServer { #[structopt(name = "SOCKET_ADDR", default_value = "0.0.0.0:3000")] socket_addr: String, + /// Reload templates when they're changed + #[structopt(long = "reload")] + reload: bool, }, /// Starts cratesfyi daemon @@ -78,8 +81,11 @@ impl CommandLine { pub fn handle_args(self) { match self { Self::Build(build) => build.handle_args(), - Self::StartWebServer { socket_addr } => { - Server::start(Some(&socket_addr)); + Self::StartWebServer { + socket_addr, + reload, + } => { + Server::start(Some(&socket_addr), reload); } Self::Daemon { foreground } => cratesfyi::utils::start_daemon(!foreground), Self::Database { subcommand } => subcommand.handle_args(), diff --git a/src/docbuilder/limits.rs b/src/docbuilder/limits.rs index fe58aa2a8..e641afdfc 100644 --- a/src/docbuilder/limits.rs +++ b/src/docbuilder/limits.rs @@ -1,9 +1,9 @@ use crate::error::Result; use postgres::Connection; -use std::collections::BTreeMap; +use serde::Serialize; use std::time::Duration; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub(crate) struct Limits { memory: usize, targets: usize, @@ -67,52 +67,6 @@ impl Limits { pub(crate) fn targets(&self) -> usize { self.targets } - - pub(crate) fn for_website(&self) -> BTreeMap { - let mut res = BTreeMap::new(); - res.insert("Available RAM".into(), SIZE_SCALE(self.memory)); - res.insert( - "Maximum rustdoc execution time".into(), - TIME_SCALE(self.timeout.as_secs() as usize), - ); - res.insert( - "Maximum size of a build log".into(), - SIZE_SCALE(self.max_log_size), - ); - if self.networking { - res.insert("Network access".into(), "allowed".into()); - } else { - res.insert("Network access".into(), "blocked".into()); - } - res.insert( - "Maximum number of build targets".into(), - self.targets.to_string(), - ); - res - } -} - -const TIME_SCALE: fn(usize) -> String = |v| scale(v, 60, &["seconds", "minutes", "hours"]); -const SIZE_SCALE: fn(usize) -> String = |v| scale(v, 1024, &["bytes", "KB", "MB", "GB"]); - -fn scale(value: usize, interval: usize, labels: &[&str]) -> String { - let (mut value, interval) = (value as f64, interval as f64); - let mut chosen_label = &labels[0]; - for label in &labels[1..] { - if value / interval >= 1.0 { - chosen_label = label; - value /= interval; - } else { - break; - } - } - // 2.x - let mut value = format!("{:.1}", value); - // 2.0 -> 2 - if value.ends_with(".0") { - value.truncate(value.len() - 2); - } - format!("{} {}", value, chosen_label) } #[cfg(test)] @@ -161,56 +115,4 @@ mod test { Ok(()) }); } - - #[test] - fn display_limits() { - let limits = Limits { - memory: 102_400, - timeout: Duration::from_secs(300), - targets: 1, - ..Limits::default() - }; - let display = limits.for_website(); - assert_eq!(display.get("Network access"), Some(&"blocked".into())); - assert_eq!( - display.get("Maximum size of a build log"), - Some(&"100 KB".into()) - ); - assert_eq!( - display.get("Maximum number of build targets"), - Some(&limits.targets.to_string()) - ); - assert_eq!( - display.get("Maximum rustdoc execution time"), - Some(&"5 minutes".into()) - ); - assert_eq!(display.get("Available RAM"), Some(&"100 KB".into())); - } - - #[test] - fn scale_limits() { - // time - assert_eq!(TIME_SCALE(300), "5 minutes"); - assert_eq!(TIME_SCALE(1), "1 seconds"); - assert_eq!(TIME_SCALE(7200), "2 hours"); - - // size - assert_eq!(SIZE_SCALE(1), "1 bytes"); - assert_eq!(SIZE_SCALE(100), "100 bytes"); - assert_eq!(SIZE_SCALE(1024), "1 KB"); - assert_eq!(SIZE_SCALE(10240), "10 KB"); - assert_eq!(SIZE_SCALE(1_048_576), "1 MB"); - assert_eq!(SIZE_SCALE(10_485_760), "10 MB"); - assert_eq!(SIZE_SCALE(1_073_741_824), "1 GB"); - assert_eq!(SIZE_SCALE(10_737_418_240), "10 GB"); - assert_eq!(SIZE_SCALE(std::u32::MAX as usize), "4 GB"); - - // fractional sizes - assert_eq!(TIME_SCALE(90), "1.5 minutes"); - assert_eq!(TIME_SCALE(5400), "1.5 hours"); - - assert_eq!(SIZE_SCALE(1_288_490_189), "1.2 GB"); - assert_eq!(SIZE_SCALE(3_758_096_384), "3.5 GB"); - assert_eq!(SIZE_SCALE(1_048_051_712), "999.5 MB"); - } } diff --git a/src/lib.rs b/src/lib.rs index 2a5769307..9505b763c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,14 +21,12 @@ use web::page::GlobalAlert; // Warning message shown in the navigation bar of every page. Set to `None` to hide it. pub(crate) static GLOBAL_ALERT: Option = None; -/* -pub(crate) static GLOBAL_ALERT: Option = Some(GlobalAlert { - url: "https://blog.rust-lang.org/2019/09/18/upcoming-docsrs-changes.html", - text: "Upcoming docs.rs breaking changes!", - css_class: "error", - fa_icon: "warning", -}); -*/ +// pub(crate) static GLOBAL_ALERT: Option = Some(GlobalAlert { +// url: "https://blog.rust-lang.org/2019/09/18/upcoming-docsrs-changes.html", +// text: "Upcoming docs.rs breaking changes!", +// css_class: "error", +// fa_icon: "warning", +// }); /// Version string generated at build time contains last git /// commit hash and build date diff --git a/src/utils/daemon.rs b/src/utils/daemon.rs index 66da81f2d..6ed18f5bc 100644 --- a/src/utils/daemon.rs +++ b/src/utils/daemon.rs @@ -260,7 +260,7 @@ pub fn start_daemon(background: bool) { // at least start web server info!("Starting web server"); - crate::Server::start(None); + crate::Server::start(None, false); } fn opts() -> DocBuilderOptions { diff --git a/src/web/builds.rs b/src/web/builds.rs index 9c3223003..48bd69b34 100644 --- a/src/web/builds.rs +++ b/src/web/builds.rs @@ -1,66 +1,22 @@ -use super::duration_to_str; -use super::page::Page; +use super::page::{BuildsPage, WebPage}; use super::pool::Pool; use super::MetaData; use crate::docbuilder::Limits; use iron::prelude::*; use router::Router; -use serde::ser::{Serialize, SerializeStruct, Serializer}; +use serde::Serialize; -#[derive(Debug, Clone, PartialEq, Eq)] -struct Build { +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub struct Build { id: i32, rustc_version: String, - cratesfyi_version: String, + docsrs_version: String, build_status: bool, + #[serde(serialize_with = "super::rfc3339")] build_time: time::Timespec, output: Option, } -impl Serialize for Build { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("Build", 7)?; - state.serialize_field("id", &self.id)?; - state.serialize_field("rustc_version", &self.rustc_version)?; - state.serialize_field("cratesfyi_version", &self.cratesfyi_version)?; - state.serialize_field("build_status", &self.build_status)?; - state.serialize_field( - "build_time", - &time::at(self.build_time).rfc3339().to_string(), - )?; - state.serialize_field("build_time_relative", &duration_to_str(self.build_time))?; - state.serialize_field("output", &self.output)?; - - state.end() - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -struct BuildsPage { - metadata: Option, - builds: Vec, - build_details: Option, - limits: Limits, -} - -impl Serialize for BuildsPage { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("Buildspage", 4)?; - state.serialize_field("metadata", &self.metadata)?; - state.serialize_field("builds", &self.builds)?; - state.serialize_field("build_details", &self.build_details)?; - state.serialize_field("limits", &self.limits.for_website())?; - - state.end() - } -} - pub fn build_list_handler(req: &mut Request) -> IronResult { let router = extension!(req, Router); let name = cexpect!(router.find("name")); @@ -90,7 +46,7 @@ pub fn build_list_handler(req: &mut Request) -> IronResult { &[&name, &version] )); - let mut build_details = None; + let mut build_log = None; // FIXME: getting builds.output may cause performance issues when release have tons of builds let mut build_list = query .into_iter() @@ -100,14 +56,14 @@ pub fn build_list_handler(req: &mut Request) -> IronResult { let build = Build { id, rustc_version: row.get(6), - cratesfyi_version: row.get(7), + docsrs_version: row.get(7), build_status: row.get(8), build_time: row.get(9), output: row.get(10), }; if id == req_build_id { - build_details = Some(build.clone()); + build_log = Some(build.clone()); } build @@ -137,172 +93,12 @@ pub fn build_list_handler(req: &mut Request) -> IronResult { resp.headers.set(AccessControlAllowOrigin::Any); Ok(resp) } else { - let builds_page = BuildsPage { + BuildsPage { metadata: MetaData::from_crate(&conn, &name, &version), builds: build_list, - build_details, + build_log, limits, - }; - Page::new(builds_page) - .set_true("show_package_navigation") - .set_true("package_navigation_builds_tab") - .to_resp("builds") - } -} - -#[cfg(test)] -mod tests { - use super::*; - use serde_json::json; - - #[test] - fn serialize_build() { - let time = time::get_time(); - let mut build = Build { - id: 22, - rustc_version: "rustc 1.43.0 (4fb7144ed 2020-04-20)".to_string(), - cratesfyi_version: "docsrs 0.6.0 (3dd32ec 2020-05-01)".to_string(), - build_status: true, - build_time: time, - output: None, - }; - - let correct_json = json!({ - "id": 22, - "rustc_version": "rustc 1.43.0 (4fb7144ed 2020-04-20)", - "cratesfyi_version": "docsrs 0.6.0 (3dd32ec 2020-05-01)", - "build_time": time::at(time).rfc3339().to_string(), - "build_time_relative": duration_to_str(time), - "output": null, - "build_status": true - }); - - assert_eq!(correct_json, serde_json::to_value(&build).unwrap()); - - build.output = Some("some random stuff".to_string()); - let correct_json = json!({ - "id": 22, - "rustc_version": "rustc 1.43.0 (4fb7144ed 2020-04-20)", - "cratesfyi_version": "docsrs 0.6.0 (3dd32ec 2020-05-01)", - "build_time": time::at(time).rfc3339().to_string(), - "build_time_relative": duration_to_str(time), - "output": "some random stuff", - "build_status": true - }); - - assert_eq!(correct_json, serde_json::to_value(&build).unwrap()); - } - - #[test] - fn serialize_build_page() { - let time = time::get_time(); - let build = Build { - id: 22, - rustc_version: "rustc 1.43.0 (4fb7144ed 2020-04-20)".to_string(), - cratesfyi_version: "docsrs 0.6.0 (3dd32ec 2020-05-01)".to_string(), - build_status: true, - build_time: time, - output: None, - }; - let limits = Limits::default(); - let mut builds = BuildsPage { - metadata: Some(MetaData { - name: "serde".to_string(), - version: "1.0.0".to_string(), - description: Some("serde does stuff".to_string()), - target_name: None, - rustdoc_status: true, - default_target: "x86_64-unknown-linux-gnu".to_string(), - }), - builds: vec![build.clone()], - build_details: Some(build.clone()), - limits: limits.clone(), - }; - - let correct_json = json!({ - "metadata": { - "name": "serde", - "version": "1.0.0", - "description": "serde does stuff", - "target_name": null, - "rustdoc_status": true, - "default_target": "x86_64-unknown-linux-gnu" - }, - "builds": [{ - "id": 22, - "rustc_version": "rustc 1.43.0 (4fb7144ed 2020-04-20)", - "cratesfyi_version": "docsrs 0.6.0 (3dd32ec 2020-05-01)", - "build_time": time::at(time).rfc3339().to_string(), - "build_time_relative": duration_to_str(time), - "output": null, - "build_status": true - }], - "build_details": { - "id": 22, - "rustc_version": "rustc 1.43.0 (4fb7144ed 2020-04-20)", - "cratesfyi_version": "docsrs 0.6.0 (3dd32ec 2020-05-01)", - "build_time": time::at(time).rfc3339().to_string(), - "build_time_relative": duration_to_str(time), - "output": null, - "build_status": true - }, - "limits": limits.for_website(), - }); - - assert_eq!(correct_json, serde_json::to_value(&builds).unwrap()); - - builds.metadata = None; - let correct_json = json!({ - "metadata": null, - "builds": [{ - "id": 22, - "rustc_version": "rustc 1.43.0 (4fb7144ed 2020-04-20)", - "cratesfyi_version": "docsrs 0.6.0 (3dd32ec 2020-05-01)", - "build_time": time::at(time).rfc3339().to_string(), - "build_time_relative": duration_to_str(time), - "output": null, - "build_status": true - }], - "build_details": { - "id": 22, - "rustc_version": "rustc 1.43.0 (4fb7144ed 2020-04-20)", - "cratesfyi_version": "docsrs 0.6.0 (3dd32ec 2020-05-01)", - "build_time": time::at(time).rfc3339().to_string(), - "build_time_relative": duration_to_str(time), - "output": null, - "build_status": true - }, - "limits": limits.for_website(), - }); - - assert_eq!(correct_json, serde_json::to_value(&builds).unwrap()); - - builds.builds = Vec::new(); - let correct_json = json!({ - "metadata": null, - "builds": [], - "build_details": { - "id": 22, - "rustc_version": "rustc 1.43.0 (4fb7144ed 2020-04-20)", - "cratesfyi_version": "docsrs 0.6.0 (3dd32ec 2020-05-01)", - "build_time": time::at(time).rfc3339().to_string(), - "build_time_relative": duration_to_str(time), - "output": null, - "build_status": true - }, - "limits": limits.for_website() - }); - - assert_eq!(correct_json, serde_json::to_value(&builds).unwrap()); - - builds.build_details = None; - let correct_json = json!({ - "metadata": null, - "builds": [], - "build_details": null, - "limits": limits.for_website(), - }); - - assert_eq!(correct_json, serde_json::to_value(&builds).unwrap()); + } + .into_response() } } diff --git a/src/web/crate_details.rs b/src/web/crate_details.rs index 37fdf3a25..2fc1f07d5 100644 --- a/src/web/crate_details.rs +++ b/src/web/crate_details.rs @@ -1,5 +1,5 @@ use super::error::Nope; -use super::page::Page; +use super::page::{CrateDetailsPage, WebPage}; use super::pool::Pool; use super::{ duration_to_str, match_version, redirect_base, render_markdown, MatchSemver, MetaData, @@ -16,7 +16,7 @@ use serde_json::Value; // TODO: Add target name and versions -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq)] pub struct CrateDetails { name: String, version: String, @@ -41,6 +41,7 @@ pub struct CrateDetails { github_stars: Option, github_forks: Option, github_issues: Option, + // TODO: Is this even needed for rendering pages? pub(crate) metadata: MetaData, is_library: bool, yanked: bool, @@ -105,7 +106,7 @@ impl Serialize for CrateDetails { } } -#[derive(Debug, Eq, PartialEq, Serialize)] +#[derive(Debug, Clone, Eq, PartialEq, Serialize)] pub struct Release { pub version: String, pub build_status: bool, @@ -373,11 +374,7 @@ pub fn crate_details_handler(req: &mut Request) -> IronResult { Some(MatchSemver::Exact((version, _))) => { let details = CrateDetails::new(&conn, &name, &version); - Page::new(details) - .set_true("show_package_navigation") - .set_true("javascript_highlightjs") - .set_true("package_navigation_crate_tab") - .to_resp("crate_details") + CrateDetailsPage { details }.into_response() } Some(MatchSemver::Semver((version, _))) => { let url = ctry!(Url::parse( diff --git a/src/web/error.rs b/src/web/error.rs index 2a29404ef..1efa1678d 100644 --- a/src/web/error.rs +++ b/src/web/error.rs @@ -1,9 +1,8 @@ -use crate::web::page::Page; +use crate::web::page::{Error, Search, WebPage}; use iron::prelude::*; use iron::status; use iron::Handler; -use std::error::Error; -use std::fmt; +use std::{error, fmt}; #[derive(Debug, Copy, Clone)] pub enum Nope { @@ -22,41 +21,51 @@ impl fmt::Display for Nope { } } -impl Error for Nope {} +impl error::Error for Nope {} impl Handler for Nope { fn handle(&self, req: &mut Request) -> IronResult { match *self { Nope::ResourceNotFound => { // user tried to navigate to a resource (doc page/file) that doesn't exist - Page::new("no such resource".to_owned()) - .set_status(status::NotFound) - .title("The requested resource does not exist") - .to_resp("error") + Error { + title: "The requested resource does not exist".to_owned(), + search_query: None, + status: status::NotFound, + } + .into_response() } + Nope::CrateNotFound => { // user tried to navigate to a crate that doesn't exist - Page::new("no such crate".to_owned()) - .set_status(status::NotFound) - .title("The requested crate does not exist") - .to_resp("error") + Error { + title: "The requested crate does not exist".to_owned(), + search_query: None, + status: status::NotFound, + } + .into_response() } + Nope::NoResults => { use params::{Params, Value}; let params = req.get::().unwrap(); if let Some(&Value::String(ref query)) = params.find(&["query"]) { // this used to be a search - Page::new(Vec::::new()) - .set_status(status::NotFound) - .set("search_query", &query) - .title(&format!("No crates found matching '{}'", query)) - .to_resp("releases") + Search { + title: format!("No crates found matching '{}'", query), + search_query: Some(query.to_owned()), + status: status::NotFound, + ..Default::default() + } + .into_response() } else { // user did a search with no search terms - Page::new(Vec::::new()) - .set_status(status::NotFound) - .title("No results given for empty search query") - .to_resp("releases") + Search { + title: "No results given for empty search query".to_owned(), + status: status::NotFound, + ..Default::default() + } + .into_response() } } } diff --git a/src/web/mod.rs b/src/web/mod.rs index 930e93e51..1faf93043 100644 --- a/src/web/mod.rs +++ b/src/web/mod.rs @@ -7,33 +7,50 @@ use log::{debug, info}; /// ctry! (cratesfyitry) is extremely similar to try! and itry! /// except it returns an error page response instead of plain Err. macro_rules! ctry { - ($result:expr) => { + ($result:expr) => {{ + use $crate::web::page::WebPage; + match $result { Ok(v) => v, Err(e) => { - return $crate::web::page::Page::new(format!("{:?}", e)) - .title("An error has occured") - .set_status(::iron::status::BadRequest) - .to_resp("resp"); + log::error!("An error occurred: {:?}", e); + + return $crate::web::page::Error { + search_query: None, + title: "An error has occurred".to_owned(), + status: iron::status::BadRequest, + } + .into_response(); } } - }; + }}; } /// cexpect will check an option and if it's not Some /// it will return an error page response macro_rules! cexpect { - ($option:expr) => { + ($option:expr) => {{ + use $crate::web::page::WebPage; + match $option { Some(v) => v, None => { - return $crate::web::page::Page::new("Resource not found".to_owned()) - .title("An error has occured") - .set_status(::iron::status::BadRequest) - .to_resp("resp"); + log::error!( + "{}:{}:{} Called `cexpect` on a None value", + file!(), + line!(), + column!(), + ); + + return $crate::web::page::Error { + search_query: None, + title: "Resource not found".to_owned(), + status: iron::status::BadRequest, + } + .into_response(); } } - }; + }}; } /// Gets an extension from Request @@ -56,7 +73,6 @@ mod sitemap; mod source; use self::pool::Pool; -use handlebars_iron::{DirectorySource, HandlebarsEngine, SourceError}; use iron::headers::{CacheControl, CacheDirective, ContentType, Expires, HttpDate}; use iron::modifiers::Redirect; use iron::prelude::*; @@ -64,6 +80,7 @@ use iron::{self, status, Handler, Listening, Url}; use postgres::Connection; use router::NoRoute; use semver::{Version, VersionReq}; +use serde::ser::{Serialize, Serializer}; use staticfile::Static; use std::net::SocketAddr; use std::{env, fmt, path::PathBuf, time::Duration}; @@ -80,17 +97,6 @@ const OPENSEARCH_XML: &[u8] = include_bytes!("opensearch.xml"); const DEFAULT_BIND: &str = "0.0.0.0:3000"; -fn handlebars_engine() -> Result { - // TODO: Use DocBuilderOptions for paths - let mut hbse = HandlebarsEngine::new(); - hbse.add(Box::new(DirectorySource::new("./templates", ".hbs"))); - - // load templates - hbse.reload()?; - - Ok(hbse) -} - struct CratesfyiHandler { shared_resource_handler: Box, router_handler: Box, @@ -101,11 +107,8 @@ struct CratesfyiHandler { impl CratesfyiHandler { fn chain(pool: Pool, base: H) -> Chain { - let hbse = handlebars_engine().expect("Failed to load handlebar templates"); - let mut chain = Chain::new(base); chain.link_before(pool); - chain.link_after(hbse); chain } @@ -346,7 +349,12 @@ pub struct Server { } impl Server { - pub fn start(addr: Option<&str>) -> Self { + pub fn start(addr: Option<&str>, reload_templates: bool) -> Self { + page::TEMPLATE_DATA.poke().expect("This returns Ok(())"); + if reload_templates { + page::TemplateData::start_template_reloading(); + } + let server = Self::start_inner(addr.unwrap_or(DEFAULT_BIND), Pool::new()); info!("Running docs.rs web server on http://{}", server.addr()); server @@ -550,13 +558,14 @@ impl MetaData { } } +fn rfc3339(time: &time::Timespec, serializer: S) -> Result { + time::at(*time).rfc3339().to_string().serialize(serializer) +} + #[cfg(test)] mod test { use super::*; - use crate::{ - test::*, - web::{handlebars_engine, match_version}, - }; + use crate::{test::*, web::match_version}; use html5ever::tendril::TendrilSink; use serde_json::json; @@ -765,11 +774,6 @@ mod test { }); } - #[test] - fn test_templates_are_valid() { - handlebars_engine().expect("Failed to load handlebar templates"); - } - #[test] fn serialize_metadata() { let mut metadata = MetaData { diff --git a/src/web/page.rs b/src/web/page.rs deleted file mode 100644 index bc3ae1c80..000000000 --- a/src/web/page.rs +++ /dev/null @@ -1,270 +0,0 @@ -//! Generic page struct - -use handlebars_iron::Template; -use iron::response::Response; -use iron::{status, IronResult, Set}; -use serde::ser::{Serialize, SerializeStruct, Serializer}; -use serde_json::Value; -use std::collections::BTreeMap; - -lazy_static::lazy_static! { - static ref RUSTC_RESOURCE_SUFFIX: String = load_rustc_resource_suffix() - .unwrap_or_else(|_| "???".into()); -} - -fn load_rustc_resource_suffix() -> Result { - let conn = crate::db::connect_db()?; - - let res = conn.query( - "SELECT value FROM config WHERE name = 'rustc_version';", - &[], - )?; - if res.is_empty() { - failure::bail!("missing rustc version"); - } - - if let Some(Ok(vers)) = res.get(0).get_opt::<_, Value>("value") { - if let Some(vers_str) = vers.as_str() { - return Ok(crate::utils::parse_rustc_version(vers_str)?); - } - } - - failure::bail!("failed to parse the rustc version"); -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize)] -pub(crate) struct GlobalAlert { - pub(crate) url: &'static str, - pub(crate) text: &'static str, - pub(crate) css_class: &'static str, - pub(crate) fa_icon: &'static str, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Page { - title: Option, - content: T, - status: status::Status, - varss: BTreeMap, - varsb: BTreeMap, - varsi: BTreeMap, - rustc_resource_suffix: &'static str, -} - -impl Page { - pub fn new(content: T) -> Page { - Page { - title: None, - content, - status: status::Ok, - varss: BTreeMap::new(), - varsb: BTreeMap::new(), - varsi: BTreeMap::new(), - rustc_resource_suffix: &RUSTC_RESOURCE_SUFFIX, - } - } - - /// Sets a string variable - pub fn set(mut self, var: &str, val: &str) -> Page { - self.varss.insert(var.to_owned(), val.to_owned()); - self - } - - /// Sets a boolean variable - pub fn set_bool(mut self, var: &str, val: bool) -> Page { - self.varsb.insert(var.to_owned(), val); - self - } - - /// Sets a boolean variable to true - pub fn set_true(mut self, var: &str) -> Page { - self.varsb.insert(var.to_owned(), true); - self - } - - /// Sets an integer variable - pub fn set_int(mut self, var: &str, val: i64) -> Page { - self.varsi.insert(var.to_owned(), val); - self - } - - /// Sets title of page - pub fn title(mut self, title: &str) -> Page { - self.title = Some(title.to_owned()); - self - } - - /// Sets status code for response - pub fn set_status(mut self, s: status::Status) -> Page { - self.status = s; - self - } - - #[allow(clippy::wrong_self_convention)] - pub fn to_resp(self, template: &str) -> IronResult { - let mut resp = Response::new(); - let status = self.status; - let temp = Template::new(template, self); - resp.set_mut(temp).set_mut(status); - - Ok(resp) - } -} - -impl Serialize for Page { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - // Make sure that the length parameter passed to serde is correct by - // adding the someness of the global alert to the total. `true` - // is 1 and `false` is 0, so it increments if the value is some (and therefore - // needs to be serialized) - let mut state = serializer.serialize_struct( - "Page", - 8 + crate::GLOBAL_ALERT.is_some() as usize + self.title.is_some() as usize, - )?; - - if let Some(ref title) = self.title { - state.serialize_field("title", title)?; - } - - state.serialize_field("has_global_alert", &crate::GLOBAL_ALERT.is_some())?; - if let Some(ref global_alert) = crate::GLOBAL_ALERT { - state.serialize_field("global_alert", global_alert)?; - } - - state.serialize_field("content", &self.content)?; - state.serialize_field("rustc_resource_suffix", self.rustc_resource_suffix)?; - state.serialize_field("cratesfyi_version", crate::BUILD_VERSION)?; - state.serialize_field( - "cratesfyi_version_safe", - &build_version_safe(crate::BUILD_VERSION), - )?; - state.serialize_field("varss", &self.varss)?; - state.serialize_field("varsb", &self.varsb)?; - state.serialize_field("varsi", &self.varsi)?; - - state.end() - } -} - -fn build_version_safe(version: &str) -> String { - version.replace(" ", "-").replace("(", "").replace(")", "") -} - -#[cfg(test)] -mod tests { - use super::super::releases::{self, Release}; - use super::*; - use iron::Url; - use serde_json::json; - - #[test] - fn serialize_page() { - let time = time::get_time(); - - let mut release = Release::default(); - release.name = "lasso".into(); - release.version = "0.1.0".into(); - release.release_time = time.clone(); - - let mut varss = BTreeMap::new(); - varss.insert("test".into(), "works".into()); - let mut varsb = BTreeMap::new(); - varsb.insert("test2".into(), true); - let mut varsi = BTreeMap::new(); - varsi.insert("test3".into(), 1337); - - let page = Page { - title: None, - content: vec![release.clone()], - status: status::Status::Ok, - varss, - varsb, - varsi, - rustc_resource_suffix: &*RUSTC_RESOURCE_SUFFIX, - }; - - let correct_json = json!({ - "content": [{ - "name": "lasso", - "version": "0.1.0", - "description": null, - "target_name": null, - "rustdoc_status": false, - "release_time": super::super::duration_to_str(time), - "release_time_rfc3339": time::at(time).rfc3339().to_string(), - "stars": 0 - }], - "varss": { "test": "works" }, - "varsb": { "test2": true }, - "varsi": { "test3": 1337 }, - "rustc_resource_suffix": &*RUSTC_RESOURCE_SUFFIX, - "cratesfyi_version": crate::BUILD_VERSION, - "cratesfyi_version_safe": build_version_safe(crate::BUILD_VERSION), - "has_global_alert": crate::GLOBAL_ALERT.is_some() - }); - - assert_eq!(correct_json, serde_json::to_value(&page).unwrap()); - } - - #[test] - fn load_page_from_releases() { - crate::test::wrapper(|env| { - let db = env.db(); - db.fake_release().name("foo").version("0.1.0").create()?; - let packages = releases::get_releases(&db.conn(), 1, 1, releases::Order::ReleaseTime); - - let mut varsb = BTreeMap::new(); - varsb.insert("show_search_form".into(), true); - varsb.insert("hide_package_navigation".into(), true); - - let correct_page = Page { - title: None, - content: packages.clone(), - status: status::Status::Ok, - varss: BTreeMap::new(), - varsb, - varsi: BTreeMap::new(), - rustc_resource_suffix: &RUSTC_RESOURCE_SUFFIX, - }; - - let page = Page::new(packages) - .set_true("show_search_form") - .set_true("hide_package_navigation"); - - assert_eq!(page, correct_page); - - Ok(()) - }) - } - - #[test] - fn serialize_global_alert() { - let alert = GlobalAlert { - url: "http://www.hasthelargehadroncolliderdestroyedtheworldyet.com/", - text: "THE WORLD WILL SOON END", - css_class: "THE END IS NEAR", - fa_icon: "https://gph.is/1uOvmqR", - }; - - let correct_json = json!({ - "url": "http://www.hasthelargehadroncolliderdestroyedtheworldyet.com/", - "text": "THE WORLD WILL SOON END", - "css_class": "THE END IS NEAR", - "fa_icon": "https://gph.is/1uOvmqR" - }); - - assert_eq!(correct_json, serde_json::to_value(&alert).unwrap()); - } - - #[test] - fn build_version_url_safe() { - let safe = format!( - "https://docs.rs/builds/{}", - build_version_safe(crate::BUILD_VERSION) - ); - assert!(Url::parse(&safe).is_ok()); - } -} diff --git a/src/web/page/mod.rs b/src/web/page/mod.rs new file mode 100644 index 000000000..ec88cfe51 --- /dev/null +++ b/src/web/page/mod.rs @@ -0,0 +1,48 @@ +use serde::Serialize; + +mod models; +mod templates; + +pub(crate) use models::{ + About, BuildsPage, CrateDetailsPage, Error, HomePage, ReleaseActivity, ReleaseFeed, + ReleaseQueue, ReleaseType, RustdocPage, Search, SitemapXml, SourcePage, ViewReleases, WebPage, +}; +pub(crate) use templates::TemplateData; + +lazy_static::lazy_static! { + /// Holds all data relevant to templating + pub(crate) static ref TEMPLATE_DATA: TemplateData = TemplateData::new().expect("Failed to load template data"); +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize)] +pub(crate) struct GlobalAlert { + pub(crate) url: &'static str, + pub(crate) text: &'static str, + pub(crate) css_class: &'static str, + pub(crate) fa_icon: &'static str, +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[test] + fn serialize_global_alert() { + let alert = GlobalAlert { + url: "http://www.hasthelargehadroncolliderdestroyedtheworldyet.com/", + text: "THE WORLD WILL SOON END", + css_class: "THE END IS NEAR", + fa_icon: "https://gph.is/1uOvmqR", + }; + + let correct_json = json!({ + "url": "http://www.hasthelargehadroncolliderdestroyedtheworldyet.com/", + "text": "THE WORLD WILL SOON END", + "css_class": "THE END IS NEAR", + "fa_icon": "https://gph.is/1uOvmqR" + }); + + assert_eq!(correct_json, serde_json::to_value(&alert).unwrap()); + } +} diff --git a/src/web/page/models.rs b/src/web/page/models.rs new file mode 100644 index 000000000..7727acd23 --- /dev/null +++ b/src/web/page/models.rs @@ -0,0 +1,265 @@ +use super::TEMPLATE_DATA; +use crate::{ + docbuilder::Limits, + web::{ + builds::Build, crate_details::CrateDetails, releases::Release, source::FileList, MetaData, + }, +}; +use iron::{headers::ContentType, response::Response, status::Status, IronResult}; +use serde::Serialize; +use serde_json::Value; +use tera::Context; + +pub trait WebPage: Serialize + Sized { + /// Turn the current instance into a `Response`, ready to be served + // TODO: The potential for caching similar pages is here due to render taking `&Context` + fn into_response(self) -> IronResult { + let ctx = Context::from_serialize(&self).unwrap(); + let rendered = TEMPLATE_DATA + .templates + .load() + .render(Self::template(), &ctx) + .unwrap(); + + let mut response = Response::with((self.get_status(), rendered)); + response.headers.set(Self::content_type()); + + Ok(response) + } + + /// Get the name of the template to be rendered + fn template() -> &'static str; + + /// Gets the status of the request, defaults to `Ok` + fn get_status(&self) -> Status { + Status::Ok + } + + /// The content type that the template should be served with, defaults to html + fn content_type() -> ContentType { + ContentType::html() + } +} + +/// The sitemap, corresponds with `templates/sitemap.xml` +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub(crate) struct SitemapXml { + /// The releases to be displayed on the sitemap + pub releases: Vec<(String, String)>, +} + +impl WebPage for SitemapXml { + fn template() -> &'static str { + "docsrs/sitemap.xml" + } + + fn content_type() -> ContentType { + ContentType("application/xml".parse().unwrap()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub(crate) struct About { + pub rustc_version: Option, + pub limits: Limits, +} + +impl WebPage for About { + fn template() -> &'static str { + "docsrs/about.html" + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub(crate) struct HomePage { + pub recent_releases: Vec, +} + +impl WebPage for HomePage { + fn template() -> &'static str { + "docsrs/home.html" + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub(crate) struct ReleaseFeed { + pub recent_releases: Vec, +} + +impl WebPage for ReleaseFeed { + fn template() -> &'static str { + "releases/feed.xml" + } + + fn content_type() -> ContentType { + ContentType("application/atom+xml".parse().unwrap()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub(crate) struct ViewReleases { + pub releases: Vec, + pub description: String, + pub release_type: ReleaseType, + pub show_next_page: bool, + pub show_previous_page: bool, + pub page_number: i64, + pub author: Option, +} + +impl WebPage for ViewReleases { + fn template() -> &'static str { + "releases/releases.html" + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize)] +#[serde(rename_all = "kebab-case")] +pub(crate) enum ReleaseType { + Recent, + Stars, + RecentFailures, + Failures, + Author, + Search, +} + +#[derive(Debug, Clone, PartialEq, Serialize)] +pub(crate) struct ReleaseActivity { + pub description: String, + pub activity_data: Value, +} + +impl WebPage for ReleaseActivity { + fn template() -> &'static str { + "releases/activity.html" + } +} + +#[derive(Debug, Clone, PartialEq, Serialize)] +pub(crate) struct ReleaseQueue { + pub description: String, + pub queue_is_empty: bool, + pub queue: Vec<(String, String, i32)>, +} + +impl WebPage for ReleaseQueue { + fn template() -> &'static str { + "releases/queue.html" + } +} + +#[derive(Debug, Clone, PartialEq, Serialize)] +pub(crate) struct CrateDetailsPage { + pub details: Option, +} + +impl WebPage for CrateDetailsPage { + fn template() -> &'static str { + "crate/details.html" + } +} + +#[derive(Debug, Clone, PartialEq, Serialize)] +pub(crate) struct BuildsPage { + pub metadata: Option, + pub builds: Vec, + pub build_log: Option, + pub limits: Limits, +} + +impl WebPage for BuildsPage { + fn template() -> &'static str { + "crate/builds.html" + } +} + +#[derive(Debug, Clone, PartialEq, Serialize)] +pub(crate) struct RustdocPage { + pub latest_path: String, + pub latest_version: String, + pub inner_path: String, + pub is_latest_version: bool, + pub rustdoc_head: String, + pub rustdoc_body: String, + pub rustdoc_body_class: String, + pub krate: CrateDetails, +} + +impl WebPage for RustdocPage { + fn template() -> &'static str { + "rustdoc/page.html" + } +} + +#[derive(Debug, Clone, PartialEq, Serialize)] +pub(crate) struct SourcePage { + pub file_list: FileList, + pub show_parent_link: bool, + pub file_content: Option, + pub is_rust_source: bool, +} + +impl WebPage for SourcePage { + fn template() -> &'static str { + "crate/source.html" + } +} + +#[derive(Debug, Clone, PartialEq, Serialize)] +pub(crate) struct Error { + pub title: String, + pub search_query: Option, + #[serde(skip)] + pub status: iron::status::Status, +} + +impl WebPage for Error { + fn template() -> &'static str { + "error.html" + } + + fn get_status(&self) -> Status { + self.status + } +} + +#[derive(Debug, Clone, PartialEq, Serialize)] +pub(crate) struct Search { + pub title: String, + #[serde(rename = "releases")] + pub results: Vec, + pub search_query: Option, + pub previous_page_button: bool, + pub next_page_button: bool, + pub current_page: i64, + /// This should always be `ReleaseType::Search` + pub release_type: ReleaseType, + #[serde(skip)] + pub status: iron::status::Status, +} + +impl WebPage for Search { + fn template() -> &'static str { + "releases/releases.html" + } + + fn get_status(&self) -> Status { + self.status + } +} + +impl Default for Search { + fn default() -> Self { + Self { + title: String::default(), + results: Vec::default(), + search_query: None, + previous_page_button: false, + next_page_button: false, + current_page: 0, + release_type: ReleaseType::Search, + status: iron::status::Ok, + } + } +} diff --git a/src/web/page/templates.rs b/src/web/page/templates.rs new file mode 100644 index 000000000..51e106529 --- /dev/null +++ b/src/web/page/templates.rs @@ -0,0 +1,192 @@ +use super::TEMPLATE_DATA; +use crate::error::Result; +use arc_swap::ArcSwap; +use serde_json::Value; +use std::collections::HashMap; +use tera::{Result as TeraResult, Tera}; + +/// Holds all data relevant to templating +pub(crate) struct TemplateData { + /// The actual templates, stored in an `ArcSwap` so that they're hot-swappable + // TODO: Conditional compilation so it's not always wrapped, the `ArcSwap` is unneeded overhead for prod + pub templates: ArcSwap, + /// The current global alert, serialized into a json value + global_alert: Value, + /// The version of docs.rs, serialized into a json value + docsrs_version: Value, + /// The current resource suffix of rustc, serialized into a json value + resource_suffix: Value, +} + +impl TemplateData { + pub fn new() -> Result { + log::trace!("Loading templates"); + + let data = Self { + templates: ArcSwap::from_pointee(load_templates()?), + global_alert: serde_json::to_value(crate::GLOBAL_ALERT)?, + docsrs_version: Value::String(crate::BUILD_VERSION.to_owned()), + resource_suffix: Value::String(load_rustc_resource_suffix().unwrap_or_else(|err| { + log::error!("Failed to load rustc resource suffix: {:?}", err); + String::from("???") + })), + }; + + log::trace!("Finished loading templates"); + + Ok(data) + } + + pub fn start_template_reloading() { + use std::{sync::Arc, thread, time::Duration}; + + thread::spawn(|| loop { + match load_templates() { + Ok(templates) => { + log::info!("Reloaded templates"); + TEMPLATE_DATA.templates.swap(Arc::new(templates)); + thread::sleep(Duration::from_secs(10)); + } + + Err(err) => { + log::info!("Error Loading Templates:\n{}", err); + thread::sleep(Duration::from_secs(5)); + } + } + }); + } + + /// Used to initialize a `TemplateData` instance in a `lazy_static`. + /// Loading tera takes a second, so it's important that this is done before any + /// requests start coming in + pub fn poke(&self) -> Result<()> { + Ok(()) + } +} + +// TODO: Is there a reason this isn't fatal? If the rustc version is incorrect (Or "???" as used by default), then +// all pages will be served *really* weird because they'll lack all CSS +fn load_rustc_resource_suffix() -> Result { + let conn = crate::db::connect_db()?; + + let res = conn.query( + "SELECT value FROM config WHERE name = 'rustc_version';", + &[], + )?; + if res.is_empty() { + failure::bail!("missing rustc version"); + } + + if let Some(Ok(vers)) = res.get(0).get_opt::<_, Value>("value") { + if let Some(vers_str) = vers.as_str() { + return Ok(crate::utils::parse_rustc_version(vers_str)?); + } + } + + failure::bail!("failed to parse the rustc version"); +} + +pub(super) fn load_templates() -> TeraResult { + let mut tera = Tera::new("templates/**/*")?; + + // Custom functions + tera.register_function("global_alert", global_alert); + tera.register_function("docsrs_version", docsrs_version); + tera.register_function("rustc_resource_suffix", rustc_resource_suffix); + + // Custom filters + tera.register_filter("timeformat", timeformat); + tera.register_filter("dbg", dbg); + tera.register_filter("dedent", dedent); + + Ok(tera) +} + +/// Returns an `Option` in json form for templates +fn global_alert(args: &HashMap) -> TeraResult { + debug_assert!(args.is_empty(), "global_alert takes no args"); + + Ok(TEMPLATE_DATA.global_alert.clone()) +} + +/// Returns the version of docs.rs, takes the `safe` parameter which can be `true` to get a url-safe version +fn docsrs_version(args: &HashMap) -> TeraResult { + debug_assert!( + args.is_empty(), + "docsrs_version only takes no args, to get a safe version use `docsrs_version() | slugify`", + ); + + Ok(TEMPLATE_DATA.docsrs_version.clone()) +} + +/// Returns the current rustc resource suffix +fn rustc_resource_suffix(args: &HashMap) -> TeraResult { + debug_assert!(args.is_empty(), "rustc_resource_suffix takes no args"); + + Ok(TEMPLATE_DATA.resource_suffix.clone()) +} + +/// Prettily format a timestamp +// TODO: This can be replaced by chrono +fn timeformat(value: &Value, args: &HashMap) -> TeraResult { + let fmt = if let Some(Value::Bool(true)) = args.get("relative") { + let value = time::strptime(value.as_str().unwrap(), "%Y-%m-%dT%H:%M:%S%z").unwrap(); + + super::super::duration_to_str(value.to_timespec()) + } else { + const TIMES: &[&str] = &["seconds", "minutes", "hours"]; + + let mut value = value.as_f64().unwrap(); + let mut chosen_time = &TIMES[0]; + + for time in &TIMES[1..] { + if value / 60.0 >= 1.0 { + chosen_time = time; + value /= 60.0; + } else { + break; + } + } + + // TODO: This formatting section can be optimized, two string allocations aren't needed + let mut value = format!("{:.1}", value); + if value.ends_with(".0") { + value.truncate(value.len() - 2); + } + + format!("{} {}", value, chosen_time) + }; + + Ok(Value::String(fmt)) +} + +/// Print a tera value to stdout +fn dbg(value: &Value, _args: &HashMap) -> TeraResult { + println!("{:?}", value); + + Ok(value.clone()) +} + +/// Dedent a string by removing all leading whitespace +fn dedent(value: &Value, _args: &HashMap) -> TeraResult { + let string = value.as_str().expect("dedent takes a string"); + + Ok(Value::String( + string + .lines() + .map(|l| l.trim_start()) + .collect::>() + .join("\n"), + )) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_templates_are_valid() { + let tera = load_templates().unwrap(); + tera.check_macro_files().unwrap(); + } +} diff --git a/src/web/releases.rs b/src/web/releases.rs index 72b142eda..8c707c204 100644 --- a/src/web/releases.rs +++ b/src/web/releases.rs @@ -1,14 +1,20 @@ //! Releases web handlers -use super::error::Nope; -use super::page::Page; -use super::pool::Pool; -use super::{duration_to_str, match_version, redirect_base}; +use super::{ + error::Nope, + match_version, + page::{ + HomePage, ReleaseActivity, ReleaseFeed, ReleaseQueue, ReleaseType, Search, ViewReleases, + WebPage, + }, + pool::Pool, + redirect_base, +}; use iron::prelude::*; use iron::status; use postgres::Connection; use router::Router; -use serde::ser::{Serialize, SerializeStruct, Serializer}; +use serde::Serialize; use serde_json::Value; /// Number of release in home page @@ -18,13 +24,14 @@ const RELEASES_IN_RELEASES: i64 = 30; /// Releases in recent releases feed const RELEASES_IN_FEED: i64 = 150; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub struct Release { pub(crate) name: String, pub(crate) version: String, description: Option, target_name: Option, rustdoc_status: bool, + #[serde(serialize_with = "super::rfc3339")] pub(crate) release_time: time::Timespec, stars: i32, } @@ -43,28 +50,6 @@ impl Default for Release { } } -impl Serialize for Release { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("Release", 8)?; - state.serialize_field("name", &self.name)?; - state.serialize_field("version", &self.version)?; - state.serialize_field("description", &self.description)?; - state.serialize_field("target_name", &self.target_name)?; - state.serialize_field("rustdoc_status", &self.rustdoc_status)?; - state.serialize_field("release_time", &duration_to_str(self.release_time))?; - state.serialize_field( - "release_time_rfc3339", - &time::at(self.release_time).rfc3339().to_string(), - )?; - state.serialize_field("stars", &self.stars)?; - - state.end() - } -} - #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub(crate) enum Order { ReleaseTime, // this is default order @@ -343,52 +328,40 @@ fn get_search_results( pub fn home_page(req: &mut Request) -> IronResult { let conn = extension!(req, Pool).get()?; - let packages = get_releases(&conn, 1, RELEASES_IN_HOME, Order::ReleaseTime); - Page::new(packages) - .set_true("show_search_form") - .set_true("hide_package_navigation") - .to_resp("releases") + let recent_releases = get_releases(&conn, 1, RELEASES_IN_HOME, Order::ReleaseTime); + + HomePage { recent_releases }.into_response() } pub fn releases_feed_handler(req: &mut Request) -> IronResult { let conn = extension!(req, Pool).get()?; - let packages = get_releases(&conn, 1, RELEASES_IN_FEED, Order::ReleaseTime); - let mut resp = ctry!(Page::new(packages).to_resp("releases_feed")); - resp.headers.set(::iron::headers::ContentType( - "application/atom+xml".parse().unwrap(), - )); - Ok(resp) + let recent_releases = get_releases(&conn, 1, RELEASES_IN_FEED, Order::ReleaseTime); + + ReleaseFeed { recent_releases }.into_response() } fn releases_handler( - packages: Vec, + releases: Vec, page_number: i64, - release_type: &str, - tab: &str, - title: &str, + release_type: ReleaseType, + description: impl Into, ) -> IronResult { - if packages.is_empty() { - return Err(IronError::new(Nope::CrateNotFound, status::NotFound)); - } - // Show next and previous page buttons - // This is a temporary solution to avoid expensive COUNT(*) let (show_next_page, show_previous_page) = ( - packages.len() == RELEASES_IN_RELEASES as usize, + releases.len() == RELEASES_IN_RELEASES as usize, page_number != 1, ); - Page::new(packages) - .title("Releases") - .set("description", title) - .set("release_type", release_type) - .set_true("show_releases_navigation") - .set_true(tab) - .set_bool("show_next_page_button", show_next_page) - .set_int("next_page", page_number + 1) - .set_bool("show_previous_page_button", show_previous_page) - .set_int("previous_page", page_number - 1) - .to_resp("releases") + ViewReleases { + releases, + description: description.into(), + release_type, + show_next_page, + show_previous_page, + page_number, + author: None, + } + .into_response() } // Following functions caused a code repeat due to design of our /releases/ URL routes @@ -400,11 +373,11 @@ pub fn recent_releases_handler(req: &mut Request) -> IronResult { .unwrap_or(1); let conn = extension!(req, Pool).get()?; let packages = get_releases(&conn, page_number, RELEASES_IN_RELEASES, Order::ReleaseTime); + releases_handler( packages, page_number, - "recent", - "releases_navigation_recent_tab", + ReleaseType::Recent, "Recently uploaded crates", ) } @@ -417,11 +390,11 @@ pub fn releases_by_stars_handler(req: &mut Request) -> IronResult { .unwrap_or(1); let conn = extension!(req, Pool).get()?; let packages = get_releases(&conn, page_number, RELEASES_IN_RELEASES, Order::GithubStars); + releases_handler( packages, page_number, - "stars", - "releases_navigation_stars_tab", + ReleaseType::Stars, "Crates with most stars", ) } @@ -439,11 +412,11 @@ pub fn releases_recent_failures_handler(req: &mut Request) -> IronResult IronResult IronResult { let conn = extension!(req, Pool).get()?; - #[allow(clippy::or_fun_call)] let author = ctry!(router .find("author") - .ok_or(IronError::new(Nope::CrateNotFound, status::NotFound))); + .ok_or_else(|| IronError::new(Nope::CrateNotFound, status::NotFound))); - let (author_name, packages) = if author.starts_with('@') { + let (author_name, releases) = if author.starts_with('@') { let mut author = author.split('@'); get_releases_by_owner( @@ -495,28 +467,27 @@ pub fn author_handler(req: &mut Request) -> IronResult { get_releases_by_author(&conn, page_number, RELEASES_IN_RELEASES, author) }; - if packages.is_empty() { + if releases.is_empty() { return Err(IronError::new(Nope::CrateNotFound, status::NotFound)); } // Show next and previous page buttons // This is a temporary solution to avoid expensive COUNT(*) let (show_next_page, show_previous_page) = ( - packages.len() == RELEASES_IN_RELEASES as usize, + releases.len() == RELEASES_IN_RELEASES as usize, page_number != 1, ); - Page::new(packages) - .title("Releases") - .set("description", &format!("Crates from {}", author_name)) - .set("author", &author_name) - .set("release_type", author) - .set_true("show_releases_navigation") - .set_true("show_stars") - .set_bool("show_next_page_button", show_next_page) - .set_int("next_page", page_number + 1) - .set_bool("show_previous_page_button", show_previous_page) - .set_int("previous_page", page_number - 1) - .to_resp("releases") + + ViewReleases { + releases, + description: format!("Crates from {}", author_name), + release_type: ReleaseType::Author, + show_next_page, + show_previous_page, + page_number, + author: Some(author_name), + } + .into_response() } pub fn search_handler(req: &mut Request) -> IronResult { @@ -526,7 +497,7 @@ pub fn search_handler(req: &mut Request) -> IronResult { let query = params.find(&["query"]); let conn = extension!(req, Pool).get()?; - if let Some(&Value::String(ref query)) = query { + if let Some(Value::String(query)) = query { // check if I am feeling lucky button pressed and redirect user to crate page // if there is a match // TODO: Redirecting to latest doc might be more useful @@ -615,10 +586,13 @@ pub fn search_handler(req: &mut Request) -> IronResult { }; // FIXME: There is no pagination - Page::new(results) - .set("search_query", &query) - .title(&title) - .to_resp("releases") + Search { + title, + results, + search_query: Some(query.to_owned()), + ..Default::default() + } + .into_response() } else { Err(IronError::new(Nope::NoResults, status::NotFound)) } @@ -626,19 +600,18 @@ pub fn search_handler(req: &mut Request) -> IronResult { pub fn activity_handler(req: &mut Request) -> IronResult { let conn = extension!(req, Pool).get()?; - let release_activity_data: Value = ctry!(conn.query( + let activity_data: Value = ctry!(conn.query( "SELECT value FROM config WHERE name = 'release_activity'", &[] )) .get(0) .get(0); - Page::new(release_activity_data) - .title("Releases") - .set("description", "Monthly release activity") - .set_true("show_releases_navigation") - .set_true("releases_navigation_activity_tab") - .set_true("javascript_highchartjs") - .to_resp("releases_activity") + + ReleaseActivity { + description: "Monthly release activity".to_owned(), + activity_data, + } + .into_response() } pub fn build_queue_handler(req: &mut Request) -> IronResult { @@ -653,8 +626,8 @@ pub fn build_queue_handler(req: &mut Request) -> IronResult { ) .unwrap(); - let crates: Vec<(String, String, i32)> = query - .into_iter() + let queue: Vec<(String, String, i32)> = query + .iter() .map(|krate| { ( krate.get("name"), @@ -667,14 +640,12 @@ pub fn build_queue_handler(req: &mut Request) -> IronResult { }) .collect(); - let is_empty = crates.is_empty(); - Page::new(crates) - .title("Build queue") - .set("description", "List of crates scheduled to build") - .set_bool("queue_empty", is_empty) - .set_true("show_releases_navigation") - .set_true("releases_queue_tab") - .to_resp("releases_queue") + ReleaseQueue { + description: "List of crates scheduled to build".to_owned(), + queue_is_empty: queue.is_empty(), + queue, + } + .into_response() } #[cfg(test)] @@ -740,8 +711,7 @@ mod tests { let near_matches = ["Regex", "rEgex", "reGex", "regEx", "regeX"]; for name in near_matches.iter() { - let (num_results, mut results) = - dbg!(get_search_results(&db.conn(), *name, 1, 100)); + let (num_results, mut results) = get_search_results(&db.conn(), *name, 1, 100); assert_eq!(num_results, 3); for name in releases.iter() { @@ -1016,8 +986,7 @@ mod tests { #[test] fn serialize_releases() { - let current_time = time::get_time(); - let now = time::at(current_time); + let now = time::get_time(); let mut release = Release { name: "serde".to_string(), @@ -1025,7 +994,7 @@ mod tests { description: Some("serde makes things other things".to_string()), target_name: Some("x86_64-pc-windows-msvc".to_string()), rustdoc_status: true, - release_time: current_time, + release_time: now, stars: 100, }; @@ -1035,8 +1004,7 @@ mod tests { "description": "serde makes things other things", "target_name": "x86_64-pc-windows-msvc", "rustdoc_status": true, - "release_time": duration_to_str(current_time), - "release_time_rfc3339": now.rfc3339().to_string(), + "release_time": time::at(now).rfc3339().to_string(), "stars": 100 }); @@ -1049,8 +1017,7 @@ mod tests { "description": "serde makes things other things", "target_name": null, "rustdoc_status": true, - "release_time": duration_to_str(current_time), - "release_time_rfc3339": now.rfc3339().to_string(), + "release_time": time::at(now).rfc3339().to_string(), "stars": 100 }); @@ -1063,8 +1030,7 @@ mod tests { "description": null, "target_name": null, "rustdoc_status": true, - "release_time": duration_to_str(current_time), - "release_time_rfc3339": now.rfc3339().to_string(), + "release_time": time::at(now).rfc3339().to_string(), "stars": 100 }); diff --git a/src/web/rustdoc.rs b/src/web/rustdoc.rs index 893992529..af9bf2ac7 100644 --- a/src/web/rustdoc.rs +++ b/src/web/rustdoc.rs @@ -3,7 +3,7 @@ use super::crate_details::CrateDetails; use super::error::Nope; use super::file::File; -use super::page::Page; +use super::page::{RustdocPage, WebPage}; use super::pool::Pool; use super::redirect_base; use super::{match_version, MatchSemver}; @@ -15,39 +15,6 @@ use iron::Handler; use iron::{status, Url}; use postgres::Connection; use router::Router; -use serde::ser::{Serialize, SerializeStruct, Serializer}; - -#[derive(Debug, Default)] -struct RustdocPage { - head: String, - body: String, - body_class: String, - name: String, - full: String, - version: String, - description: Option, - crate_details: Option, -} - -impl Serialize for RustdocPage { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("RustdocPage", 9)?; - state.serialize_field("rustdoc_head", &self.head)?; - state.serialize_field("rustdoc_body", &self.body)?; - state.serialize_field("rustdoc_body_class", &self.body_class)?; - state.serialize_field("rustdoc_full", &self.full)?; - state.serialize_field("rustdoc_status", &true)?; - state.serialize_field("name", &self.name)?; - state.serialize_field("version", &self.version)?; - state.serialize_field("description", &self.description)?; - state.serialize_field("crate_details", &self.crate_details)?; - - state.end() - } -} #[derive(Clone)] pub struct RustLangRedirector { @@ -272,11 +239,11 @@ pub fn rustdoc_html_server_handler(req: &mut Request) -> IronResult { }; // Get the crate's details from the database - let crate_details = cexpect!(CrateDetails::new(&conn, &name, &version)); + let krate = cexpect!(CrateDetails::new(&conn, &name, &version)); // if visiting the full path to the default target, remove the target from the path // expects a req_path that looks like `[/:target]/.*` - if req_path.get(0).copied() == Some(&crate_details.metadata.default_target) { + if req_path.get(0).copied() == Some(&krate.metadata.default_target) { return redirect(&name, &version, &req_path[1..]); } @@ -312,17 +279,18 @@ pub fn rustdoc_html_server_handler(req: &mut Request) -> IronResult { let file_content = ctry!(String::from_utf8(file.0.content)); // Extract the head and body of the rustdoc file so that we can insert it into our own html - let (head, body, mut body_class) = ctry!(utils::extract_head_and_body(&file_content)); + let (rustdoc_head, rustdoc_body, mut rustdoc_body_class) = + ctry!(utils::extract_head_and_body(&file_content)); // Add the `rustdoc` classes to the html body - if body_class.is_empty() { - body_class = "rustdoc container-rustdoc".to_string(); + if rustdoc_body_class.is_empty() { + rustdoc_body_class = "rustdoc container-rustdoc".to_string(); } else { // rustdoc adds its own "rustdoc" class to the body - body_class.push_str(" container-rustdoc"); + rustdoc_body_class.push_str(" container-rustdoc"); } - let latest_release = crate_details.latest_release(); + let latest_release = krate.latest_release(); // Get the latest version of the crate let latest_version = latest_release.version.to_owned(); @@ -342,7 +310,7 @@ pub fn rustdoc_html_server_handler(req: &mut Request) -> IronResult { "/{}/{}/{}", name, latest_version, - path_for_version(&latest_path, &crate_details.doc_targets, &conn) + path_for_version(&latest_path, &krate.doc_targets, &conn) ) } else { format!("/crate/{}/{}", name, latest_version) @@ -355,35 +323,24 @@ pub fn rustdoc_html_server_handler(req: &mut Request) -> IronResult { // Drop the `rustdoc/:crate/:version[/:platform]` prefix inner_path.drain(..3).for_each(drop); - if inner_path.len() > 1 && crate_details.doc_targets.iter().any(|s| s == inner_path[0]) { + if inner_path.len() > 1 && krate.doc_targets.iter().any(|s| s == inner_path[0]) { inner_path.remove(0); } inner_path.join("/") }; - // Build the page of documentation - let content = RustdocPage { - head, - body, - body_class, - name, - full: file_content, - version, - crate_details: Some(crate_details), - ..Default::default() - }; - - // Build the page served to the user while setting options for templating - Page::new(content) - .set_true("show_package_navigation") - .set_true("package_navigation_documentation_tab") - .set_true("package_navigation_show_platforms_tab") - .set_bool("is_latest_version", is_latest_version) - .set("latest_path", &latest_path) - .set("latest_version", &latest_version) - .set("inner_path", &inner_path) - .to_resp("rustdoc") + RustdocPage { + latest_path, + latest_version, + inner_path, + is_latest_version, + rustdoc_head, + rustdoc_body, + rustdoc_body_class, + krate, + } + .into_response() } /// Checks whether the given path exists. @@ -564,10 +521,8 @@ impl Handler for SharedResourceHandler { #[cfg(test)] mod test { - use super::*; use crate::test::*; use reqwest::StatusCode; - use serde_json::json; use std::{collections::BTreeMap, iter::FromIterator}; fn try_latest_version_redirect( @@ -858,8 +813,8 @@ mod test { .create()?; let web = env.frontend(); - let redirect = latest_version_redirect("/dummy/0.1.0/dummy/", web)?; - assert_eq!(redirect, "/dummy/0.2.1/dummy/index.html"); + let redirect = dbg!(latest_version_redirect("/dummy/0.1.0/dummy/", web))?; + assert_eq!(dbg!(redirect), "/dummy/0.2.1/dummy/index.html"); let redirect = latest_version_redirect("/dummy/0.2.0/dummy/", web)?; assert_eq!(redirect, "/dummy/0.2.1/dummy/index.html"); @@ -1369,100 +1324,4 @@ mod test { Ok(()) }) } - - #[test] - fn serialize_rustdoc_page() { - let time = time::get_time(); - let details = json!({ - "name": "rcc", - "version": "100.0.0", - "description": null, - "authors": [], - "owners": [], - "authors_json": null, - "dependencies": null, - "release_time": super::super::duration_to_str(time), - "build_status": true, - "last_successful_build": null, - "rustdoc_status": true, - "repository_url": null, - "homepage_url": null, - "keywords": null, - "have_examples": true, - "target_name": "x86_64-unknown-linux-gnu", - "releases": [], - "github": true, - "yanked": false, - "github_stars": null, - "github_forks": null, - "github_issues": null, - "metadata": { - "name": "serde", - "version": "1.0.0", - "description": "serde does stuff", - "target_name": null, - "rustdoc_status": true, - "default_target": "x86_64-unknown-linux-gnu" - }, - "is_library": true, - "doc_targets": [], - "license": null, - "documentation_url": null - }); - - let mut page = RustdocPage { - head: "Whee".to_string(), - body: "

idk

".to_string(), - body_class: "docsrs-body".to_string(), - name: "rcc".to_string(), - full: "??".to_string(), - version: "100.0.100".to_string(), - description: Some("a Rust compiler in C. Wait, maybe the other way around".to_string()), - crate_details: Some(CrateDetails::default_tester(time)), - }; - - let correct_json = json!({ - "rustdoc_head": "Whee", - "rustdoc_body": "

idk

", - "rustdoc_body_class": "docsrs-body", - "rustdoc_full": "??", - "rustdoc_status": true, - "name": "rcc", - "version": "100.0.100", - "description": "a Rust compiler in C. Wait, maybe the other way around", - "crate_details": details - }); - - assert_eq!(correct_json, serde_json::to_value(&page).unwrap()); - - page.description = None; - let correct_json = json!({ - "rustdoc_head": "Whee", - "rustdoc_body": "

idk

", - "rustdoc_body_class": "docsrs-body", - "rustdoc_full": "??", - "rustdoc_status": true, - "name": "rcc", - "version": "100.0.100", - "description": null, - "crate_details": details - }); - - assert_eq!(correct_json, serde_json::to_value(&page).unwrap()); - - page.crate_details = None; - let correct_json = json!({ - "rustdoc_head": "Whee", - "rustdoc_body": "

idk

", - "rustdoc_body_class": "docsrs-body", - "rustdoc_full": "??", - "rustdoc_status": true, - "name": "rcc", - "version": "100.0.100", - "description": null, - "crate_details": null - }); - - assert_eq!(correct_json, serde_json::to_value(&page).unwrap()); - } } diff --git a/src/web/sitemap.rs b/src/web/sitemap.rs index 2fb3eebed..4984f166d 100644 --- a/src/web/sitemap.rs +++ b/src/web/sitemap.rs @@ -1,9 +1,10 @@ -use super::page::Page; -use super::pool::Pool; -use iron::headers::ContentType; -use iron::prelude::*; +use super::{ + page::{About, SitemapXml, WebPage}, + pool::Pool, +}; +use crate::docbuilder::Limits; +use iron::{headers::ContentType, prelude::*}; use serde_json::Value; -use std::collections::BTreeMap; pub fn sitemap_handler(req: &mut Request) -> IronResult { let conn = extension!(req, Pool).get()?; @@ -28,10 +29,9 @@ pub fn sitemap_handler(req: &mut Request) -> IronResult { }) .collect::>(); - let mut resp = ctry!(Page::new(releases).to_resp("sitemap")); - resp.headers - .set(ContentType("application/xml".parse().unwrap())); - Ok(resp) + // TODO: Only the second element of each tuple (the release time) is actually used + // in the template + SitemapXml { releases }.into_response() } pub fn robots_txt_handler(_: &mut Request) -> IronResult { @@ -41,23 +41,21 @@ pub fn robots_txt_handler(_: &mut Request) -> IronResult { } pub fn about_handler(req: &mut Request) -> IronResult { - let mut content = BTreeMap::new(); - let conn = extension!(req, Pool).get()?; let res = ctry!(conn.query("SELECT value FROM config WHERE name = 'rustc_version'", &[])); + let mut rustc_version = None; if let Some(row) = res.iter().next() { if let Some(Ok::(res)) = row.get_opt(0) { if let Some(vers) = res.as_str() { - content.insert("rustc_version".to_string(), Value::String(vers.to_string())); + rustc_version = Some(vers.to_owned()); } } } - content.insert( - "limits".to_string(), - serde_json::to_value(&crate::docbuilder::Limits::default().for_website()).unwrap(), - ); - - Page::new(content).title("About Docs.rs").to_resp("about") + About { + rustc_version, + limits: Limits::default(), + } + .into_response() } diff --git a/src/web/source.rs b/src/web/source.rs index 4851be467..b1bdf4912 100644 --- a/src/web/source.rs +++ b/src/web/source.rs @@ -1,68 +1,32 @@ //! Source code browser use super::file::File as DbFile; -use super::page::Page; +use super::page::{SourcePage, WebPage}; use super::pool::Pool; use super::MetaData; use iron::prelude::*; use postgres::Connection; use router::Router; -use serde::ser::{Serialize, SerializeStruct, Serializer}; +use serde::Serialize; use serde_json::Value; use std::cmp::Ordering; -use std::collections::HashMap; -/// A source file's type -#[derive(PartialEq, PartialOrd)] -enum FileType { - Dir, - Text, - Binary, - RustSource, -} - -/// A source file -#[derive(PartialEq, PartialOrd)] +/// A source file's name and mime type +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Serialize)] struct File { + /// The name of the file name: String, - file_type: FileType, + /// The MIME type of the file + mime: String, } /// A list of source files -struct FileList { +#[derive(Debug, Clone, PartialEq, Serialize)] +pub struct FileList { metadata: MetaData, files: Vec, } -impl Serialize for FileList { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("FileList", 2)?; - state.serialize_field("metadata", &self.metadata)?; - - let mut files = Vec::with_capacity(self.files.len()); - for file in &self.files { - let mut map = HashMap::with_capacity(2); - map.insert("name", Value::String(file.name.to_owned())); - - let file_type = match file.file_type { - FileType::Dir => "file_type_dir", - FileType::Text => "file_type_text", - FileType::Binary => "file_type_binary", - FileType::RustSource => "file_type_rust_source", - }; - map.insert(file_type, Value::Bool(true)); - - files.push(map); - } - state.serialize_field("files", &files)?; - - state.end() - } -} - impl FileList { /// Gets FileList from a request path /// @@ -125,19 +89,15 @@ impl FileList { let path_splited: Vec<&str> = path.split('/').collect(); // if path have '/' it is a directory - let ftype = if path_splited.len() > 1 { - FileType::Dir - } else if mime.starts_with("text") && path_splited[0].ends_with(".rs") { - FileType::RustSource - } else if mime.starts_with("text") { - FileType::Text + let mime = if path_splited.len() > 1 { + "dir".to_owned() } else { - FileType::Binary + mime.to_owned() }; let file = File { name: path_splited[0].to_owned(), - file_type: ftype, + mime, }; // avoid adding duplicates, a directory may occur more than once @@ -154,9 +114,9 @@ impl FileList { file_list.sort_by(|a, b| { // directories must be listed first - if a.file_type == FileType::Dir && b.file_type != FileType::Dir { + if a.mime == "dir" && b.mime != "dir" { Ordering::Less - } else if a.file_type != FileType::Dir && b.file_type == FileType::Dir { + } else if a.mime != "dir" && b.mime == "dir" { Ordering::Greater } else { a.name.to_lowercase().cmp(&b.name.to_lowercase()) @@ -220,7 +180,7 @@ pub fn source_browser_handler(req: &mut Request) -> IronResult { None }; - let (content, is_rust_source) = if let Some(file) = file { + let (file_content, is_rust_source) = if let Some(file) = file { // serve the file with DatabaseFileHandler if file isn't text and not empty if !file.0.mime.starts_with("text") && !file.is_empty() { return Ok(file.serve()); @@ -236,74 +196,21 @@ pub fn source_browser_handler(req: &mut Request) -> IronResult { (None, false) }; - let list = FileList::from_path(&conn, &name, &version, &req_path); - if list.is_none() { + let file_list = if let Some(file_list) = FileList::from_path(&conn, &name, &version, &req_path) + { + file_list + } else { use super::error::Nope; use iron::status; - return Err(IronError::new(Nope::NoResults, status::NotFound)); - } - - let page = Page::new(list) - .set_bool("show_parent_link", !req_path.is_empty()) - .set_true("javascript_highlightjs") - .set_true("show_package_navigation") - .set_true("package_source_tab"); - - if let Some(content) = content { - page.set("file_content", &content) - .set_bool("file_content_rust_source", is_rust_source) - .to_resp("source") - } else { - page.to_resp("source") - } -} -#[cfg(test)] -mod tests { - use super::*; - use serde_json::json; - - #[test] - fn serialize_file_list() { - let file_list = FileList { - metadata: MetaData { - name: "rcc".to_string(), - version: "0.0.0".to_string(), - description: Some("it compiles an unholy language".to_string()), - target_name: None, - rustdoc_status: true, - default_target: "x86_64-unknown-linux-gnu".to_string(), - }, - files: vec![ - File { - name: "main.rs".to_string(), - file_type: FileType::RustSource, - }, - File { - name: "lib.rs".to_string(), - file_type: FileType::RustSource, - }, - ], - }; - - let correct_json = json!({ - "metadata": { - "name": "rcc", - "version": "0.0.0", - "description": "it compiles an unholy language", - "target_name": null, - "rustdoc_status": true, - "default_target": "x86_64-unknown-linux-gnu" - }, - "files": [{ - "name": "main.rs", - "file_type_rust_source": true - }, { - "name": "lib.rs", - "file_type_rust_source": true - }], - }); + return Err(IronError::new(Nope::NoResults, status::NotFound)); + }; - assert_eq!(correct_json, serde_json::to_value(&file_list).unwrap(),); + SourcePage { + file_list, + show_parent_link: !req_path.is_empty(), + file_content, + is_rust_source, } + .into_response() } diff --git a/templates/about.hbs b/templates/about.hbs deleted file mode 100644 index bd52de966..000000000 --- a/templates/about.hbs +++ /dev/null @@ -1,198 +0,0 @@ -{{> header}} - -
-

- Docs.rs (formerly cratesfyi) is an - open source - documentation host for crates of the - Rust Programming Language. -

- -

- Docs.rs automatically builds crates' documentation released on - crates.io - using the nightly release of the Rust compiler. - {{#if content.rustc_version}} - The current version of the Rust compiler in use is {{content.rustc_version}}. - If you need a newer version of this compiler, check the - issues page - and file a new issue if you don't see an existing request. - {{/if}} -

- -

- The source code of Docs.rs is available on - GitHub. If - you ever encounter an issue, don't hesitate to report it! (And - thanks in advance!) -

- -

- The README of a crate is taken from the readme field defined in - Cargo.toml. If a crate doesn't have this field, - no README will be displayed. -

- -

Global sandbox limits

- -

- All the builds on docs.rs are executed inside a sandbox with limited - resources. The current limits are the following: -

- - - - {{#each content.limits}} - - - - - {{/each}} - -
{{{@key}}}{{this}}
- -

- If a build fails because it hit one of those limits please - open an issue - to get them increased for your crate. -

- -

Redirections

- -

- Docs.rs is using semver to parse URLs. You can use this feature to access - crates' documentation easily. Example of URL redirections for - clap crate: -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
URLRedirects to documentation of
docs.rs/clapLatest version of clap
docs.rs/clap/~22.* version
docs.rs/clap/~2.92.9.* version
docs.rs/clap/2.9.32.9.3 version (you don't need = unlike semver)
docs.rs/clap/*/clap/struct.App.htmlLatest version of this page (if it still exists). "latest" or "newest" work as well as *.
- -

Badges

-

- You can use badges to show state of your documentation to your users. - The default badge will be pointed at the latest version of a crate. - You can use version parameter to show status of documentation for - any version you want. -

-

- Badge will display in blue if docs.rs is successfully hosting your crate - documentation, and red if building documentation failing. -

-

Example badges for mio crate:

- - - - - - - - - - - - - - - - - - - - - -
URLBadge
Latest version: https://docs.rs/mio/badge.svgmio
Version 0.4.4: https://docs.rs/mio/badge.svg?version=0.4.4mio
Version 0.1.0: https://docs.rs/mio/badge.svg?version=0.1.0mio
- -

Metadata for custom builds

- -

You can customize docs.rs builds by defining [package.metadata.docs.rs] table in your crates' `Cargo.toml`.

- -

The available configuration flags you can customize are:

- -
[package]
-name = "test"
-
-[package.metadata.docs.rs]
-
-# Features to pass to Cargo (default: none)
-features = [ "feature1", "feature2" ]
-
-# Whether to pass `--all-features` to Cargo (default: false)
-all-features = true
-
-# Whether to pass `--no-default-features` to Cargo (default: false)
-no-default-features = true
-
-# Target to test build on, used as the default landing page (default: "x86_64-unknown-linux-gnu")
-#
-# Any target supported by rustup can be used.
-default-target = "x86_64-unknown-linux-gnu"
-
-# Targets to build (default: see below)
-#
-# Any target supported by rustup can be used.
-#
-# Default targets:
-# - x86_64-unknown-linux-gnu
-# - x86_64-apple-darwin
-# - x86_64-pc-windows-msvc
-# - i686-unknown-linux-gnu
-# - i686-pc-windows-msvc
-#
-# Set this to `[]` to only build the default target.
-#
-# If `default-target` is unset, the first element of `targets` is treated as the default target.
-# Otherwise, these `targets` are built in addition to the default target.
-# If both `default-target` and `targets` are unset,
-#   all tier-one targets will be built and `x86_64-unknown-linux-gnu` will be used as the default target.
-targets = [ "x86_64-apple-darwin", "x86_64-pc-windows-msvc" ]
-
-# Additional `RUSTFLAGS` to set (default: none)
-rustc-args = [ "--example-rustc-arg" ]
-
-# Additional `RUSTDOCFLAGS` to set (default: none)
-rustdoc-args = [ "--example-rustdoc-arg" ]
- -

Test crate documentation build locally

-

The docs.rs README describes how to build an unpublished crate's documentation locally using the same build environment as the build agent.

- -

Version

-

Currently running Docs.rs version is: {{cratesfyi_version}} - -

Contact

-

- Docs.rs is run and maintained by the Docs.rs team. - You can find us in #docs-rs on Discord. -

- -
- -{{> footer}} diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 000000000..5b36df95c --- /dev/null +++ b/templates/base.html @@ -0,0 +1,43 @@ +{%- import "macros.html" as macros -%} +{%- import "header/package_navigation.html" as navigation -%} + + + + + + + + + {%- block meta -%}{%- endblock meta -%} + + + + + + + + + {%- block css -%}{%- endblock css -%} + + + + {%- block title -%} Docs.rs {%- endblock title -%} + + + + {%- include "header/topbar.html" -%} + + {%- block header %}{% endblock header -%} + + {%- block body -%}{%- endblock body -%} + + + + + + {%- block javascript -%}{%- endblock javascript -%} + + diff --git a/templates/builds.hbs b/templates/builds.hbs deleted file mode 100644 index 0fefd76d6..000000000 --- a/templates/builds.hbs +++ /dev/null @@ -1,66 +0,0 @@ -{{> header}} - -{{#with content}} -
-
- - {{#if build_details}} -
- Build #{{build_details.id}} {{build_details.build_time}} -
-
$ rustc --version
-{{build_details.rustc_version}}
-$ cratesfyi --version
-{{build_details.cratesfyi_version}}
-$ cratesfyi ...
-{{build_details.output}}
- {{/if}} - -
- Builds -
- - - -
-

{{metadata.name}}'s sandbox limits

- -

- All the builds on docs.rs are executed inside a sandbox with limited - resources. The limits for this crate are the following: -

- - - - {{#each limits}} - - - - - {{/each}} - -
{{{@key}}}{{this}}
-

- If a build fails because it hit one of those limits please - open an issue - to get them increased. -

-
-
-
-{{/with}} - -{{> footer}} diff --git a/templates/crate/builds.html b/templates/crate/builds.html new file mode 100644 index 000000000..286f78c9e --- /dev/null +++ b/templates/crate/builds.html @@ -0,0 +1,82 @@ +{%- extends "base.html" -%} + +{%- block title -%} + {{ macros::doc_title(name=metadata.name, version=metadata.version) }} +{%- endblock title -%} + +{%- block header -%} + {{ navigation::package_navigation(metadata=metadata, active_tab="builds") }} +{%- endblock header -%} + +{%- block body -%} +
+
+ {# If there is a build log then show it #} + {# TODO: When viewing a build log, show a back button or a hide button #} + {%- if build_log -%} +
+ Build #{{ build_log.id }} {{ build_log.build_time | date(format="%+") }} +
+ + {%- filter dedent -%} +
+                        $ rustc --version
+                        {{ build_log.rustc_version }}
+                        $ docsrs --version
+                        {{ build_log.docsrs_version }}
+                        $ docsrs ...
+                        {{ build_log.output }}
+                    
+ {%- endfilter -%} + {%- endif -%} + +
+ Builds +
+ + + +
+ {# BuildsPage.metadata is an `Option`, so accessing it can fail #} + {%- if metadata -%} +

{{ metadata.name }}'s sandbox limits

+ {%- else -%} +

Sandbox limits

+ {%- endif -%} + +

+ All the builds on docs.rs are executed inside a sandbox with limited + resources. The limits for this crate are the following: +

+ + {{ macros::crate_limits(limits=limits) }} + +

+ If a build fails because it hit one of those limits please + open an issue + to get them increased. +

+
+
+
+{%- endblock body -%} diff --git a/templates/crate/details.html b/templates/crate/details.html new file mode 100644 index 000000000..f97469c5f --- /dev/null +++ b/templates/crate/details.html @@ -0,0 +1,302 @@ +{%- extends "base.html" -%} + +{%- block title -%} + {{ macros::doc_title(name=details.name, version=details.version) }} +{%- endblock title -%} + +{%- block header -%} + {# Set the active tab to the `crate` tab #} + {{ navigation::package_navigation(metadata=details.metadata, active_tab="crate") }} +{%- endblock header -%} + +{%- block body -%} +
+
+
+
+ +
+
+ +
+ {# If the release is not a library #} + {%- if not details.is_library -%} +
+ {{ details.name }}-{{ details.version }} is not a library. +
+ + {# If the release has been yanked and is a library #} + {%- elif details.yanked -%} +
+ {{ details.name }}-{{ details.version }} has been yanked. +
+ + {# If the build succeeded, isn't yanked and is a library #} + {%- elif details.build_status -%} + {# If there are no docs display a warning #} + {%- if not details.rustdoc_status -%} +
{{ details.name }}-{{ details.version }} doesn't have any documentation.
+ {%- endif -%} + + {# If there's a readme, display it #} + {%- if details.readme -%} + {{ details.readme | safe }} + + {# If there's not a readme then attempt to display docs #} + {%- elif details.rustdoc -%} + {{ details.rustdoc | safe }} + {%- endif -%} + + {# If the build failed, the release isn't yanked and the release is a library #} + {%- else -%} + {# Display a warning telling the user we failed to build the docs #} +
+ docs.rs failed to build {{ details.name }}-{{ details.version }}
Please check the + build logs and, if you believe this is + docs.rs' fault, open an issue. +
+ + {# If there is one, display the most next most recent successful build #} + {%- if details.last_successful_build -%} + + {%- endif -%} + {%- endif -%} +
+
+
+{%- endblock body -%} + +{%- block css -%} + {{ macros::highlight_css() }} +{%- endblock css -%} + +{%- block javascript -%} + {# Enable and load Rust and TOML syntax highlighting #} + {{ macros::highlight_js(languages=["rust", "ini"]) }} + + +{% endblock javascript -%} diff --git a/templates/crate/source.html b/templates/crate/source.html new file mode 100644 index 000000000..0e81d0be5 --- /dev/null +++ b/templates/crate/source.html @@ -0,0 +1,104 @@ +{%- extends "base.html" -%} + +{%- block title -%} + {{ macros::doc_title(name=file_list.metadata.name, version=file_list.metadata.version) }} +{%- endblock title -%} + +{%- block header -%} + {# Set the active tab to the `source` tab #} + {{ navigation::package_navigation(metadata=file_list.metadata, active_tab="source") }} +{%- endblock header -%} + +{%- block body -%} +
+
+
+
+ +
+
+ + {# If the file has content, then display it in a codeblock #} + {%- if file_content -%} +
+
{{ file_content }}
+
+ {%- endif -%} +
+
+{%- endblock body -%} + +{%- block css -%} + {# Highlight.js CSS #} + {{ macros::highlight_css() }} +{%- endblock css -%} + +{%- block javascript -%} + {# Highlight.js JavaScript #} + {{ macros::highlight_js(languages=["rust", "ini", "markdown"]) }} +{%- endblock javascript -%} diff --git a/templates/crate_details.hbs b/templates/crate_details.hbs deleted file mode 100644 index 1a1385708..000000000 --- a/templates/crate_details.hbs +++ /dev/null @@ -1,105 +0,0 @@ -{{> header}} - - -{{#with content}} -
-
-
-
- -
-
-
- {{#unless is_library}} -
{{name}}-{{version}} is not a library.
- {{else}} - {{#if yanked}} -
{{name}}-{{version}} has been yanked.
- {{else}} - {{#unless build_status}} -
docs.rs failed to build {{name}}-{{version}}
Please check the build logs and, if you believe this is docs.rs' fault, open an issue.
- {{#if last_successful_build}} -
Visit the last successful build: {{name}}-{{last_successful_build}}
- {{/if}} - {{else}} - {{#unless rustdoc_status}} -
{{name}}-{{version}} doesn't have any documentation.
- {{/unless}} - {{/unless}} - {{/if}} - {{/unless}} - {{#if readme}} - {{{readme}}} - {{else}} - {{{rustdoc}}} - {{/if}} -
-
- -
-{{/with}} - - -{{> footer}} diff --git a/templates/docsrs/Cargo.toml.example b/templates/docsrs/Cargo.toml.example new file mode 100644 index 000000000..6ae0de57f --- /dev/null +++ b/templates/docsrs/Cargo.toml.example @@ -0,0 +1,44 @@ +{# The example Cargo.toml used in about.html #} +[package] +name = "test" + +[package.metadata.docs.rs] + +# Features to pass to Cargo (default: none) +features = [ "feature1", "feature2" ] + +# Whether to pass `--all-features` to Cargo (default: false) +all-features = true + +# Whether to pass `--no-default-features` to Cargo (default: false) +no-default-features = true + +# Target to test build on, used as the default landing page (default: "x86_64-unknown-linux-gnu") +# +# Any target supported by rustup can be used. +default-target = "x86_64-unknown-linux-gnu" + +# Targets to build (default: see below) +# +# Any target supported by rustup can be used. +# +# Default targets: +# - x86_64-unknown-linux-gnu +# - x86_64-apple-darwin +# - x86_64-pc-windows-msvc +# - i686-unknown-linux-gnu +# - i686-pc-windows-msvc +# +# Set this to `[]` to only build the default target. +# +# If `default-target` is unset, the first element of `targets` is treated as the default target. +# Otherwise, these `targets` are built in addition to the default target. +# If both `default-target` and `targets` are unset, +# all tier-one targets will be built and `x86_64-unknown-linux-gnu` will be used as the default target. +targets = [ "x86_64-apple-darwin", "x86_64-pc-windows-msvc" ] + +# Additional `RUSTFLAGS` to set (default: none) +rustc-args = [ "--example-rustc-arg" ] + +# Additional `RUSTDOCFLAGS` to set (default: none) +rustdoc-args = [ "--example-rustdoc-arg" ] diff --git a/templates/docsrs/about.html b/templates/docsrs/about.html new file mode 100644 index 000000000..31ebaf7bc --- /dev/null +++ b/templates/docsrs/about.html @@ -0,0 +1,195 @@ +{% extends "base.html" -%} + +{%- block title -%} About Docs.rs {%- endblock title -%} + +{%- block body -%} + {%- set docsrs_repo = "https://github.com/rust-lang/docs.rs" -%} + +

About Docs.rs

+
+

+ Docs.rs (formerly cratesfyi) is an + open source + documentation host for crates of the + Rust Programming Language. +

+ +

+ Docs.rs automatically builds crates' documentation released on + crates.io + using the nightly release of the Rust compiler. + + {%- if rustc_version -%} + The current version of the Rust compiler in use is {{ rustc_version }}. + If you need a newer version of this compiler, check the + issues page + and file a new issue if you don't see an existing request. + {%- endif -%} +

+ +

+ The source code of Docs.rs is available on + GitHub. If + you ever encounter an issue, don't hesitate to report it! (And + thanks in advance!) +

+ +

+ The README of a crate is taken from the readme field defined in + Cargo.toml. If a crate doesn't have this field, + no README will be displayed. +

+ +

Global sandbox limits

+ +

+ All the builds on docs.rs are executed inside a sandbox with limited + resources. The current limits are the following: +

+ + {{ macros::crate_limits(limits=limits) }} + +

+ If a build fails because it hit one of those limits please + open an issue + to get them increased for your crate. +

+ +

Redirections

+ +

+ Docs.rs is using semver to parse URLs. You can use this feature to access + crates' documentation easily. Example of URL redirections for + clap crate: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
URLRedirects to documentation of
docs.rs/clapLatest version of clap
docs.rs/clap/~22.* version
docs.rs/clap/~2.92.9.* version
+ docs.rs/clap/2.9.3 + + 2.9.3 version (you don't need = unlike semver) +
+ docs.rs/clap/*/clap/struct.App.html + + Latest version of this page (if it still exists). "latest" or "newest" work as well as + *. +
+ +

Badges

+ +

+ You can use badges to show state of your documentation to your users. + The default badge will be pointed at the latest version of a crate. + You can use version parameter to show status of documentation for + any version you want. +

+ +

+ Badge will display in blue if docs.rs is successfully hosting your crate + documentation, and red if building documentation failing. +

+ +

Example badges for mio crate:

+ + + + + + + + + + {%- set mio_badge = "https://docs.rs/mio/badge.svg" -%} + + + + + + + + + + + + + + + +
URLBadge
Latest version: {{ mio_badge }}mio
+ Version 0.4.4: {{ mio_badge }}?version=0.4.4 + mio
+ Version 0.1.0: {{ mio_badge }}?version=0.1.0 + mio
+ +

Metadata for custom builds

+ +

+ You can customize docs.rs builds by defining [package.metadata.docs.rs] + table in your crates' Cargo.toml. +

+ +

The available configuration flags you can customize are:

+ + {# Include the example file #} +
{%- include "docsrs/Cargo.toml.example" -%}
+ +

Test crate documentation build locally

+ {%- set build_subcommand = docsrs_repo ~ "/blob/master/README.md#build-subcommand" -%} +

+ The docs.rs README describes how to build an + unpublished crate's documentation locally using the same build nvironment as the build agent. +

+ +

Version

+

Currently running Docs.rs version is: {{ docsrs_version() }}

+ +

Contact

+ {%- set governance_link = "https://www.rust-lang.org/governance/teams/dev-tools#docs-rs" -%} +

+ Docs.rs is run and maintained by the Docs.rs team. + You can find us in #docs-rs on Discord. +

+ +
+{%- endblock body -%} + +{%- block css -%} + {{ macros::highlight_css() }} +{%- endblock css -%} + +{%- block javascript -%} + {{ macros::highlight_js(languages=["ini"]) }} +{%- endblock javascript -%} diff --git a/templates/docsrs/home.html b/templates/docsrs/home.html new file mode 100644 index 000000000..059bd1372 --- /dev/null +++ b/templates/docsrs/home.html @@ -0,0 +1,108 @@ +{%- extends "base.html" -%} + +{%- block title -%}Docs.rs{%- endblock title -%} + +{%- block body -%} +
+

Docs.rs

+ +
+
+ +
+ +
+ + +
+
+
+ +
+
+ + + +
+
+{%- endblock body -%} + +{%- block javascript -%} + +{%- endblock javascript -%} diff --git a/templates/docsrs/sitemap.xml b/templates/docsrs/sitemap.xml new file mode 100644 index 000000000..2eeb51e9c --- /dev/null +++ b/templates/docsrs/sitemap.xml @@ -0,0 +1,9 @@ + + + {%- for release in releases -%} + + https://docs.rs/{{ release[0] }} + {{ release[1] | escape_xml }} + + {%- endfor -%} + diff --git a/templates/error.hbs b/templates/error.hbs deleted file mode 100644 index bf2fd7d93..000000000 --- a/templates/error.hbs +++ /dev/null @@ -1,2 +0,0 @@ -{{> header}} -{{> footer}} diff --git a/templates/error.html b/templates/error.html new file mode 100644 index 000000000..172eda12f --- /dev/null +++ b/templates/error.html @@ -0,0 +1,12 @@ +{%- extends "base.html" -%} + +{%- block header -%} +
+
+

{{ title }}

+
+ {# Needed for padding, maybe add a witty quote or something? #} +
+
+
+{%- endblock header -%} diff --git a/templates/footer.hbs b/templates/footer.hbs deleted file mode 100644 index 17ca3265c..000000000 --- a/templates/footer.hbs +++ /dev/null @@ -1,5 +0,0 @@ -{{#if varsb.javascript_highlightjs}}{{/if}} - - - - diff --git a/templates/header.hbs b/templates/header.hbs deleted file mode 100644 index d1a001cfd..000000000 --- a/templates/header.hbs +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - {{#if varsb.javascript_highlightjs}} - - - - {{/if}} - {{#if varsb.javascript_highchartjs}} - - {{/if}} - {{#if title}}{{title}} - {{/if}}{{#if content.metadata.name}}{{content.metadata.name}} {{content.metadata.version}} - {{/if}}Docs.rs - - - - {{> navigation}} diff --git a/templates/header/global_alert.html b/templates/header/global_alert.html new file mode 100644 index 000000000..bf281851d --- /dev/null +++ b/templates/header/global_alert.html @@ -0,0 +1,12 @@ +{# Get the current global alert #} +{%- set global_alert = global_alert() -%} + +{# If there is a global alert, render it #} +{%- if global_alert -%} +
  • + + + {{ global_alert.text }} + +
  • +{%- endif -%} diff --git a/templates/header/package_navigation.html b/templates/header/package_navigation.html new file mode 100644 index 000000000..d832e660b --- /dev/null +++ b/templates/header/package_navigation.html @@ -0,0 +1,99 @@ +{# + The standard package navigation menu + + * `title` A possibly-null string. If it is null, `metadata.name metadata.version` will be used as the title + * `metadata` A non-null instance of the MetaData struct + * `platforms` A possibly-null vector of strings + * `active_tab` A string with one of the following values: + * `crate` + * `source` + * `builds` + + Note: `false` here is acting as a pseudo-null value since you can't directly construct null values + and tera requires all parameters without defaults to be filled +#} +{% macro package_navigation(title=false, metadata, platforms=false, active_tab) %} +
    +
    + {# Page title #} +

    + {%- if title -%} + {{ title }} + {%- else -%} + {{ metadata.name }} {{ metadata.version }} + + {%- endif -%} +

    + + {# Page description #} +
    + {%- if metadata.description -%} + {{ metadata.description }} + {%- endif -%} +
    + +
    + {# If there are platforms, show a dropdown with them #} + {%- if platforms -%} + + {%- endif -%} + +
      + {# The partial path of the crate, `:name/:release` #} + {%- set crate_path = metadata.name ~ "/" ~ metadata.version -%} + + {# If docs are built, show a tab for them #} + {%- if metadata.rustdoc_status -%} +
    • + {# The docs tab redirects to the docs, so the tab will never be selected and seen #} + + + Documentation + +
    • + {%- endif -%} + + {# The crate information tab #} +
    • + + Crate + +
    • + + {# The source view tab #} +
    • + + + Source + +
    • + + {# The builds tab #} +
    • + + + Builds + +
    • +
    +
    +
    +
    +{% endmacro package_navigation %} diff --git a/templates/header/topbar.html b/templates/header/topbar.html new file mode 100644 index 000000000..35c1a7797 --- /dev/null +++ b/templates/header/topbar.html @@ -0,0 +1,96 @@ +{# + This is the unchanging top bar that is on every single page. + The only piece of context it can take is `search_query`, which should + be a string and will populate the search field if it exists +#} + diff --git a/templates/macros.html b/templates/macros.html new file mode 100644 index 000000000..2f8738cdc --- /dev/null +++ b/templates/macros.html @@ -0,0 +1,77 @@ +{# Constructs a title based on the given crate name and version #} +{% macro doc_title(name, version) %} + {%- if name -%} + {{ name }} {{ version | default(value="") }} - Docs.rs + {%- else -%} + Docs.rs + {%- endif -%} +{% endmacro doc_title %} + +{# + Makes the appropriate JS imports for highlighting + * `languages` An array of strings where each is a valid highlight.js language +#} +{% macro highlight_js(languages) %} + {# Load the highlight script #} + + + {# Load the script for each provided language #} + {%- for language in languages -%} + + {%- endfor -%} + + {# Activate highlighting #} + +{% endmacro highlight_js %} + +{# Makes the appropriate CSS imports for highlighting #} +{% macro highlight_css() %} + {# Load the highlighting theme css #} + +{% endmacro highlight_css %} + +{# + Creates a formatted table showing the resource limits of a crate + * `limits` A non-null `Limits` struct +#} +{% macro crate_limits(limits) %} + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Available RAM{{ limits.memory | filesizeformat }}
    Maximum rustdoc execution time{{ limits.timeout.secs | timeformat }}
    Maximum size of a build log{{ limits.max_log_size | filesizeformat }}
    Network access + {%- if limits.networking -%} + allowed + {%- else -%} + blocked + {%- endif -%} +
    Maximum number of build targets{{ limits.targets }}
    +{% endmacro crate_limits %} diff --git a/templates/navigation.hbs b/templates/navigation.hbs deleted file mode 100644 index eefaa13d8..000000000 --- a/templates/navigation.hbs +++ /dev/null @@ -1,93 +0,0 @@ - - - {{#unless varsb.hide_package_navigation}} -
    -
    -

    - {{#if title}} - {{title}} - {{else}} - {{content.metadata.name}} {{content.metadata.version}} - - {{/if}} -

    -
    {{#if content.metadata.description }}{{content.metadata.description}}{{else}}{{varss.description}}{{/if}}
    - - {{#if ../varsb.show_package_navigation}} -
    - {{#if ../varsb.package_navigation_show_platforms_tab}} - - {{/if}} - {{#with content.metadata}} - - {{/with}} -
    - {{/if}} - - {{#if varsb.show_releases_navigation}} -
    - -
    - {{/if}} -
    -
    - {{/unless}} diff --git a/templates/navigation_global_alert.hbs b/templates/navigation_global_alert.hbs deleted file mode 100644 index 428bb02c3..000000000 --- a/templates/navigation_global_alert.hbs +++ /dev/null @@ -1,9 +0,0 @@ -{{#if ../has_global_alert}} -
  • - - - {{../global_alert.text}} - -
  • -{{/if}} - diff --git a/templates/navigation_rustdoc.hbs b/templates/navigation_rustdoc.hbs deleted file mode 100644 index f17aedbdc..000000000 --- a/templates/navigation_rustdoc.hbs +++ /dev/null @@ -1,139 +0,0 @@ - diff --git a/templates/releases.hbs b/templates/releases.hbs deleted file mode 100644 index 30bf47568..000000000 --- a/templates/releases.hbs +++ /dev/null @@ -1,144 +0,0 @@ -{{> header}} - - - -{{#if varsb.show_search_form}} -
    -

    Docs.rs

    - -
    -
    -
    - - -
    -
    - -
    -{{/if}} - -
    -
    - {{#if varsb.show_search_form}} - - {{else}} - - {{/if}} - - - {{#unless varsb.show_search_form}} - - {{/unless}} -
    -
    - -{{> footer}} diff --git a/templates/releases/activity.html b/templates/releases/activity.html new file mode 100644 index 000000000..a8e846d2c --- /dev/null +++ b/templates/releases/activity.html @@ -0,0 +1,57 @@ +{%- extends "base.html" -%} +{%- import "releases/header.html" as release_macros -%} + +{%- block title -%}Releases - Docs.rs{%- endblock title -%} + +{%- block header -%} + {{ release_macros::header(title="Releases", description=description, tab="activity") }} +{%- endblock header -%} + +{%- block body -%} +
    +
    +
    +{%- endblock body -%} + +{# TODO: Do this with tera alone #} +{%- block javascript -%} + + + +{%- endblock javascript -%} diff --git a/templates/releases/feed.xml b/templates/releases/feed.xml new file mode 100644 index 000000000..175449ed9 --- /dev/null +++ b/templates/releases/feed.xml @@ -0,0 +1,39 @@ + + + Docs.rs + Recent Rust crates + + + + + + + urn:docs-rs:{{ docsrs_version() }} + {{ recent_releases[0].release_time | default(value=now()) | date(format="%+") }} + + {%- for release in recent_releases -%} + {%- set name = release.name | escape_xml -%} + {%- set version = release.version | escape_xml -%} + {%- if rustdoc_status -%} + {%- set link = "/" ~ release.name ~ "/" ~ release.version ~ "/" ~ release.target_name -%} + {%- else -%} + {%- set link = "/crate/" ~ release.name ~ "/" ~ version -%} + {%- endif -%} + + + {{ name }}-{{ version }} + + + urn:docs-rs:{{ name }}:{{ version }} + {{ release.release_time | date(format="%+") }} + + + {{ release.description | default(value="-") | escape_xml }} + + + + docs.rs + + + {%- endfor -%} + diff --git a/templates/releases/header.html b/templates/releases/header.html new file mode 100644 index 000000000..4d6b0df60 --- /dev/null +++ b/templates/releases/header.html @@ -0,0 +1,77 @@ +{# + Builds the header for the release dashboard + * `title` A string + * `description` A string + * `tab` A string with one of the following values + * `recent` + * `stars` + * `recent-failures` + * `failures` + * `activity` + * `queue` + * `author` + * `author` A string, used for the authors page +#} +{% macro header(title, description, tab, author=false) %} +
    +
    +

    {{ title }}

    +
    {{ description | default(value="") }}
    + + {# This does double-duty as the search, so hide all tabs when we're searching something #} + {%- if tab != "search" -%} +
    + +
    + {%- endif -%} +
    +
    +{% endmacro header %} diff --git a/templates/releases/queue.html b/templates/releases/queue.html new file mode 100644 index 000000000..6d17f5aaf --- /dev/null +++ b/templates/releases/queue.html @@ -0,0 +1,125 @@ +{%- extends "base.html" -%} +{%- import "releases/header.html" as release_macros -%} + +{%- block title -%}Build Queue - Docs.rs{%- endblock title -%} + +{%- block header -%} + {{ release_macros::header(title="Build Queue", description=description, tab="queue") }} +{%- endblock header -%} + +{%- block body -%} +
    +
    + +
    + {%- if varsb.queue_empty -%} + There is nothing in queue + {%- else -%} + Queue + {%- endif -%} +
    + +
      + {%- for crate in queue -%} +
    1. + + {{ crate[0] }} {{ crate[1] }} + + + {%- if crate[2] -%} + (priority: {{ crate[2] }}) + {%- endif -%} +
    2. + {%- endfor -%} +
    +
    +
    +{%- endblock body -%} + +{%- block javascript -%} + +{%- endblock javascript -%} diff --git a/templates/releases/releases.html b/templates/releases/releases.html new file mode 100644 index 000000000..ae734cffd --- /dev/null +++ b/templates/releases/releases.html @@ -0,0 +1,100 @@ +{%- extends "base.html" -%} +{%- import "releases/header.html" as release_macros -%} + +{%- block title -%}Releases - Docs.rs{%- endblock title -%} + +{%- block header -%} + {# These all have defaults so searches work #} + {{ release_macros::header(title="Releases", description=description | default(value=""), tab=release_type, author=author | default(value=false)) }} +{%- endblock header -%} + +{%- block body -%} +
    +
    + + + +
    +
    +{%- endblock body -%} + +{%- block javascript -%} + +{%- endblock javascript -%} diff --git a/templates/releases_activity.hbs b/templates/releases_activity.hbs deleted file mode 100644 index c82617b1a..000000000 --- a/templates/releases_activity.hbs +++ /dev/null @@ -1,41 +0,0 @@ -{{> header}} -{{#with content}} - -
    -
    -
    - - -{{/with}} -{{> footer}} diff --git a/templates/releases_feed.hbs b/templates/releases_feed.hbs deleted file mode 100644 index e60c4e1c1..000000000 --- a/templates/releases_feed.hbs +++ /dev/null @@ -1,21 +0,0 @@ - - -Docs.rs -Recent Rust crates - - - - -urn:docs-rs:{{cratesfyi_version_safe}} -{{content.[0].release_time_rfc3339}} -{{#each content}} - -{{name}}-{{version}} - -urn:docs-rs:{{name}}:{{version}} -{{release_time_rfc3339}} -{{#if description}}{{description}}{{else}}-{{/if}} -docs.rs - -{{/each}} - diff --git a/templates/releases_queue.hbs b/templates/releases_queue.hbs deleted file mode 100644 index 1d988eed7..000000000 --- a/templates/releases_queue.hbs +++ /dev/null @@ -1,29 +0,0 @@ -{{> header}} - -
    -
    - -
    - {{#if varsb.queue_empty}} - There is nothing in queue - {{else}} - Queue - {{/if}} -
    - -
      - {{#each content}} -
    1. - - {{this.[0]}} {{this.[1]}} - - {{#if this.[2]}} - (priority: {{this.[2]}}) - {{/if}} -
    2. - {{/each}} -
    -
    -
    - -{{> footer}} diff --git a/templates/rustdoc.hbs b/templates/rustdoc.hbs deleted file mode 100644 index f39289c3d..000000000 --- a/templates/rustdoc.hbs +++ /dev/null @@ -1,34 +0,0 @@ - - - - {{{content.rustdoc_head}}} - - - - - - - -{{> navigation_rustdoc}} -
    - {{{content.rustdoc_body}}} -
    - - - - diff --git a/templates/rustdoc/navigation.html b/templates/rustdoc/navigation.html new file mode 100644 index 000000000..d87346608 --- /dev/null +++ b/templates/rustdoc/navigation.html @@ -0,0 +1,248 @@ +{# The url of the current release, `/crate/:name/:version` #} +{%- set crate_url = "/crate/" ~ krate.name ~ "/" ~ krate.version -%} + + diff --git a/templates/rustdoc/page.html b/templates/rustdoc/page.html new file mode 100644 index 000000000..1cac66fd3 --- /dev/null +++ b/templates/rustdoc/page.html @@ -0,0 +1,56 @@ +{%- import "macros.html" as macros -%} + + + + + + {# Include any head that rustdoc requires #} + {{ rustdoc_head | safe }} + + + + + + + + {# Highlight.js CSS #} + {{ macros::highlight_css() }} + + {{ macros::doc_title(name=krate.name, version=krate.version) }} + + + + {%- include "rustdoc/navigation.html" -%} + + {# Make the body of the page the documentation generated by rustdoc #} +
    + {{ rustdoc_body | safe }} +
    + + + + + + + {# Highlight.js JavaScript #} + {{ macros::highlight_js(languages=["rust", "ini"]) }} + + diff --git a/templates/sitemap.hbs b/templates/sitemap.hbs deleted file mode 100644 index a4d131285..000000000 --- a/templates/sitemap.hbs +++ /dev/null @@ -1,9 +0,0 @@ - - - {{#each content}} - - https://docs.rs/{{this.[0]}} - {{this.[1]}} - - {{/each}} - diff --git a/templates/source.hbs b/templates/source.hbs deleted file mode 100644 index 8ab5a72ad..000000000 --- a/templates/source.hbs +++ /dev/null @@ -1,38 +0,0 @@ -{{> header}} - - -{{#with content}} -
    -
    - - {{#if ../varss.file_content}} -
    -
    {{ ../varss.file_content }}
    -
    - {{/if}} -
    -
    -{{/with}} - -{{> footer}}