diff --git a/Cargo.lock b/Cargo.lock index c3949548f..3f4a0b390 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,6 +18,14 @@ 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" @@ -366,6 +374,7 @@ 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)", "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)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -984,6 +993,34 @@ name = "half" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "handlebars" +version = "0.29.1" +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)", +] + +[[package]] +name = "handlebars-iron" +version = "0.25.2" +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)", +] + [[package]] name = "heck" version = "0.3.1" @@ -1925,6 +1962,11 @@ dependencies = [ "plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pest" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "pest" version = "1.0.6" @@ -2483,6 +2525,18 @@ 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" @@ -2502,6 +2556,14 @@ 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" @@ -2708,6 +2770,15 @@ 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" @@ -3230,6 +3301,14 @@ 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" @@ -3577,6 +3656,11 @@ name = "ucd-trie" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ucd-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unic-char-property" version = "0.9.0" @@ -3728,6 +3812,11 @@ 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 = "vcpkg" version = "0.2.8" @@ -3753,6 +3842,16 @@ 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" @@ -3960,6 +4059,7 @@ dependencies = [ "checksum ab_glyph_rasterizer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2b7e4e8cf778db814365e46839949ca74df4efb10e87ba4913e6ec5967ef0285" "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 arc-swap 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b585a98a234c46fc563103e9278c9391fde1f4e6850334da895d27edb9580f62" @@ -4065,6 +4165,8 @@ dependencies = [ "checksum h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" "checksum h2 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "79b7246d7e4b979c03fa093da39cfb3617a96bbeee6310af63991668d7e843ff" "checksum half 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d36fab90f82edc3c747f9d438e06cf0a491055896f2a279638bb5beed6c40177" +"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" @@ -4166,6 +4268,7 @@ dependencies = [ "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" @@ -4227,8 +4330,10 @@ 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.10.6 (registry+https://github.com/rust-lang/crates.io-index)" = "3b82c9238b305f26f53443e3a4bc8528d64b8d0bee408ec949eb7bf5635ec680" @@ -4247,6 +4352,7 @@ 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" @@ -4304,6 +4410,7 @@ dependencies = [ "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.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d3dc76004a03cec1c5932bca4cdc2e39aaa798e3f82363dd94f9adf6098c12f" @@ -4337,6 +4444,7 @@ dependencies = [ "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-trie 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" +"checksum ucd-util 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c85f514e095d348c279b1e5cd76795082cf15bd59b93207832abe0b1d8fed236" "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" @@ -4358,11 +4466,13 @@ 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 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 want 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" diff --git a/Cargo.toml b/Cargo.toml index 681a49428..1526c1b73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,7 @@ 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" diff --git a/dockerfiles/Dockerfile b/dockerfiles/Dockerfile index 206a578e1..d5f79bba9 100644 --- a/dockerfiles/Dockerfile +++ b/dockerfiles/Dockerfile @@ -74,6 +74,7 @@ RUN mkdir -p /opt/docsrs/prefix COPY --from=build /build/target/release/cratesfyi /usr/local/bin COPY static /opt/docsrs/prefix/public_html COPY templates /opt/docsrs/templates +COPY tera-templates /opt/docsrs/tera-templates COPY dockerfiles/entrypoint.sh /opt/docsrs/ WORKDIR /opt/docsrs diff --git a/src/web/mod.rs b/src/web/mod.rs index 02e0ba78b..7a949b471 100644 --- a/src/web/mod.rs +++ b/src/web/mod.rs @@ -78,6 +78,7 @@ use crate::{config::Config, db::Pool, impl_webpage, BuildQueue}; use chrono::{DateTime, Utc}; use extensions::InjectExtensions; use failure::Error; +use handlebars_iron::{DirectorySource, HandlebarsEngine, SourceError}; use iron::{ self, headers::{CacheControl, CacheDirective, ContentType, Expires, HttpDate}, @@ -103,6 +104,17 @@ 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, @@ -113,9 +125,11 @@ struct CratesfyiHandler { impl CratesfyiHandler { fn chain(inject_extensions: InjectExtensions, base: H) -> Chain { + let hbse = handlebars_engine().expect("Failed to load handlebar templates"); + let mut chain = Chain::new(base); chain.link_before(inject_extensions); - + chain.link_after(hbse); chain } @@ -570,7 +584,7 @@ fn ico_handler(req: &mut Request) -> IronResult { } /// MetaData used in header -#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)] pub(crate) struct MetaData { pub(crate) name: String, pub(crate) version: String, @@ -638,7 +652,10 @@ impl_webpage! { #[cfg(test)] mod test { use super::*; - use crate::{test::*, web::match_version}; + use crate::{ + test::*, + web::{handlebars_engine, match_version}, + }; use kuchiki::traits::TendrilSink; use serde_json::json; @@ -847,6 +864,11 @@ 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/handlebars.rs b/src/web/page/handlebars.rs new file mode 100644 index 000000000..7975fd9f5 --- /dev/null +++ b/src/web/page/handlebars.rs @@ -0,0 +1,190 @@ +//! Generic page struct + +use handlebars_iron::Template; +use iron::response::Response; +use iron::{status, IronResult, Set}; +use once_cell::sync::Lazy; +use serde::{ + ser::{SerializeStruct, Serializer}, + Serialize, +}; +use serde_json::Value; +use std::collections::BTreeMap; + +static RUSTC_RESOURCE_SUFFIX: Lazy = + Lazy::new(|| load_rustc_resource_suffix().unwrap_or_else(|_| "???".into())); + +fn load_rustc_resource_suffix() -> Result { + // New instances of the configuration or the connection pool shouldn't be created inside the + // application, but we're removing handlebars so this is not going to be a problem in the long + // term. To avoid wasting resources, the pool is hardcoded to only keep one connection alive. + let mut config = crate::Config::from_env()?; + config.max_pool_size = 1; + config.min_pool_idle = 1; + let pool = crate::db::Pool::new(&config)?; + let conn = pool.get()?; + + 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, 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 title of page + pub fn title(mut self, title: &str) -> Page { + self.title = Some(title.to_owned()); + 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::*; + use crate::web::releases; + use iron::Url; + + #[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 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 index b8a42458e..c8c1ad175 100644 --- a/src/web/page/mod.rs +++ b/src/web/page/mod.rs @@ -1,6 +1,8 @@ +mod handlebars; mod templates; mod web_page; +pub use handlebars::*; pub(crate) use templates::TemplateData; pub(crate) use web_page::WebPage; diff --git a/src/web/page/templates.rs b/src/web/page/templates.rs index 8d02af143..801de6c29 100644 --- a/src/web/page/templates.rs +++ b/src/web/page/templates.rs @@ -16,7 +16,7 @@ use std::{ use tera::{Result as TeraResult, Tera}; use walkdir::WalkDir; -const TEMPLATES_DIRECTORY: &str = "templates"; +const TEMPLATES_DIRECTORY: &str = "tera-templates"; /// Holds all data relevant to templating #[derive(Debug)] @@ -45,7 +45,7 @@ impl TemplateData { let mut watcher = watcher(tx, Duration::from_secs(2)).unwrap(); watcher - .watch(TEMPLATES_DIRECTORY, RecursiveMode::Recursive) + .watch("tera-templates", RecursiveMode::Recursive) .unwrap(); thread::spawn(move || { diff --git a/templates/footer.hbs b/templates/footer.hbs new file mode 100644 index 000000000..17ca3265c --- /dev/null +++ b/templates/footer.hbs @@ -0,0 +1,5 @@ +{{#if varsb.javascript_highlightjs}}{{/if}} + + + + diff --git a/templates/header.hbs b/templates/header.hbs new file mode 100644 index 000000000..d1a001cfd --- /dev/null +++ b/templates/header.hbs @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + {{#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/navigation.hbs b/templates/navigation.hbs new file mode 100644 index 000000000..eefaa13d8 --- /dev/null +++ b/templates/navigation.hbs @@ -0,0 +1,93 @@ + + + {{#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 new file mode 100644 index 000000000..428bb02c3 --- /dev/null +++ b/templates/navigation_global_alert.hbs @@ -0,0 +1,9 @@ +{{#if ../has_global_alert}} +
  • + + + {{../global_alert.text}} + +
  • +{{/if}} + diff --git a/templates/base.html b/tera-templates/base.html similarity index 100% rename from templates/base.html rename to tera-templates/base.html diff --git a/templates/core/Cargo.toml.example b/tera-templates/core/Cargo.toml.example similarity index 100% rename from templates/core/Cargo.toml.example rename to tera-templates/core/Cargo.toml.example diff --git a/templates/core/about.html b/tera-templates/core/about.html similarity index 100% rename from templates/core/about.html rename to tera-templates/core/about.html diff --git a/templates/core/home.html b/tera-templates/core/home.html similarity index 100% rename from templates/core/home.html rename to tera-templates/core/home.html diff --git a/templates/core/sitemap.xml b/tera-templates/core/sitemap.xml similarity index 100% rename from templates/core/sitemap.xml rename to tera-templates/core/sitemap.xml diff --git a/templates/crate/builds.html b/tera-templates/crate/builds.html similarity index 100% rename from templates/crate/builds.html rename to tera-templates/crate/builds.html diff --git a/templates/crate/details.html b/tera-templates/crate/details.html similarity index 100% rename from templates/crate/details.html rename to tera-templates/crate/details.html diff --git a/templates/crate/source.html b/tera-templates/crate/source.html similarity index 100% rename from templates/crate/source.html rename to tera-templates/crate/source.html diff --git a/templates/error.html b/tera-templates/error.html similarity index 100% rename from templates/error.html rename to tera-templates/error.html diff --git a/templates/header/global_alert.html b/tera-templates/header/global_alert.html similarity index 100% rename from templates/header/global_alert.html rename to tera-templates/header/global_alert.html diff --git a/templates/header/package_navigation.html b/tera-templates/header/package_navigation.html similarity index 100% rename from templates/header/package_navigation.html rename to tera-templates/header/package_navigation.html diff --git a/templates/header/topbar.html b/tera-templates/header/topbar.html similarity index 100% rename from templates/header/topbar.html rename to tera-templates/header/topbar.html diff --git a/templates/macros.html b/tera-templates/macros.html similarity index 100% rename from templates/macros.html rename to tera-templates/macros.html diff --git a/templates/releases/activity.html b/tera-templates/releases/activity.html similarity index 100% rename from templates/releases/activity.html rename to tera-templates/releases/activity.html diff --git a/templates/releases/build_queue.html b/tera-templates/releases/build_queue.html similarity index 100% rename from templates/releases/build_queue.html rename to tera-templates/releases/build_queue.html diff --git a/templates/releases/feed.xml b/tera-templates/releases/feed.xml similarity index 100% rename from templates/releases/feed.xml rename to tera-templates/releases/feed.xml diff --git a/templates/releases/header.html b/tera-templates/releases/header.html similarity index 100% rename from templates/releases/header.html rename to tera-templates/releases/header.html diff --git a/templates/releases/releases.html b/tera-templates/releases/releases.html similarity index 100% rename from templates/releases/releases.html rename to tera-templates/releases/releases.html diff --git a/templates/rustdoc/navigation.html b/tera-templates/rustdoc/navigation.html similarity index 100% rename from templates/rustdoc/navigation.html rename to tera-templates/rustdoc/navigation.html diff --git a/templates/rustdoc/page.html b/tera-templates/rustdoc/page.html similarity index 100% rename from templates/rustdoc/page.html rename to tera-templates/rustdoc/page.html