diff --git a/CHANGELOG.md b/CHANGELOG.md index e6fc5fa2..db069ff8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## 0.13.0 (unreleased) + +- Enable HTML minification +- Support `output_dir` in `config.toml` +- Allow sections to be drafted +- Allow specifying default language in filenames +- Render emoji in Markdown content if the `render_emoji` option is enabled +- Enable YouTube privacy mode for the YouTube shortcode +- Add language as class to the `` block +- Add bibtex to `load_data` +- Add a `[markdown]` section to `config.toml` to configure rendering +- Add `highlight_code` and `highlight_theme` to a `[markdown]` section in `config.toml` +- Add `external_links_target_blank`, `external_links_no_follow` and `external_links_no_referrer` +- Add a `smart_punctuation` option in the `[markdown]` section in `config.toml` to turn elements like dots and dashes +into their typographic forms +- Add iteration count variable `nth` for shortcodes to know how many times a shortcode has been invoked in a given +content + ## 0.12.2 (2020-09-28) - Fix `zola serve` being broken on reload diff --git a/Cargo.lock b/Cargo.lock index 79976fb5..d9b80605 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,9 +20,9 @@ checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" [[package]] name = "aho-corasick" -version = "0.7.13" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" dependencies = [ "memchr", ] @@ -51,6 +51,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "assert-json-diff" version = "1.1.0" @@ -85,6 +91,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + [[package]] name = "bincode" version = "1.3.1" @@ -133,9 +145,9 @@ dependencies = [ [[package]] name = "bstr" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931" +checksum = "473fc6b38233f9af7baa94fb5852dca389e3d95b8e21c8e3719301462c5d9faf" dependencies = [ "lazy_static", "memchr", @@ -155,6 +167,18 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +[[package]] +name = "bytecount" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f861d9ce359f56dbcb6e0c2a1cb84e52ad732cadb57b806adeb3c7668caccbd8" + +[[package]] +name = "bytecount" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" + [[package]] name = "bytemuck" version = "1.4.1" @@ -185,9 +209,9 @@ checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" [[package]] name = "cc" -version = "1.0.60" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef611cc68ff783f18535d77ddd080185275713d852c4f5cbb6122c462a7a825c" +checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" [[package]] name = "cedarwood" @@ -205,10 +229,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] -name = "chrono" -version = "0.4.18" +name = "cfg-if" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d021fddb7bd3e734370acfa4a83f34095571d8570c039f1420d77540f68d5772" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" dependencies = [ "libc", "num-integer", @@ -245,9 +275,9 @@ dependencies = [ [[package]] name = "color_quant" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] name = "colored" @@ -275,6 +305,22 @@ dependencies = [ "utils", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211" +dependencies = [ + "cfg-if 0.1.10", + "wasm-bindgen", +] + +[[package]] +name = "const_fn" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826" + [[package]] name = "cpuid-bool" version = "0.1.2" @@ -283,65 +329,64 @@ checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" [[package]] name = "crc32fast" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] name = "crossbeam-channel" -version = "0.4.4" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" +checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" dependencies = [ + "cfg-if 1.0.0", "crossbeam-utils", - "maybe-uninit", ] [[package]] name = "crossbeam-deque" -version = "0.7.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" +checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" dependencies = [ + "cfg-if 1.0.0", "crossbeam-epoch", "crossbeam-utils", - "maybe-uninit", ] [[package]] name = "crossbeam-epoch" -version = "0.8.2" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d" dependencies = [ - "autocfg", - "cfg-if", + "cfg-if 1.0.0", + "const_fn", "crossbeam-utils", "lazy_static", - "maybe-uninit", "memoffset", "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.7.2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" dependencies = [ "autocfg", - "cfg-if", + "cfg-if 1.0.0", "lazy_static", ] [[package]] name = "csv" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279" +checksum = "f9d58633299b24b515ac72a3f869f8b91306a3cec616a602843a383acd6f9e97" dependencies = [ "bstr", "csv-core", @@ -361,9 +406,9 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.1.6" +version = "3.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0b676fa23f995faf587496dcd1c80fead847ed58d2da52ac1caca9a72790dd2" +checksum = "b57a92e9749e10f25a171adcebfafe72991d45e7ec2dcb853e8f83d9dafaeb08" dependencies = [ "nix", "winapi 0.3.9", @@ -511,11 +556,11 @@ checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" [[package]] name = "encoding_rs" -version = "0.8.24" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a51b8cf747471cb9499b6d59e59b0444f4c90eba8968c4e44874e92b5b64ace2" +checksum = "801bbab217d7f79c0062f4f7205b5d4427c6d1a7bd7aafdd1475f7c59d62b283" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -548,11 +593,11 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "filetime" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed85775dcc68644b5c950ac06a2b23768d3bc9390464151aaf27136998dcf9e" +checksum = "0c122a393ea57648015bf06fbd3d372378992e86b9ff5a7a497b076a28c79efe" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "redox_syscall", "winapi 0.3.9", @@ -560,14 +605,14 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "766d0e77a2c1502169d4a93ff3b8c15a71fd946cd0126309752104e5f3c46d94" +checksum = "7411863d55df97a419aa64cb4d2f167103ea9d767e2c54a1868b7ac3f6b47129" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "crc32fast", "libc", - "miniz_oxide 0.4.2", + "miniz_oxide 0.4.3", ] [[package]] @@ -576,6 +621,16 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "form_urlencoded" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" +dependencies = [ + "matches", + "percent-encoding", +] + [[package]] name = "front_matter" version = "0.1.0" @@ -586,7 +641,9 @@ dependencies = [ "regex", "serde", "serde_derive", + "serde_yaml", "tera", + "test-case", "toml", "utils", ] @@ -638,30 +695,30 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.5" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" +checksum = "4b7109687aa4e177ef6fe84553af6280ef2778bdb7783ba44c9dc3399110fe64" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.5" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" +checksum = "847ce131b72ffb13b6109a221da9ad97a64cbe48feb1028356b836b47b8f1748" [[package]] name = "futures-io" -version = "0.3.5" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" +checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb" [[package]] name = "futures-macro" -version = "0.3.5" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" +checksum = "77408a692f1f97bcc61dc001d752e00643408fbc922e4d634c655df50d595556" dependencies = [ "proc-macro-hack", "proc-macro2", @@ -671,31 +728,31 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.5" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" +checksum = "f878195a49cee50e006b02b93cf7e0a95a38ac7b776b4c4d9cc1207cd20fcb3d" [[package]] name = "futures-task" -version = "0.3.5" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" +checksum = "7c554eb5bf48b2426c4771ab68c6b14468b6e76cc90996f528c3338d761a4d0d" dependencies = [ "once_cell", ] [[package]] name = "futures-util" -version = "0.3.5" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" +checksum = "d304cff4a7b99cfb7986f7d43fbe93d175e72e704a8860787cc95e9ffd85cbd2" dependencies = [ "futures-core", "futures-io", "futures-macro", "futures-task", "memchr", - "pin-project", + "pin-project 1.0.2", "pin-utils", "proc-macro-hack", "proc-macro-nested", @@ -727,11 +784,21 @@ version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] +[[package]] +name = "gh-emoji" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17a050b7eb420553344e1cf1db648e8b584c79e98b74e6e6d119eeedd9ddcbc" +dependencies = [ + "phf", + "regex", +] + [[package]] name = "gif" version = "0.11.1" @@ -750,9 +817,9 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "globset" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ad1da430bd7281dde2576f44c84cc3f0f7b475e7202cd503042dff01a8c8120" +checksum = "c152169ef1e421390738366d2f796655fec62621dabbd0fd476f905934061e4a" dependencies = [ "aho-corasick", "bstr", @@ -763,9 +830,9 @@ dependencies = [ [[package]] name = "globwalk" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "178270263374052c40502e9f607134947de75302c1348d1a0e31db67c1691446" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" dependencies = [ "bitflags", "ignore", @@ -774,9 +841,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993f9e0baeed60001cf565546b0d3dbe6a6ad23f2bd31644a133c641eccf6d53" +checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535" dependencies = [ "bytes 0.5.6", "fnv", @@ -789,6 +856,7 @@ dependencies = [ "tokio", "tokio-util", "tracing", + "tracing-futures", ] [[package]] @@ -803,9 +871,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00d63df3d41950fb462ed38308eea019113ad1508da725bbedcd0fa5a85ef5f7" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" [[package]] name = "heck" @@ -818,9 +886,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151" +checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" dependencies = [ "libc", ] @@ -880,9 +948,9 @@ checksum = "b6cab2627acfc432780848602f3f558f7e9dd427352224b0d9324025796d2a5e" [[package]] name = "hyper" -version = "0.13.8" +version = "0.13.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f3afcfae8af5ad0576a31e768415edb627824129e8e5a29b8bfccb2f234e835" +checksum = "f6ad767baac13b44d4529fcf58ba2cd0995e36e7b435bc5b039de6f47e880dbf" dependencies = [ "bytes 0.5.6", "futures-channel", @@ -894,7 +962,7 @@ dependencies = [ "httparse", "httpdate", "itoa", - "pin-project", + "pin-project 1.0.2", "socket2", "tokio", "tower-service", @@ -948,9 +1016,9 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.16" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22dcbf2a4a289528dbef21686354904e1c694ac642610a9bff9e7df730d9ec72" +checksum = "b287fb45c60bb826a0dc68ff08742b9d88a2fea13d6e0c286b3172065aaf878c" dependencies = [ "crossbeam-utils", "globset", @@ -966,12 +1034,13 @@ dependencies = [ [[package]] name = "image" -version = "0.23.10" +version = "0.23.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "985fc06b1304d19c28d5c562ed78ef5316183f2b0053b46763a0b94862373c34" +checksum = "7ce04077ead78e39ae8610ad26216aed811996b043d47beed5090db674f9e9b5" dependencies = [ "bytemuck", "byteorder", + "color_quant", "gif", "jpeg-decoder", "num-iter", @@ -1002,7 +1071,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" dependencies = [ "autocfg", - "hashbrown 0.9.0", + "hashbrown 0.9.1", ] [[package]] @@ -1018,9 +1087,9 @@ dependencies = [ [[package]] name = "inotify-sys" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0" +checksum = "c4563555856585ab3180a5bf0b2f9f8d301a728462afffc8195b3f5394229c55" dependencies = [ "libc", ] @@ -1072,9 +1141,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.45" +version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca059e81d9486668f12d455a4ea6daa600bd408134cd17e3d3fb5a32d1f016f8" +checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175" dependencies = [ "wasm-bindgen", ] @@ -1108,10 +1177,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73a004f877f468548d8d0ac4977456a249d8fabbdb8416c36db163dfc8f2e8ca" [[package]] -name = "libc" -version = "0.2.77" +name = "lexical-core" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" +checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if 0.1.10", + "ryu", + "static_assertions", +] + +[[package]] +name = "libc" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" [[package]] name = "library" @@ -1231,15 +1313,9 @@ version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", ] -[[package]] -name = "lzw" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" - [[package]] name = "mac" version = "0.1.1" @@ -1287,23 +1363,17 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - [[package]] name = "memchr" -version = "2.3.3" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] name = "memoffset" -version = "0.5.6" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" +checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" dependencies = [ "autocfg", ] @@ -1326,9 +1396,9 @@ dependencies = [ [[package]] name = "minify-html" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30f114c59124a0424fff930aecd3c3b991fc9a775bf656ecd9b659975d1b9658" +checksum = "89ac7185726f6e49b3a692680c54aeee49027351696a2db1bcebafdc33d077e3" dependencies = [ "aho-corasick", "lazy_static", @@ -1346,9 +1416,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c60c0dfe32c10b43a144bad8fc83538c52f58302c92300ea7ec7bf7b38d5a7b9" +checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" dependencies = [ "adler", "autocfg", @@ -1356,11 +1426,11 @@ dependencies = [ [[package]] name = "mio" -version = "0.6.22" +version = "0.6.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "fuchsia-zircon", "fuchsia-zircon-sys", "iovec", @@ -1387,9 +1457,9 @@ dependencies = [ [[package]] name = "miow" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" dependencies = [ "kernel32-sys", "net2", @@ -1399,9 +1469,9 @@ dependencies = [ [[package]] name = "mockito" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a634720d366bcbce30fb05871a35da229cef101ad0b2ea4e46cf5abf031a273" +checksum = "36a0eb7e686b49b02c1cb87c14b8e2a05de0d36c6eee0293653d0a875906d499" dependencies = [ "assert-json-diff", "colored", @@ -1412,16 +1482,16 @@ dependencies = [ "rand", "regex", "serde_json", - "serde_urlencoded", + "serde_urlencoded 0.6.1", ] [[package]] name = "net2" -version = "0.2.35" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ebc3ec692ed7c9a255596c67808dee269f64655d8baf7b4f0638e51ba1d6853" +checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "libc", "winapi 0.3.9", ] @@ -1434,15 +1504,81 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" [[package]] name = "nix" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363" +checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055" dependencies = [ "bitflags", "cc", - "cfg-if", + "cfg-if 0.1.10", "libc", - "void", +] + +[[package]] +name = "nom" +version = "5.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +dependencies = [ + "lexical-core", + "memchr", + "version_check", +] + +[[package]] +name = "nom-bibtex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9db257f6c7b9c8b3ab67ee6a4b23a290c157d183fef2ac065bf9fce5f1c1299" +dependencies = [ + "nom", + "nom-tracable", + "nom_locate 2.1.0", + "quick-error", +] + +[[package]] +name = "nom-tracable" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e012c742e1269f801f6bfe0d1ebf99d7a3f7bc1d65c970bab0e7bee439e31610" +dependencies = [ + "nom", + "nom-tracable-macros", + "nom_locate 1.0.0", + "nom_locate 2.1.0", +] + +[[package]] +name = "nom-tracable-macros" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65ad630ff46d4c61da89042f327e6fdf104a6ebb667565727ef0bb294a7c3197" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "nom_locate" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f932834fd8e391fc7710e2ba17e8f9f8645d846b55aa63207e17e110a1e1ce35" +dependencies = [ + "bytecount 0.3.2", + "memchr", + "nom", +] + +[[package]] +name = "nom_locate" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a67484adf5711f94f2f28b653bf231bff8e438be33bf5b0f35935a0db4f618a2" +dependencies = [ + "bytecount 0.6.2", + "memchr", + "nom", ] [[package]] @@ -1465,9 +1601,9 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" dependencies = [ "autocfg", "num-traits", @@ -1475,9 +1611,9 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.41" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6e6b7c748f995c4c29c5f5ae0248536e04a5739927c74ec0fa564805094b9f" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" dependencies = [ "autocfg", "num-integer", @@ -1486,9 +1622,9 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5b4d7360f362cfb50dde8143501e6940b22f644be75a4cc90b2d81968908138" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" dependencies = [ "autocfg", "num-integer", @@ -1497,9 +1633,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ "autocfg", ] @@ -1516,15 +1652,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.4.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" +checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" [[package]] name = "onig" -version = "6.1.0" +version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a155d13862da85473665694f4c05d77fb96598bdceeaf696433c84ea9567e20" +checksum = "30b46fd9edbc018f0be4e366c24c46db44fac49cd01c039ae85308088b089dd5" dependencies = [ "bitflags", "lazy_static", @@ -1534,9 +1670,9 @@ dependencies = [ [[package]] name = "onig_sys" -version = "69.5.1" +version = "69.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bff06597a6b17855040955cae613af000fc0bfc8ad49ea68b9479a74e59292d" +checksum = "ed063c96cf4c0f2e5d09324409d158b38a0a85a7b90fbd68c8cad75c495d5775" dependencies = [ "cc", "pkg-config", @@ -1661,18 +1797,38 @@ dependencies = [ [[package]] name = "pin-project" -version = "0.4.24" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f48fad7cfbff853437be7cf54d7b993af21f53be7f0988cbfe4a51535aa77205" +checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15" dependencies = [ - "pin-project-internal", + "pin-project-internal 0.4.27", +] + +[[package]] +name = "pin-project" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ccc2237c2c489783abd8c4c80e5450fc0e98644555b1364da68cc29aa151ca7" +dependencies = [ + "pin-project-internal 1.0.2", ] [[package]] name = "pin-project-internal" -version = "0.4.24" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24c6d293bdd3ca5a1697997854c6cf7855e43fb6a0ba1c47af57a5bcafd158ae" +checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8e8d2bf0b23038a4424865103a4df472855692821aab4e4f5c3312d461d9e5f" dependencies = [ "proc-macro2", "quote", @@ -1681,9 +1837,15 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.1.8" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71f349a4f0e70676ffb2dbafe16d0c992382d02f0a952e3ddf584fc289dac6b3" +checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" + +[[package]] +name = "pin-project-lite" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c" [[package]] name = "pin-utils" @@ -1693,9 +1855,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" [[package]] name = "plist" @@ -1703,7 +1865,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b336d94e8e4ce29bf15bba393164629764744c567e8ad306cc1fdd0119967fd" dependencies = [ - "base64", + "base64 0.12.3", "chrono", "indexmap", "line-wrap", @@ -1713,9 +1875,9 @@ dependencies = [ [[package]] name = "png" -version = "0.16.7" +version = "0.16.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfe7f9f1c730833200b134370e1d5098964231af8450bce9b78ee3ab5278b970" +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" dependencies = [ "bitflags", "crc32fast", @@ -1725,9 +1887,9 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[package]] name = "precomputed-hash" @@ -1761,9 +1923,9 @@ dependencies = [ [[package]] name = "proc-macro-hack" -version = "0.5.18" +version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro-nested" @@ -1773,9 +1935,9 @@ checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" [[package]] name = "proc-macro2" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51ef7cd2518ead700af67bf9d1a658d90b6037d77110fd9c0445429d0ba1c6c9" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ "unicode-xid", ] @@ -1791,6 +1953,12 @@ dependencies = [ "unicase", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quote" version = "1.0.7" @@ -1853,9 +2021,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfd016f0c045ad38b5251be2c9c0ab806917f82da4d36b2a327e5166adad9270" +checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" dependencies = [ "autocfg", "crossbeam-deque", @@ -1865,9 +2033,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c4fec834fb6e6d2dd5eece3c7b432a52f0ba887cf40e595190c4107edc08bf" +checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -1884,13 +2052,13 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "regex" -version = "1.3.9" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" +checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.6.18", + "regex-syntax 0.6.21", "thread_local", ] @@ -1911,9 +2079,9 @@ checksum = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e" [[package]] name = "regex-syntax" -version = "0.6.18" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" +checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" [[package]] name = "relative-path" @@ -1937,6 +2105,7 @@ dependencies = [ "config", "errors", "front_matter", + "gh-emoji", "lazy_static", "link_checker", "pest", @@ -1953,11 +2122,11 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9eaa17ac5d7b838b7503d118fa16ad88f440498bf9ffe5424e621f93190d61e" +checksum = "fb15d6255c792356a0f578d8a645c677904dc02e862bebe2ecc18e0c01b9a0ce" dependencies = [ - "base64", + "base64 0.13.0", "bytes 0.5.6", "encoding_rs", "futures-core", @@ -1973,15 +2142,16 @@ dependencies = [ "mime", "mime_guess", "percent-encoding", - "pin-project-lite", + "pin-project-lite 0.2.0", "rustls", "serde", - "serde_urlencoded", + "serde_urlencoded 0.7.0", "tokio", "tokio-rustls", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-bindgen-test", "web-sys", "webpki-roots", "winreg", @@ -1989,9 +2159,9 @@ dependencies = [ [[package]] name = "ring" -version = "0.16.15" +version = "0.16.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "952cd6b98c85bbc30efa1ba5783b8abf12fec8b3287ffa52605b9432313e34e4" +checksum = "024a1e66fea74c66c66624ee5622a7ff0e4b73a13b4f5c326ddb50c708944226" dependencies = [ "cc", "libc", @@ -2027,7 +2197,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81" dependencies = [ - "base64", + "base64 0.12.3", "log", "ring", "sct", @@ -2077,6 +2247,12 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + [[package]] name = "scoped_threadpool" version = "0.1.9" @@ -2113,18 +2289,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.116" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5" +checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.116" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8" +checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df" dependencies = [ "proc-macro2", "quote", @@ -2133,9 +2309,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.57" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" +checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779" dependencies = [ "indexmap", "itoa", @@ -2155,6 +2331,30 @@ dependencies = [ "url", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7baae0a99f1a324984bcdc5f0718384c1f69775f1c7eec8b859b71b443e3fd7" +dependencies = [ + "dtoa", + "linked-hash-map", + "serde", + "yaml-rust", +] + [[package]] name = "sha-1" version = "0.8.2" @@ -2169,12 +2369,12 @@ dependencies = [ [[package]] name = "sha2" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2933378ddfeda7ea26f48c555bdad8bb446bf8a3d17832dc83e380d444cfb8c1" +checksum = "6e7aab86fe2149bad8c507606bdb3f4ef5e7b2380eb92350f56122cca72a42a8" dependencies = [ "block-buffer 0.9.0", - "cfg-if", + "cfg-if 1.0.0", "cpuid-bool", "digest 0.9.0", "opaque-debug 0.3.0", @@ -2205,10 +2405,12 @@ dependencies = [ "search", "serde", "serde_derive", + "slotmap", "tempfile", "templates", "tera", "utils", + "walkdir", ] [[package]] @@ -2234,17 +2436,17 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.4.2" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" +checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75" [[package]] name = "socket2" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44" +checksum = "2c29947abdee2a218277abeca306f25789c938e500ea5a9d4b12a5a504466902" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "redox_syscall", "winapi 0.3.9", @@ -2257,10 +2459,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] -name = "string_cache" -version = "0.8.0" +name = "static_assertions" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2940c75beb4e3bf3a494cef919a747a2cb81e52571e212bfbd185074add7208a" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "string_cache" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ddb1139b5353f96e429e1a5e19fbaf663bddedaa06d1dbd49f82e352601209a" dependencies = [ "lazy_static", "new_debug_unreachable", @@ -2319,9 +2527,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.42" +version = "1.0.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c51d92969d209b54a98397e1b91c8ae82d8c87a7bb87df0b29aa2ad81454228" +checksum = "9a2af957a63d6bd42255c359c93d9bfdb97076bd3b820897ce55ffbfbf107f44" dependencies = [ "proc-macro2", "quote", @@ -2330,9 +2538,9 @@ dependencies = [ [[package]] name = "syntect" -version = "4.4.0" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3978df05b5850c839a6b352d3c35ce0478944a4be689be826b53cf75363e88" +checksum = "2bfac2b23b4d049dc9a89353b4e06bbc85a8f42020cccbe5409a115cf19031e5" dependencies = [ "bincode", "bitflags", @@ -2342,7 +2550,7 @@ dependencies = [ "lazycell", "onig", "plist", - "regex-syntax 0.6.18", + "regex-syntax 0.6.21", "serde", "serde_derive", "serde_json", @@ -2356,7 +2564,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "libc", "rand", "redox_syscall", @@ -2368,7 +2576,7 @@ dependencies = [ name = "templates" version = "0.1.0" dependencies = [ - "base64", + "base64 0.13.0", "config", "csv", "errors", @@ -2377,7 +2585,8 @@ dependencies = [ "lazy_static", "library", "mockito", - "pulldown-cmark", + "nom-bibtex", + "rendering", "reqwest", "serde_json", "sha2", @@ -2423,13 +2632,25 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" dependencies = [ "winapi-util", ] +[[package]] +name = "test-case" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "199464148b42bcf3da8b2a56f6ee87ca68f47402496d1268849291ec9fb463c8" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "version_check", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -2450,13 +2671,13 @@ dependencies = [ [[package]] name = "tiff" -version = "0.5.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f3b8a87c4da944c3f27e5943289171ac71a6150a79ff6bacfff06d159dfff2f" +checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437" dependencies = [ - "byteorder", - "lzw", - "miniz_oxide 0.3.7", + "jpeg-decoder", + "miniz_oxide 0.4.3", + "weezl", ] [[package]] @@ -2472,15 +2693,24 @@ dependencies = [ [[package]] name = "tinyvec" -version = "0.3.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117" +checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d34ca54d84bf2b5b4d7d31e901a8464f7b60ac145a284fba25ceb801f2ddccd" +checksum = "099837d3464c16a808060bb3f02263b412f6fafcb5d01c533d309985fbeebe48" dependencies = [ "bytes 0.5.6", "fnv", @@ -2490,16 +2720,16 @@ dependencies = [ "memchr", "mio", "num_cpus", - "pin-project-lite", + "pin-project-lite 0.1.11", "slab", "tokio-macros", ] [[package]] name = "tokio-macros" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" +checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" dependencies = [ "proc-macro2", "quote", @@ -2528,15 +2758,15 @@ dependencies = [ "futures-core", "futures-sink", "log", - "pin-project-lite", + "pin-project-lite 0.1.11", "tokio", ] [[package]] name = "toml" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" +checksum = "75cf45bb0bef80604d001caaec0d09da99611b3c0fd39d3080468875cdb65645" dependencies = [ "serde", ] @@ -2549,24 +2779,35 @@ checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" [[package]] name = "tracing" -version = "0.1.19" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d79ca061b032d6ce30c660fded31189ca0b9922bf483cd70759f13a2d86786c" +checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "log", + "pin-project-lite 0.2.0", "tracing-core", ] [[package]] name = "tracing-core" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bcf46c1f1f06aeea2d6b81f3c863d0930a596c86ad1920d4e5bad6dd1d7119a" +checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" dependencies = [ "lazy_static", ] +[[package]] +name = "tracing-futures" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c" +dependencies = [ + "pin-project 0.4.27", + "tracing", +] + [[package]] name = "try-lock" version = "0.2.3" @@ -2655,18 +2896,18 @@ dependencies = [ [[package]] name = "unicode-normalization" -version = "0.1.13" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" +checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" +checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" [[package]] name = "unicode-width" @@ -2688,10 +2929,11 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" +checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" dependencies = [ + "form_urlencoded", "idna", "matches", "percent-encoding", @@ -2738,12 +2980,6 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - [[package]] name = "walkdir" version = "2.3.1" @@ -2779,11 +3015,11 @@ checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" [[package]] name = "wasm-bindgen" -version = "0.2.68" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42" +checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "serde", "serde_json", "wasm-bindgen-macro", @@ -2791,9 +3027,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.68" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f22b422e2a757c35a73774860af8e112bff612ce6cb604224e8e47641a9e4f68" +checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62" dependencies = [ "bumpalo", "lazy_static", @@ -2806,11 +3042,11 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.18" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7866cab0aa01de1edf8b5d7936938a7e397ee50ce24119aef3e1eaa3b6171da" +checksum = "1fe9756085a84584ee9457a002b7cdfe0bfff169f45d2591d8be1345a6780e35" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "js-sys", "wasm-bindgen", "web-sys", @@ -2818,9 +3054,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.68" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b13312a745c08c469f0b292dd2fcd6411dba5f7160f593da6ef69b64e407038" +checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2828,9 +3064,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.68" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f249f06ef7ee334cc3b8ff031bfc11ec99d00f34d86da7498396dc1e3b1498fe" +checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" dependencies = [ "proc-macro2", "quote", @@ -2841,15 +3077,39 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.68" +version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307" +checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158" + +[[package]] +name = "wasm-bindgen-test" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0355fa0c1f9b792a09b6dcb6a8be24d51e71e6d74972f9eb4a44c4c004d24a25" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27e07b46b98024c2ba2f9e83a10c2ef0515f057f2da299c1762a2017de80438b" +dependencies = [ + "proc-macro2", + "quote", +] [[package]] name = "web-sys" -version = "0.3.45" +version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bf6ef87ad7ae8008e15a355ce696bed26012b7caa21605188cfd8214ab51e2d" +checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3" dependencies = [ "js-sys", "wasm-bindgen", @@ -2857,9 +3117,9 @@ dependencies = [ [[package]] name = "webpki" -version = "0.21.3" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab146130f5f790d45f82aeeb09e55a256573373ec64409fc19a6fb82fb1032ae" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" dependencies = [ "ring", "untrusted", @@ -2867,18 +3127,18 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8eff4b7516a57307f9349c64bf34caa34b940b66fed4b2fb3136cb7386e5739" +checksum = "0f20dea7535251981a9670857150d571846545088359b28e4951d350bdaf179f" dependencies = [ "webpki", ] [[package]] name = "weezl" -version = "0.1.0" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3d2f24b6c3aa92fb33279566dbebf1cbe66b03a73f09aa69cf8cf14d2f9feb9" +checksum = "3e2bb9fc8309084dd7cd651336673844c1d47f8ef6d2091ec160b27f5c4aa277" [[package]] name = "winapi" @@ -2995,7 +3255,7 @@ dependencies = [ [[package]] name = "zola" -version = "0.12.2" +version = "0.13.0" dependencies = [ "atty", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 2f475f63..6fc4a8cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zola" -version = "0.12.2" +version = "0.13.0" authors = ["Vincent Prouillet "] edition = "2018" license = "MIT" diff --git a/components/config/src/config/markup.rs b/components/config/src/config/markup.rs new file mode 100644 index 00000000..035aad7d --- /dev/null +++ b/components/config/src/config/markup.rs @@ -0,0 +1,71 @@ +use serde_derive::{Deserialize, Serialize}; + +pub const DEFAULT_HIGHLIGHT_THEME: &str = "base16-ocean-dark"; + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(default)] +pub struct Markdown { + /// Whether to highlight all code blocks found in markdown files. Defaults to false + pub highlight_code: bool, + /// Which themes to use for code highlighting. See Readme for supported themes + /// Defaults to "base16-ocean-dark" + pub highlight_theme: String, + /// Whether to render emoji aliases (e.g.: :smile: => 😄) in the markdown files + pub render_emoji: bool, + /// Whether external links are to be opened in a new tab + /// If this is true, a `rel="noopener"` will always automatically be added for security reasons + pub external_links_target_blank: bool, + /// Whether to set rel="nofollow" for all external links + pub external_links_no_follow: bool, + /// Whether to set rel="noreferrer" for all external links + pub external_links_no_referrer: bool, + /// Whether smart punctuation is enabled (changing quotes, dashes, dots etc in their typographic form) + pub smart_punctuation: bool, +} + +impl Markdown { + pub fn has_external_link_tweaks(&self) -> bool { + self.external_links_target_blank + || self.external_links_no_follow + || self.external_links_no_referrer + } + + pub fn construct_external_link_tag(&self, url: &str, title: &str) -> String { + let mut rel_opts = Vec::new(); + let mut target = "".to_owned(); + let title = if title == "" { "".to_owned() } else { format!("title=\"{}\" ", title) }; + + if self.external_links_target_blank { + // Security risk otherwise + rel_opts.push("noopener"); + target = "target=\"_blank\" ".to_owned(); + } + if self.external_links_no_follow { + rel_opts.push("nofollow"); + } + if self.external_links_no_referrer { + rel_opts.push("noreferrer"); + } + let rel = if rel_opts.is_empty() { + "".to_owned() + } else { + format!("rel=\"{}\" ", rel_opts.join(" ")) + }; + + format!("", rel, target, title, url) + } +} + +impl Default for Markdown { + fn default() -> Markdown { + Markdown { + highlight_code: false, + highlight_theme: DEFAULT_HIGHLIGHT_THEME.to_owned(), + render_emoji: false, + external_links_target_blank: false, + external_links_no_follow: false, + external_links_no_referrer: false, + smart_punctuation: false, + } + } +} diff --git a/components/config/src/config/mod.rs b/components/config/src/config/mod.rs index a23e3825..fc928ee3 100644 --- a/components/config/src/config/mod.rs +++ b/components/config/src/config/mod.rs @@ -1,5 +1,6 @@ pub mod languages; pub mod link_checker; +pub mod markup; pub mod search; pub mod slugify; pub mod taxonomies; @@ -96,6 +97,8 @@ pub struct Config { #[serde(skip_serializing, skip_deserializing)] // not a typo, 2 are need pub extra_syntax_set: Option, + pub output_dir: String, + pub link_checker: link_checker::LinkChecker, /// The setup for which slugification strategies to use for paths, taxonomies and anchors @@ -104,6 +107,9 @@ pub struct Config { /// The search config, telling what to include in the search index pub search: search::Search, + /// The config for the Markdown rendering: syntax highlighting and everything + pub markdown: markup::Markdown, + /// All user params set in [extra] in the config pub extra: HashMap, } @@ -153,8 +159,9 @@ impl Config { } } - // TODO: re-enable once it's a bit more tested - config.minify_html = false; + if config.highlight_code { + println!("`highlight_code` has been moved to a [markdown] section. Top level `highlight_code` and `highlight_theme` will stop working in 0.14."); + } Ok(config) } @@ -170,6 +177,30 @@ impl Config { Config::parse(&content) } + /// Temporary, while we have the settings in 2 places + /// TODO: remove me in 0.14 + pub fn highlight_code(&self) -> bool { + if !self.highlight_code && !self.markdown.highlight_code { + return false; + } + + if self.highlight_code { + true + } else { + self.markdown.highlight_code + } + } + + /// Temporary, while we have the settings in 2 places + /// TODO: remove me in 0.14 + pub fn highlight_theme(&self) -> &str { + if self.highlight_theme != markup::DEFAULT_HIGHLIGHT_THEME { + &self.highlight_theme + } else { + &self.markdown.highlight_theme + } + } + /// Attempt to load any extra syntax found in the extra syntaxes of the config pub fn load_extra_syntaxes(&mut self, base_path: &Path) -> Result<()> { if self.extra_syntaxes.is_empty() { @@ -333,9 +364,11 @@ impl Default for Config { translations: HashMap::new(), extra_syntaxes: Vec::new(), extra_syntax_set: None, + output_dir: "public".to_string(), link_checker: link_checker::LinkChecker::default(), slugify: slugify::Slugify::default(), search: search::Search::default(), + markdown: markup::Markdown::default(), extra: HashMap::new(), } } @@ -654,4 +687,27 @@ bar = "baz" // We expect an error here assert_eq!(false, config.add_theme_extra(&theme).is_ok()); } + + #[test] + fn default_output_dir() { + let config = r#" +title = "My site" +base_url = "https://replace-this-with-your-url.com" + "#; + + let config = Config::parse(config).unwrap(); + assert_eq!(config.output_dir, "public".to_string()); + } + + #[test] + fn can_add_output_dir() { + let config = r#" +title = "My site" +base_url = "https://replace-this-with-your-url.com" +output_dir = "docs" + "#; + + let config = Config::parse(config).unwrap(); + assert_eq!(config.output_dir, "docs".to_string()); + } } diff --git a/components/front_matter/Cargo.toml b/components/front_matter/Cargo.toml index eb3f8a6f..d368e90c 100644 --- a/components/front_matter/Cargo.toml +++ b/components/front_matter/Cargo.toml @@ -9,9 +9,14 @@ tera = "1" chrono = "0.4" serde = "1" serde_derive = "1" +serde_yaml = "0.8" toml = "0.5" regex = "1" lazy_static = "1" errors = { path = "../errors" } utils = { path = "../utils" } + +[dev-dependencies] +test-case = "1.0" + diff --git a/components/front_matter/src/lib.rs b/components/front_matter/src/lib.rs index f0866797..834747b3 100644 --- a/components/front_matter/src/lib.rs +++ b/components/front_matter/src/lib.rs @@ -3,7 +3,9 @@ use serde_derive::{Deserialize, Serialize}; use errors::{bail, Error, Result}; use regex::Regex; +use serde_yaml; use std::path::Path; +use toml; mod page; mod section; @@ -12,8 +14,31 @@ pub use page::PageFrontMatter; pub use section::SectionFrontMatter; lazy_static! { - static ref PAGE_RE: Regex = + static ref TOML_RE: Regex = Regex::new(r"^[[:space:]]*\+\+\+(\r?\n(?s).*?(?-s))\+\+\+\r?\n?((?s).*(?-s))$").unwrap(); + static ref YAML_RE: Regex = + Regex::new(r"^[[:space:]]*---(\r?\n(?s).*?(?-s))---\r?\n?((?s).*(?-s))$").unwrap(); +} + +pub enum RawFrontMatter<'a> { + Toml(&'a str), + Yaml(&'a str), +} + +impl RawFrontMatter<'_> { + fn deserialize(&self) -> Result + where + T: serde::de::DeserializeOwned, + { + let f: T = match self { + RawFrontMatter::Toml(s) => toml::from_str(s)?, + RawFrontMatter::Yaml(s) => match serde_yaml::from_str(s) { + Ok(d) => d, + Err(e) => bail!(format!("YAML deserialize error: {:?}", e)), + }, + }; + Ok(f) + } } #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] @@ -37,20 +62,30 @@ pub enum InsertAnchor { /// Split a file between the front matter and its content /// Will return an error if the front matter wasn't found -fn split_content<'c>(file_path: &Path, content: &'c str) -> Result<(&'c str, &'c str)> { - if !PAGE_RE.is_match(content) { +fn split_content<'c>(file_path: &Path, content: &'c str) -> Result<(RawFrontMatter<'c>, &'c str)> { + let (re, is_toml) = if TOML_RE.is_match(content) { + (&TOML_RE as &Regex, true) + } else if YAML_RE.is_match(content) { + (&YAML_RE as &Regex, false) + } else { bail!( - "Couldn't find front matter in `{}`. Did you forget to add `+++`?", + "Couldn't find front matter in `{}`. Did you forget to add `+++` or `---`?", file_path.to_string_lossy() ); - } + }; // 2. extract the front matter and the content - let caps = PAGE_RE.captures(content).unwrap(); + let caps = re.captures(content).unwrap(); // caps[0] is the full match // caps[1] => front matter // caps[2] => content - Ok((caps.get(1).unwrap().as_str(), caps.get(2).unwrap().as_str())) + let front_matter = caps.get(1).unwrap().as_str(); + let content = caps.get(2).unwrap().as_str(); + if is_toml { + Ok((RawFrontMatter::Toml(front_matter), content)) + } else { + Ok((RawFrontMatter::Yaml(front_matter), content)) + } } /// Split a file between the front matter and its content. @@ -88,71 +123,125 @@ pub fn split_page_content<'c>( #[cfg(test)] mod tests { use std::path::Path; + use test_case::test_case; use super::{split_page_content, split_section_content}; - #[test] - fn can_split_page_content_valid() { - let content = r#" + #[test_case(r#" +++ title = "Title" description = "hey there" date = 2002-10-12 +++ Hello -"#; +"#; "toml")] + #[test_case(r#" +--- +title: Title +description: hey there +date: 2002-10-12 +--- +Hello +"#; "yaml")] + fn can_split_page_content_valid(content: &str) { let (front_matter, content) = split_page_content(Path::new(""), content).unwrap(); assert_eq!(content, "Hello\n"); assert_eq!(front_matter.title.unwrap(), "Title"); } - #[test] - fn can_split_section_content_valid() { - let content = r#" + #[test_case(r#" +++ paginate_by = 10 +++ Hello -"#; +"#; "toml")] + #[test_case(r#" +--- +paginate_by: 10 +--- +Hello +"#; "yaml")] + fn can_split_section_content_valid(content: &str) { let (front_matter, content) = split_section_content(Path::new(""), content).unwrap(); assert_eq!(content, "Hello\n"); assert!(front_matter.is_paginated()); } - #[test] - fn can_split_content_with_only_frontmatter_valid() { - let content = r#" + #[test_case(r#" +++ title = "Title" description = "hey there" date = 2002-10-12 -+++"#; ++++"#; "toml")] + #[test_case(r#" +--- +title: Title +description: hey there +date: 2002-10-12 +---"#; "yaml")] + fn can_split_content_with_only_frontmatter_valid(content: &str) { let (front_matter, content) = split_page_content(Path::new(""), content).unwrap(); assert_eq!(content, ""); assert_eq!(front_matter.title.unwrap(), "Title"); } - #[test] - fn can_split_content_lazily() { - let content = r#" + #[test_case(r#" +++ title = "Title" description = "hey there" date = 2002-10-02T15:00:00Z +++ -+++"#; - let (front_matter, content) = split_page_content(Path::new(""), content).unwrap(); - assert_eq!(content, "+++"); - assert_eq!(front_matter.title.unwrap(), "Title"); - } - - #[test] - fn errors_if_cannot_locate_frontmatter() { - let content = r#" ++++"#, "+++"; "toml with pluses in content")] + #[test_case(r#" +++ title = "Title" description = "hey there" -date = 2002-10-12"#; +date = 2002-10-02T15:00:00Z ++++ +---"#, "---"; "toml with minuses in content")] + #[test_case(r#" +--- +title: Title +description: hey there +date: 2002-10-02T15:00:00Z +--- ++++"#, "+++"; "yaml with pluses in content")] + #[test_case(r#" +--- +title: Title +description: hey there +date: 2002-10-02T15:00:00Z +--- +---"#, "---"; "yaml with minuses in content")] + fn can_split_content_lazily(content: &str, expected: &str) { + let (front_matter, content) = split_page_content(Path::new(""), content).unwrap(); + assert_eq!(content, expected); + assert_eq!(front_matter.title.unwrap(), "Title"); + } + + #[test_case(r#" ++++ +title = "Title" +description = "hey there" +date = 2002-10-12"#; "toml")] + #[test_case(r#" ++++ +title = "Title" +description = "hey there" +date = 2002-10-12 +---"#; "toml unmatched")] + #[test_case(r#" +--- +title: Title +description: hey there +date: 2002-10-12"#; "yaml")] + #[test_case(r#" +--- +title: Title +description: hey there +date: 2002-10-12 ++++"#; "yaml unmatched")] + fn errors_if_cannot_locate_frontmatter(content: &str) { let res = split_page_content(Path::new(""), content); assert!(res.is_err()); } diff --git a/components/front_matter/src/page.rs b/components/front_matter/src/page.rs index f38f1a2e..da39a5e7 100644 --- a/components/front_matter/src/page.rs +++ b/components/front_matter/src/page.rs @@ -7,6 +7,8 @@ use tera::{Map, Value}; use errors::{bail, Result}; use utils::de::{fix_toml_dates, from_toml_datetime}; +use crate::RawFrontMatter; + /// The front matter of every page #[derive(Debug, Clone, PartialEq, Deserialize)] #[serde(default)] @@ -69,11 +71,8 @@ fn parse_datetime(d: &str) -> Option { } impl PageFrontMatter { - pub fn parse(toml: &str) -> Result { - let mut f: PageFrontMatter = match toml::from_str(toml) { - Ok(d) => d, - Err(e) => bail!(e), - }; + pub fn parse(raw: &RawFrontMatter) -> Result { + let mut f: PageFrontMatter = raw.deserialize()?; if let Some(ref slug) = f.slug { if slug == "" { @@ -140,21 +139,27 @@ impl Default for PageFrontMatter { #[cfg(test)] mod tests { use super::PageFrontMatter; + use super::RawFrontMatter; use tera::to_value; + use test_case::test_case; - #[test] - fn can_have_empty_front_matter() { - let content = r#" "#; + #[test_case(&RawFrontMatter::Toml(r#" "#); "toml")] + #[test_case(&RawFrontMatter::Toml(r#" "#); "yaml")] + fn can_have_empty_front_matter(content: &RawFrontMatter) { let res = PageFrontMatter::parse(content); println!("{:?}", res); assert!(res.is_ok()); } - #[test] - fn can_parse_valid_front_matter() { - let content = r#" - title = "Hello" - description = "hey there""#; + #[test_case(&RawFrontMatter::Toml(r#" +title = "Hello" +description = "hey there" +"#); "toml")] + #[test_case(&RawFrontMatter::Yaml(r#" +title: Hello +description: hey there +"#); "yaml")] + fn can_parse_valid_front_matter(content: &RawFrontMatter) { let res = PageFrontMatter::parse(content); assert!(res.is_ok()); let res = res.unwrap(); @@ -162,183 +167,281 @@ mod tests { assert_eq!(res.description.unwrap(), "hey there".to_string()) } - #[test] - fn errors_with_invalid_front_matter() { - let content = r#"title = 1\n"#; + #[test_case(&RawFrontMatter::Toml(r#"title = |\n"#); "toml")] + #[test_case(&RawFrontMatter::Yaml(r#"title: |\n"#); "yaml")] + fn errors_with_invalid_front_matter(content: &RawFrontMatter) { let res = PageFrontMatter::parse(content); assert!(res.is_err()); } - #[test] - fn errors_on_present_but_empty_slug() { - let content = r#" - title = "Hello" - description = "hey there" - slug = """#; + #[test_case(&RawFrontMatter::Toml(r#" +title = "Hello" +description = "hey there" +slug = "" +"#); "toml")] + #[test_case(&RawFrontMatter::Yaml(r#" +title: Hello +description: hey there +slug: "" +"#); "yaml")] + fn errors_on_present_but_empty_slug(content: &RawFrontMatter) { let res = PageFrontMatter::parse(content); assert!(res.is_err()); } - #[test] - fn errors_on_present_but_empty_path() { - let content = r#" - title = "Hello" - description = "hey there" - path = """#; + #[test_case(&RawFrontMatter::Toml(r#" +title = "Hello" +description = "hey there" +path = "" +"#); "toml")] + #[test_case(&RawFrontMatter::Yaml(r#" +title: Hello +description: hey there +path: "" +"#); "yaml")] + fn errors_on_present_but_empty_path(content: &RawFrontMatter) { let res = PageFrontMatter::parse(content); assert!(res.is_err()); } - #[test] - fn can_parse_date_yyyy_mm_dd() { - let content = r#" - title = "Hello" - description = "hey there" - date = 2016-10-10 - "#; + #[test_case(&RawFrontMatter::Toml(r#" +title = "Hello" +description = "hey there" +date = 2016-10-10 +"#); "toml")] + #[test_case(&RawFrontMatter::Yaml(r#" +title: Hello +description: hey there +date: 2016-10-10 +"#); "yaml")] + fn can_parse_date_yyyy_mm_dd(content: &RawFrontMatter) { let res = PageFrontMatter::parse(content).unwrap(); assert!(res.datetime.is_some()); } - #[test] - fn can_parse_date_rfc3339() { - let content = r#" - title = "Hello" - description = "hey there" - date = 2002-10-02T15:00:00Z - "#; + #[test_case(&RawFrontMatter::Toml(r#" +title = "Hello" +description = "hey there" +date = 2002-10-02T15:00:00Z +"#); "toml")] + #[test_case(&RawFrontMatter::Yaml(r#" +title: Hello +description: hey there +date: 2002-10-02T15:00:00Z +"#); "yaml")] + fn can_parse_date_rfc3339(content: &RawFrontMatter) { let res = PageFrontMatter::parse(content).unwrap(); assert!(res.datetime.is_some()); } - #[test] - fn can_parse_date_rfc3339_without_timezone() { - let content = r#" - title = "Hello" - description = "hey there" - date = 2002-10-02T15:00:00 - "#; + #[test_case(&RawFrontMatter::Toml(r#" +title = "Hello" +description = "hey there" +date = 2002-10-02T15:00:00 +"#); "toml")] + #[test_case(&RawFrontMatter::Yaml(r#" +title: Hello +description: hey there +date: 2002-10-02T15:00:00 +"#); "yaml")] + fn can_parse_date_rfc3339_without_timezone(content: &RawFrontMatter) { let res = PageFrontMatter::parse(content).unwrap(); assert!(res.datetime.is_some()); } - #[test] - fn can_parse_date_rfc3339_with_space() { - let content = r#" - title = "Hello" - description = "hey there" - date = 2002-10-02 15:00:00+02:00 - "#; + #[test_case(&RawFrontMatter::Toml(r#" +title = "Hello" +description = "hey there" +date = 2002-10-02 15:00:00+02:00 +"#); "toml")] + #[test_case(&RawFrontMatter::Yaml(r#" +title: Hello +description: hey there +date: 2002-10-02 15:00:00+02:00 +"#); "yaml")] + fn can_parse_date_rfc3339_with_space(content: &RawFrontMatter) { let res = PageFrontMatter::parse(content).unwrap(); assert!(res.datetime.is_some()); } - #[test] - fn can_parse_date_rfc3339_with_space_without_timezone() { - let content = r#" - title = "Hello" - description = "hey there" - date = 2002-10-02 15:00:00 - "#; + #[test_case(&RawFrontMatter::Toml(r#" +title = "Hello" +description = "hey there" +date = 2002-10-02 15:00:00 +"#); "toml")] + #[test_case(&RawFrontMatter::Yaml(r#" +title: Hello +description: hey there +date: 2002-10-02 15:00:00 +"#); "yaml")] + fn can_parse_date_rfc3339_with_space_without_timezone(content: &RawFrontMatter) { let res = PageFrontMatter::parse(content).unwrap(); assert!(res.datetime.is_some()); } - #[test] - fn can_parse_date_rfc3339_with_microseconds() { - let content = r#" - title = "Hello" - description = "hey there" - date = 2002-10-02T15:00:00.123456Z - "#; + #[test_case(&RawFrontMatter::Toml(r#" +title = "Hello" +description = "hey there" +date = 2002-10-02T15:00:00.123456Z +"#); "toml")] + #[test_case(&RawFrontMatter::Yaml(r#" +title: Hello +description: hey there +date: 2002-10-02T15:00:00.123456Z +"#); "yaml")] + fn can_parse_date_rfc3339_with_microseconds(content: &RawFrontMatter) { let res = PageFrontMatter::parse(content).unwrap(); assert!(res.datetime.is_some()); } - #[test] - fn cannot_parse_random_date_format() { - let content = r#" - title = "Hello" - description = "hey there" - date = 2002/10/12"#; + #[test_case(&RawFrontMatter::Toml(r#" +title = "Hello" +description = "hey there" +date = 2002/10/12 +"#); "toml")] + #[test_case(&RawFrontMatter::Yaml(r#" +title: Hello +description: hey there +date: 2002/10/12 +"#); "yaml")] + fn cannot_parse_random_date_format(content: &RawFrontMatter) { let res = PageFrontMatter::parse(content); assert!(res.is_err()); } - #[test] - fn cannot_parse_invalid_date_format() { - let content = r#" - title = "Hello" - description = "hey there" - date = 2002-14-01"#; + #[test_case(&RawFrontMatter::Toml(r#" +title = "Hello" +description = "hey there" +date = 2002-14-01 +"#); "toml")] + #[test_case(&RawFrontMatter::Yaml(r#" +title: Hello +description: hey there +date: 2002-14-01 +"#); "yaml")] + fn cannot_parse_invalid_date_format(content: &RawFrontMatter) { let res = PageFrontMatter::parse(content); assert!(res.is_err()); } - #[test] - fn cannot_parse_date_as_string() { - let content = r#" - title = "Hello" - description = "hey there" - date = "2002-14-01""#; + #[test_case(&RawFrontMatter::Toml(r#" +title = "Hello" +description = "hey there" +date = "2016-10-10" +"#); "toml")] + #[test_case(&RawFrontMatter::Yaml(r#" +title: Hello +description: hey there +date: "2016-10-10" +"#); "yaml")] + fn can_parse_valid_date_as_string(content: &RawFrontMatter) { + let res = PageFrontMatter::parse(content).unwrap(); + assert!(res.date.is_some()); + } + + #[test_case(&RawFrontMatter::Toml(r#" +title = "Hello" +description = "hey there" +date = "2002-14-01" +"#); "toml")] + #[test_case(&RawFrontMatter::Yaml(r#" +title: Hello +description: hey there +date: "2002-14-01" +"#); "yaml")] + fn cannot_parse_invalid_date_as_string(content: &RawFrontMatter) { let res = PageFrontMatter::parse(content); assert!(res.is_err()); } - #[test] - fn can_parse_dates_in_extra() { - let content = r#" - title = "Hello" - description = "hey there" + #[test_case(&RawFrontMatter::Toml(r#" +title = "Hello" +description = "hey there" - [extra] - some-date = 2002-14-01"#; +[extra] +some-date = 2002-14-01 +"#); "toml")] + #[test_case(&RawFrontMatter::Yaml(r#" +title: Hello +description: hey there + +extra: + some-date: 2002-14-01 +"#); "yaml")] + fn can_parse_dates_in_extra(content: &RawFrontMatter) { let res = PageFrontMatter::parse(content); println!("{:?}", res); assert!(res.is_ok()); assert_eq!(res.unwrap().extra["some-date"], to_value("2002-14-01").unwrap()); } - #[test] - fn can_parse_nested_dates_in_extra() { - let content = r#" - title = "Hello" - description = "hey there" + #[test_case(&RawFrontMatter::Toml(r#" +title = "Hello" +description = "hey there" - [extra.something] - some-date = 2002-14-01"#; +[extra.something] +some-date = 2002-14-01 +"#); "toml")] + #[test_case(&RawFrontMatter::Yaml(r#" +title: Hello +description: hey there + +extra: + something: + some-date: 2002-14-01 +"#); "yaml")] + fn can_parse_nested_dates_in_extra(content: &RawFrontMatter) { let res = PageFrontMatter::parse(content); println!("{:?}", res); assert!(res.is_ok()); assert_eq!(res.unwrap().extra["something"]["some-date"], to_value("2002-14-01").unwrap()); } - #[test] - fn can_parse_fully_nested_dates_in_extra() { - let content = r#" - title = "Hello" - description = "hey there" + #[test_case(&RawFrontMatter::Toml(r#" +title = "Hello" +description = "hey there" - [extra] - date_example = 2020-05-04 - [[extra.questions]] - date = 2020-05-03 - name = "Who is the prime minister of Uganda?""#; +[extra] +date_example = 2020-05-04 +[[extra.questions]] +date = 2020-05-03 +name = "Who is the prime minister of Uganda?" +"#); "toml")] + #[test_case(&RawFrontMatter::Yaml(r#" +title: Hello +description: hey there + +extra: + date_example: 2020-05-04 + questions: + - date: 2020-05-03 + name: "Who is the prime minister of Uganda?" +"#); "yaml")] + fn can_parse_fully_nested_dates_in_extra(content: &RawFrontMatter) { let res = PageFrontMatter::parse(content); println!("{:?}", res); assert!(res.is_ok()); assert_eq!(res.unwrap().extra["questions"][0]["date"], to_value("2020-05-03").unwrap()); } - #[test] - fn can_parse_taxonomies() { - let content = r#" + #[test_case(&RawFrontMatter::Toml(r#" title = "Hello World" [taxonomies] tags = ["Rust", "JavaScript"] categories = ["Dev"] -"#; +"#); "toml")] + #[test_case(&RawFrontMatter::Yaml(r#" +title: Hello World + +taxonomies: + tags: + - Rust + - JavaScript + categories: + - Dev +"#); "yaml")] + fn can_parse_taxonomies(content: &RawFrontMatter) { let res = PageFrontMatter::parse(content); println!("{:?}", res); assert!(res.is_ok()); diff --git a/components/front_matter/src/section.rs b/components/front_matter/src/section.rs index 365dd524..063ba014 100644 --- a/components/front_matter/src/section.rs +++ b/components/front_matter/src/section.rs @@ -2,9 +2,11 @@ use serde_derive::{Deserialize, Serialize}; use tera::{Map, Value}; use super::{InsertAnchor, SortBy}; -use errors::{bail, Result}; +use errors::Result; use utils::de::fix_toml_dates; +use crate::RawFrontMatter; + static DEFAULT_PAGINATE_PATH: &str = "page"; /// The front matter of every section @@ -22,6 +24,8 @@ pub struct SectionFrontMatter { /// Higher values means it will be at the end. Defaults to `0` #[serde(skip_serializing)] pub weight: usize, + /// whether the section is a draft + pub draft: bool, /// Optional template, if we want to specify which template to render for that section #[serde(skip_serializing)] pub template: Option, @@ -71,11 +75,8 @@ pub struct SectionFrontMatter { } impl SectionFrontMatter { - pub fn parse(toml: &str) -> Result { - let mut f: SectionFrontMatter = match toml::from_str(toml) { - Ok(d) => d, - Err(e) => bail!(e), - }; + pub fn parse(raw: &RawFrontMatter) -> Result { + let mut f: SectionFrontMatter = raw.deserialize()?; f.extra = match fix_toml_dates(f.extra) { Value::Object(o) => o, @@ -114,6 +115,7 @@ impl Default for SectionFrontMatter { aliases: Vec::new(), generate_feed: false, extra: Map::new(), + draft: false, } } } diff --git a/components/library/src/content/file_info.rs b/components/library/src/content/file_info.rs index 7cae3c11..167d6048 100644 --- a/components/library/src/content/file_info.rs +++ b/components/library/src/content/file_info.rs @@ -129,6 +129,11 @@ impl FileInfo { // We can document that let mut parts: Vec = self.name.splitn(2, '.').map(|s| s.to_string()).collect(); + // If language code is same as default language, go for default + if config.default_language == parts[1].as_str() { + return Ok(config.default_language.clone()); + } + // The language code is not present in the config: typo or the user forgot to add it to the // config if !config.languages_codes().contains(&parts[1].as_ref()) { @@ -189,6 +194,19 @@ mod tests { assert_eq!(res.unwrap(), "fr"); } + #[test] + fn can_find_valid_language_with_default_locale() { + let mut config = Config::default(); + config.languages.push(Language { code: String::from("fr"), feed: false, search: false }); + let mut file = FileInfo::new_page( + &Path::new("/home/vincent/code/site/content/posts/tutorials/python.en.md"), + &PathBuf::new(), + ); + let res = file.find_language(&config); + assert!(res.is_ok()); + assert_eq!(res.unwrap(), config.default_language); + } + #[test] fn can_find_valid_language_in_page_with_assets() { let mut config = Config::default(); diff --git a/components/library/src/library.rs b/components/library/src/library.rs index 3aa7748b..02328ac0 100644 --- a/components/library/src/library.rs +++ b/components/library/src/library.rs @@ -112,7 +112,7 @@ impl Library { subsections // Using the original filename to work for multi-lingual sections .entry(grand_parent.join(§ion.file.filename)) - .or_insert_with(|| vec![]) + .or_insert_with(Vec::new) .push(section.file.path.clone()); } @@ -157,7 +157,7 @@ impl Library { parent_is_transparent = section.meta.transparent; } page.ancestors = - ancestors.get(&parent_section_path).cloned().unwrap_or_else(|| vec![]); + ancestors.get(&parent_section_path).cloned().unwrap_or_else(Vec::new); // Don't forget to push the actual parent page.ancestors.push(*section_key); @@ -201,8 +201,7 @@ impl Library { children.sort_by(|a, b| sections_weight[a].cmp(§ions_weight[b])); section.subsections = children; } - section.ancestors = - ancestors.get(§ion.file.path).cloned().unwrap_or_else(|| vec![]); + section.ancestors = ancestors.get(§ion.file.path).cloned().unwrap_or_else(Vec::new); } } diff --git a/components/library/src/taxonomies/mod.rs b/components/library/src/taxonomies/mod.rs index c048bfef..f71d8d95 100644 --- a/components/library/src/taxonomies/mod.rs +++ b/components/library/src/taxonomies/mod.rs @@ -237,7 +237,7 @@ pub fn find_taxonomies(config: &Config, library: &Library) -> Result Clivo sub inprovisoque nostrum minus fama est, discordia patrem petebat precatur absumitur, poena per sit. Foramina *tamen cupidine* memor supplex tollentes dictum unam orbem, Anubis caecae. Viderat formosior tegebat satis, Aethiopasque -sit submisso coniuge tristis ubi! +sit submisso coniuge tristis ubi! :exclamation: ## Praeceps Corinthus totidem quem crus vultum cape @@ -68,7 +68,7 @@ And a shortcode: ### Another subsection Gotta make the toc do a little bit of work -# A big title +# A big title :fire: - hello - world @@ -96,7 +96,7 @@ fn bench_render_content_without_highlighting(b: &mut test::Bencher) { tera.add_raw_template("shortcodes/youtube.html", "{{id}}").unwrap(); let permalinks_ctx = HashMap::new(); let mut config = Config::default(); - config.highlight_code = false; + config.markdown.highlight_code = false; let context = RenderContext::new(&tera, &config, "", &permalinks_ctx, InsertAnchor::None); b.iter(|| render_content(CONTENT, &context).unwrap()); } @@ -106,7 +106,7 @@ fn bench_render_content_no_shortcode(b: &mut test::Bencher) { let tera = Tera::default(); let content2 = CONTENT.replace(r#"{{ youtube(id="my_youtube_id") }}"#, ""); let mut config = Config::default(); - config.highlight_code = false; + config.markdown.highlight_code = false; let permalinks_ctx = HashMap::new(); let context = RenderContext::new(&tera, &config, "", &permalinks_ctx, InsertAnchor::None); @@ -123,3 +123,16 @@ fn bench_render_shortcodes_one_present(b: &mut test::Bencher) { b.iter(|| render_shortcodes(CONTENT, &context)); } + +#[bench] +fn bench_render_content_no_shortcode_with_emoji(b: &mut test::Bencher) { + let tera = Tera::default(); + let content2 = CONTENT.replace(r#"{{ youtube(id="my_youtube_id") }}"#, ""); + let mut config = Config::default(); + config.markdown.highlight_code = false; + config.markdown.render_emoji = true; + let permalinks_ctx = HashMap::new(); + let context = RenderContext::new(&tera, &config, "", &permalinks_ctx, InsertAnchor::None); + + b.iter(|| render_content(&content2, &context).unwrap()); +} diff --git a/components/rendering/src/context.rs b/components/rendering/src/context.rs index 8d877d1e..30249d36 100644 --- a/components/rendering/src/context.rs +++ b/components/rendering/src/context.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::collections::HashMap; use config::Config; @@ -7,11 +8,11 @@ use tera::{Context, Tera}; /// All the information from the zola site that is needed to render HTML from markdown #[derive(Debug)] pub struct RenderContext<'a> { - pub tera: &'a Tera, + pub tera: Cow<'a, Tera>, pub config: &'a Config, pub tera_context: Context, pub current_page_permalink: &'a str, - pub permalinks: &'a HashMap, + pub permalinks: Cow<'a, HashMap>, pub insert_anchor: InsertAnchor, } @@ -25,13 +26,25 @@ impl<'a> RenderContext<'a> { ) -> RenderContext<'a> { let mut tera_context = Context::new(); tera_context.insert("config", config); - RenderContext { - tera, + Self { + tera: Cow::Borrowed(tera), tera_context, current_page_permalink, - permalinks, + permalinks: Cow::Borrowed(permalinks), insert_anchor, config, } } + + // In use in the markdown filter + pub fn from_config(config: &'a Config) -> RenderContext<'a> { + Self { + tera: Cow::Owned(Tera::default()), + tera_context: Context::new(), + current_page_permalink: "", + permalinks: Cow::Owned(HashMap::new()), + insert_anchor: InsertAnchor::None, + config, + } + } } diff --git a/components/rendering/src/markdown.rs b/components/rendering/src/markdown.rs index ff419fbd..15154c07 100644 --- a/components/rendering/src/markdown.rs +++ b/components/rendering/src/markdown.rs @@ -13,7 +13,6 @@ use utils::slugs::slugify_anchors; use utils::vec::InsertMany; use self::cmark::{Event, LinkType, Options, Parser, Tag}; -use pulldown_cmark::CodeBlockKind; mod codeblock; mod fence; @@ -101,17 +100,12 @@ fn fix_link( return Ok(link.to_string()); } - // TODO: remove me in a few versions when people have upgraded - if link.starts_with("./") && link.contains(".md") { - println!("It looks like the link `{}` is using the previous syntax for internal links: start with @/ instead", link); - } - // A few situations here: // - it could be a relative link (starting with `@/`) // - it could be a link to a co-located asset // - it could be a normal link let result = if link.starts_with("@/") { - match resolve_internal_link(&link, context.permalinks) { + match resolve_internal_link(&link, &context.permalinks) { Ok(resolved) => { if resolved.anchor.is_some() { internal_links_with_anchors @@ -168,6 +162,10 @@ fn get_heading_refs(events: &[Event]) -> Vec { } pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result { + lazy_static! { + static ref EMOJI_REPLACER: gh_emoji::Replacer = gh_emoji::Replacer::new(); + } + // the rendered html let mut html = String::with_capacity(content.len()); // Set while parsing @@ -188,6 +186,10 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result Result { - if !context.config.highlight_code { + let language = match kind { + cmark::CodeBlockKind::Fenced(fence_info) => { + let fence_info = fence::FenceSettings::new(fence_info); + fence_info.language + } + _ => None, + }; + + if !context.config.highlight_code() { + if let Some(lang) = language { + let html = format!(r#"
"#, lang);
+                                return Event::Html(html.into());
+                            }
                             return Event::Html("
".into());
                         }
 
-                        let theme = &THEME_SET.themes[&context.config.highlight_theme];
+                        let theme = &THEME_SET.themes[context.config.highlight_theme()];
                         match kind {
-                            CodeBlockKind::Indented => (),
-                            CodeBlockKind::Fenced(fence_info) => {
+                            cmark::CodeBlockKind::Indented => (),
+                            cmark::CodeBlockKind::Fenced(fence_info) => {
                                 // This selects the background color the same way that
                                 // start_coloured_html_snippet does
                                 let color = theme
@@ -227,11 +244,17 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result");
+                        if let Some(lang) = language {
+                            html.push_str(r#""#);
+                        } else {
+                            html.push_str("");
+                        }
                         Event::Html(html.into())
                     }
                     Event::End(Tag::CodeBlock(_)) => {
-                        if !context.config.highlight_code {
+                        if !context.config.highlight_code() {
                             return Event::Html("
\n".into()); } // reset highlight and close the code block @@ -264,29 +287,42 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result { if markup.contains("") { has_summary = true; Event::Html(CONTINUE_READING.into()) - } else { - if in_html_block && markup.contains("
") { + } else if in_html_block && markup.contains("") { + in_html_block = false; + Event::Html(markup.replacen("", "", 1).into()) + } else if markup.contains("pre data-shortcode") { + in_html_block = true; + let m = markup.replacen("
", "", 1);
+                            if m.contains("
") { in_html_block = false; - Event::Html(markup.replacen("", "", 1).into()) - } else if markup.contains("pre data-shortcode") { - in_html_block = true; - let m = markup.replacen("
", "", 1);
-                                if m.contains("
") { - in_html_block = false; - Event::Html(m.replacen("", "", 1).into()) - } else { - Event::Html(m.into()) - } + Event::Html(m.replacen("", "", 1).into()) } else { - event + Event::Html(m.into()) } + } else { + event } } _ => event, @@ -348,7 +384,7 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result, context: &RenderContext, + invocation_count: u32, body: Option<&str>, ) -> Result { let mut tera_context = Context::new(); @@ -112,6 +114,7 @@ fn render_shortcode( // Trimming right to avoid most shortcodes with bodies ending up with a HTML new line tera_context.insert("body", b.trim_end()); } + tera_context.insert("nth", &invocation_count); tera_context.extend(context.tera_context.clone()); let mut template_name = format!("shortcodes/{}.md", name); @@ -139,6 +142,12 @@ fn render_shortcode( pub fn render_shortcodes(content: &str, context: &RenderContext) -> Result { let mut res = String::with_capacity(content.len()); + let mut invocation_map: HashMap = HashMap::new(); + let mut get_invocation_count = |name: &str| { + let invocation_number = invocation_map.entry(String::from(name)).or_insert(0); + *invocation_number += 1; + *invocation_number + }; let mut pairs = match ContentParser::parse(Rule::page, content) { Ok(p) => p, @@ -184,7 +193,13 @@ pub fn render_shortcodes(content: &str, context: &RenderContext) -> Result res.push_str(p.as_span().as_str()), Rule::inline_shortcode => { let (name, args) = parse_shortcode_call(p); - res.push_str(&render_shortcode(&name, &args, context, None)?); + res.push_str(&render_shortcode( + &name, + &args, + context, + get_invocation_count(&name), + None, + )?); } Rule::shortcode_with_body => { let mut inner = p.into_inner(); @@ -192,7 +207,13 @@ pub fn render_shortcodes(content: &str, context: &RenderContext) -> Result { res.push_str( diff --git a/components/rendering/tests/codeblock_hl_lines.rs b/components/rendering/tests/codeblock_hl_lines.rs index bbbc3655..6f0eb7e4 100644 --- a/components/rendering/tests/codeblock_hl_lines.rs +++ b/components/rendering/tests/codeblock_hl_lines.rs @@ -37,7 +37,7 @@ fn hl_lines_simple() { let tera_ctx = Tera::default(); let permalinks_ctx = HashMap::new(); let mut config = Config::default(); - config.highlight_code = true; + config.markdown.highlight_code = true; let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let res = render_content( r#" @@ -66,7 +66,7 @@ fn hl_lines_in_middle() { let tera_ctx = Tera::default(); let permalinks_ctx = HashMap::new(); let mut config = Config::default(); - config.highlight_code = true; + config.markdown.highlight_code = true; let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let res = render_content( r#" @@ -95,7 +95,7 @@ fn hl_lines_all() { let tera_ctx = Tera::default(); let permalinks_ctx = HashMap::new(); let mut config = Config::default(); - config.highlight_code = true; + config.markdown.highlight_code = true; let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let res = render_content( r#" @@ -122,7 +122,7 @@ fn hl_lines_start_from_one() { let tera_ctx = Tera::default(); let permalinks_ctx = HashMap::new(); let mut config = Config::default(); - config.highlight_code = true; + config.markdown.highlight_code = true; let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let res = render_content( r#" @@ -150,7 +150,7 @@ fn hl_lines_start_from_zero() { let tera_ctx = Tera::default(); let permalinks_ctx = HashMap::new(); let mut config = Config::default(); - config.highlight_code = true; + config.markdown.highlight_code = true; let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let res = render_content( r#" @@ -178,7 +178,7 @@ fn hl_lines_end() { let tera_ctx = Tera::default(); let permalinks_ctx = HashMap::new(); let mut config = Config::default(); - config.highlight_code = true; + config.markdown.highlight_code = true; let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let res = render_content( r#" @@ -206,7 +206,7 @@ fn hl_lines_end_out_of_bounds() { let tera_ctx = Tera::default(); let permalinks_ctx = HashMap::new(); let mut config = Config::default(); - config.highlight_code = true; + config.markdown.highlight_code = true; let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let res = render_content( r#" @@ -234,7 +234,7 @@ fn hl_lines_overlap() { let tera_ctx = Tera::default(); let permalinks_ctx = HashMap::new(); let mut config = Config::default(); - config.highlight_code = true; + config.markdown.highlight_code = true; let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let res = render_content( r#" @@ -261,7 +261,7 @@ fn hl_lines_multiple() { let tera_ctx = Tera::default(); let permalinks_ctx = HashMap::new(); let mut config = Config::default(); - config.highlight_code = true; + config.markdown.highlight_code = true; let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let res = render_content( r#" @@ -289,7 +289,7 @@ fn hl_lines_extra_spaces() { let tera_ctx = Tera::default(); let permalinks_ctx = HashMap::new(); let mut config = Config::default(); - config.highlight_code = true; + config.markdown.highlight_code = true; let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let res = render_content( r#" @@ -317,7 +317,7 @@ fn hl_lines_int_and_range() { let tera_ctx = Tera::default(); let permalinks_ctx = HashMap::new(); let mut config = Config::default(); - config.highlight_code = true; + config.markdown.highlight_code = true; let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let res = render_content( r#" @@ -346,7 +346,7 @@ fn hl_lines_single_line_range() { let tera_ctx = Tera::default(); let permalinks_ctx = HashMap::new(); let mut config = Config::default(); - config.highlight_code = true; + config.markdown.highlight_code = true; let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let res = render_content( r#" @@ -375,7 +375,7 @@ fn hl_lines_reverse_range() { let tera_ctx = Tera::default(); let permalinks_ctx = HashMap::new(); let mut config = Config::default(); - config.highlight_code = true; + config.markdown.highlight_code = true; let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let res = render_content( r#" diff --git a/components/rendering/tests/markdown.rs b/components/rendering/tests/markdown.rs index e37f9212..835e0fa4 100644 --- a/components/rendering/tests/markdown.rs +++ b/components/rendering/tests/markdown.rs @@ -23,7 +23,7 @@ fn doesnt_highlight_code_block_with_highlighting_off() { let tera_ctx = Tera::default(); let permalinks_ctx = HashMap::new(); let mut config = Config::default(); - config.highlight_code = false; + config.markdown.highlight_code = false; let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let res = render_content("```\n$ gutenberg server\n```", &context).unwrap(); assert_eq!(res.body, "
$ gutenberg server\n
\n"); @@ -34,7 +34,7 @@ fn can_highlight_code_block_no_lang() { let tera_ctx = Tera::default(); let permalinks_ctx = HashMap::new(); let mut config = Config::default(); - config.highlight_code = true; + config.markdown.highlight_code = true; let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let res = render_content("```\n$ gutenberg server\n$ ping\n```", &context).unwrap(); assert_eq!( @@ -48,12 +48,12 @@ fn can_highlight_code_block_with_lang() { let tera_ctx = Tera::default(); let permalinks_ctx = HashMap::new(); let mut config = Config::default(); - config.highlight_code = true; + config.markdown.highlight_code = true; let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let res = render_content("```python\nlist.append(1)\n```", &context).unwrap(); assert_eq!( res.body, - "
\nlist.append(1)\n
" + "
\nlist.append(1)\n
" ); } @@ -62,13 +62,13 @@ fn can_higlight_code_block_with_unknown_lang() { let tera_ctx = Tera::default(); let permalinks_ctx = HashMap::new(); let mut config = Config::default(); - config.highlight_code = true; + config.markdown.highlight_code = true; let context = RenderContext::new(&tera_ctx, &config, "", &permalinks_ctx, InsertAnchor::None); let res = render_content("```yolo\nlist.append(1)\n```", &context).unwrap(); // defaults to plain text assert_eq!( res.body, - "
\nlist.append(1)\n
" + "
\nlist.append(1)\n
" ); } @@ -87,7 +87,9 @@ Hello ) .unwrap(); assert!(res.body.contains("

Hello

\n
")); - assert!(res.body.contains(r#" +
diff --git a/components/templates/src/filters.rs b/components/templates/src/filters.rs index 5f4e521a..c9b6d6cd 100644 --- a/components/templates/src/filters.rs +++ b/components/templates/src/filters.rs @@ -2,38 +2,44 @@ use std::collections::HashMap; use std::hash::BuildHasher; use base64::{decode, encode}; -use pulldown_cmark as cmark; -use tera::{to_value, try_get_value, Result as TeraResult, Value}; +use config::Config; +use rendering::{render_content, RenderContext}; +use tera::{to_value, try_get_value, Filter as TeraFilter, Result as TeraResult, Value}; -pub fn markdown( - value: &Value, - args: &HashMap, -) -> TeraResult { - let s = try_get_value!("markdown", "value", String, value); - let inline = match args.get("inline") { - Some(val) => try_get_value!("markdown", "inline", bool, val), - None => false, - }; +#[derive(Debug)] +pub struct MarkdownFilter { + config: Config, +} - let mut opts = cmark::Options::empty(); - opts.insert(cmark::Options::ENABLE_TABLES); - opts.insert(cmark::Options::ENABLE_FOOTNOTES); - opts.insert(cmark::Options::ENABLE_STRIKETHROUGH); - opts.insert(cmark::Options::ENABLE_TASKLISTS); - - let mut html = String::new(); - let parser = cmark::Parser::new_ext(&s, opts); - cmark::html::push_html(&mut html, parser); - - if inline { - html = html - .trim_start_matches("

") - // pulldown_cmark finishes a paragraph with `

\n` - .trim_end_matches("

\n") - .to_string(); +impl MarkdownFilter { + pub fn new(config: Config) -> Self { + Self { config } } +} - Ok(to_value(&html).unwrap()) +impl TeraFilter for MarkdownFilter { + fn filter(&self, value: &Value, args: &HashMap) -> TeraResult { + let context = RenderContext::from_config(&self.config); + let s = try_get_value!("markdown", "value", String, value); + let inline = match args.get("inline") { + Some(val) => try_get_value!("markdown", "inline", bool, val), + None => false, + }; + let mut html = match render_content(&s, &context) { + Ok(res) => res.body, + Err(e) => return Err(format!("Failed to render markdown filter: {:?}", e).into()), + }; + + if inline { + html = html + .trim_start_matches("

") + // pulldown_cmark finishes a paragraph with `

\n` + .trim_end_matches("

\n") + .to_string(); + } + + Ok(to_value(&html).unwrap()) + } } pub fn base64_encode( @@ -56,22 +62,24 @@ pub fn base64_decode( mod tests { use std::collections::HashMap; - use tera::to_value; + use tera::{to_value, Filter}; - use super::{base64_decode, base64_encode, markdown}; + use super::{base64_decode, base64_encode, MarkdownFilter}; + use config::Config; #[test] fn markdown_filter() { - let result = markdown(&to_value(&"# Hey").unwrap(), &HashMap::new()); + let result = MarkdownFilter::new(Config::default()) + .filter(&to_value(&"# Hey").unwrap(), &HashMap::new()); assert!(result.is_ok()); - assert_eq!(result.unwrap(), to_value(&"

Hey

\n").unwrap()); + assert_eq!(result.unwrap(), to_value(&"

Hey

\n").unwrap()); } #[test] fn markdown_filter_inline() { let mut args = HashMap::new(); args.insert("inline".to_string(), to_value(true).unwrap()); - let result = markdown( + let result = MarkdownFilter::new(Config::default()).filter( &to_value(&"Using `map`, `filter`, and `fold` instead of `for`").unwrap(), &args, ); @@ -84,7 +92,7 @@ mod tests { fn markdown_filter_inline_tables() { let mut args = HashMap::new(); args.insert("inline".to_string(), to_value(true).unwrap()); - let result = markdown( + let result = MarkdownFilter::new(Config::default()).filter( &to_value( &r#" |id|author_id| timestamp_created|title |content | @@ -100,6 +108,26 @@ mod tests { assert!(result.unwrap().as_str().unwrap().contains("")); } + #[test] + fn markdown_filter_use_config_options() { + let mut config = Config::default(); + config.markdown.highlight_code = true; + config.markdown.smart_punctuation = true; + config.markdown.render_emoji = true; + config.markdown.external_links_target_blank = true; + + let md = "Hello :smile: ..."; + let result = + MarkdownFilter::new(config.clone()).filter(&to_value(&md).unwrap(), &HashMap::new()); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), to_value(&"

Hello https://google.com 😄 …

\n").unwrap()); + + let md = "```py\ni=0\n```"; + let result = MarkdownFilter::new(config).filter(&to_value(&md).unwrap(), &HashMap::new()); + assert!(result.is_ok()); + assert!(result.unwrap().as_str().unwrap().contains("
 Ok(OutputFormat::Toml),
             "csv" => Ok(OutputFormat::Csv),
             "json" => Ok(OutputFormat::Json),
+            "bibtex" => Ok(OutputFormat::Bibtex),
             "plain" => Ok(OutputFormat::Plain),
             format => Err(format!("Unknown output format {}", format).into()),
         }
@@ -63,6 +65,7 @@ impl OutputFormat {
             OutputFormat::Json => "application/json",
             OutputFormat::Csv => "text/csv",
             OutputFormat::Toml => "application/toml",
+            OutputFormat::Bibtex => "application/x-bibtex",
             OutputFormat::Plain => "text/plain",
         })
     }
@@ -148,7 +151,7 @@ fn get_output_format_from_args(
     let format_arg = optional_arg!(
         String,
         args.get("format"),
-        "`load_data`: `format` needs to be an argument with a string value, being one of the supported `load_data` file types (csv, json, toml, plain)"
+        "`load_data`: `format` needs to be an argument with a string value, being one of the supported `load_data` file types (csv, json, toml, bibtex, plain)"
     );
 
     if let Some(format) = format_arg {
@@ -165,11 +168,11 @@ fn get_output_format_from_args(
     };
 
     // Always default to Plain if we don't know what it is
-    OutputFormat::from_str(from_extension).or_else(|_| Ok(OutputFormat::Plain))
+    OutputFormat::from_str(from_extension).or(Ok(OutputFormat::Plain))
 }
 
 /// A Tera function to load data from a file or from a URL
-/// Currently the supported formats are json, toml, csv and plain text
+/// Currently the supported formats are json, toml, csv, bibtex and plain text
 #[derive(Debug)]
 pub struct LoadData {
     base_path: PathBuf,
@@ -223,6 +226,7 @@ impl TeraFn for LoadData {
             OutputFormat::Toml => load_toml(data),
             OutputFormat::Csv => load_csv(data),
             OutputFormat::Json => load_json(data),
+            OutputFormat::Bibtex => load_bibtex(data),
             OutputFormat::Plain => to_value(data).map_err(|e| e.into()),
         };
 
@@ -252,6 +256,47 @@ fn load_toml(toml_data: String) -> Result {
     }
 }
 
+/// Parse a BIBTEX string and convert it to a Tera Value
+fn load_bibtex(bibtex_data: String) -> Result {
+    let bibtex_model = nom_bibtex::Bibtex::parse(&bibtex_data).map_err(|e| format!("{:?}", e))?;
+    let mut bibtex_map = Map::new();
+
+    let preambles_array =
+        bibtex_model.preambles().iter().map(|v| Value::String(v.to_string())).collect();
+    bibtex_map.insert(String::from("preambles"), Value::Array(preambles_array));
+
+    let comments_array =
+        bibtex_model.comments().iter().map(|v| Value::String(v.to_string())).collect();
+    bibtex_map.insert(String::from("comments"), Value::Array(comments_array));
+
+    let mut variables_map = Map::new();
+    for (key, val) in bibtex_model.variables() {
+        variables_map.insert(key.to_string(), Value::String(val.to_string()));
+    }
+    bibtex_map.insert(String::from("variables"), Value::Object(variables_map));
+
+    let bibliographies_array = bibtex_model
+        .bibliographies()
+        .iter()
+        .map(|b| {
+            let mut m = Map::new();
+            m.insert(String::from("entry_type"), Value::String(b.entry_type().to_string()));
+            m.insert(String::from("citation_key"), Value::String(b.citation_key().to_string()));
+
+            let mut tags = Map::new();
+            for (key, val) in b.tags() {
+                tags.insert(key.to_lowercase().to_string(), Value::String(val.to_string()));
+            }
+            m.insert(String::from("tags"), Value::Object(tags));
+            Value::Object(m)
+        })
+        .collect();
+    bibtex_map.insert(String::from("bibliographies"), Value::Array(bibliographies_array));
+
+    let bibtex_value: Value = Value::Object(bibtex_map);
+    to_value(bibtex_value).map_err(|err| err.into())
+}
+
 /// Parse a CSV string and convert it to a Tera Value
 ///
 /// An example csv file `example.csv` could be:
diff --git a/components/templates/src/global_fns/mod.rs b/components/templates/src/global_fns/mod.rs
index 2b3693d4..fa7bb283 100644
--- a/components/templates/src/global_fns/mod.rs
+++ b/components/templates/src/global_fns/mod.rs
@@ -39,7 +39,7 @@ impl TeraFn for Trans {
         let term = self
             .config
             .get_translation(lang, key)
-            .map_err(|e| Error::chain("Failed to retreive term translation", e))?;
+            .map_err(|e| Error::chain("Failed to retrieve term translation", e))?;
 
         Ok(to_value(term).unwrap())
     }
@@ -331,7 +331,7 @@ impl GetTaxonomyUrl {
             }
             taxonomies.insert(format!("{}-{}", taxo.kind.name, taxo.kind.lang), items);
         }
-        Self { taxonomies, default_lang: default_lang.to_string(), slugify: slugify }
+        Self { taxonomies, default_lang: default_lang.to_string(), slugify }
     }
 }
 impl TeraFn for GetTaxonomyUrl {
@@ -735,7 +735,7 @@ title = "A title"
 
         let config = Config::parse(TRANS_CONFIG).unwrap();
         let error = Trans::new(config).call(&args).unwrap_err();
-        assert_eq!("Failed to retreive term translation", format!("{}", error));
+        assert_eq!("Failed to retrieve term translation", format!("{}", error));
     }
 
     #[test]
@@ -746,7 +746,7 @@ title = "A title"
 
         let config = Config::parse(TRANS_CONFIG).unwrap();
         let error = Trans::new(config).call(&args).unwrap_err();
-        assert_eq!("Failed to retreive term translation", format!("{}", error));
+        assert_eq!("Failed to retrieve term translation", format!("{}", error));
     }
 
     #[test]
diff --git a/components/templates/src/lib.rs b/components/templates/src/lib.rs
index a0ca5947..eef42267 100644
--- a/components/templates/src/lib.rs
+++ b/components/templates/src/lib.rs
@@ -36,7 +36,6 @@ lazy_static! {
             ("internal/alias.html", include_str!("builtins/internal/alias.html")),
         ])
         .unwrap();
-        tera.register_filter("markdown", filters::markdown);
         tera.register_filter("base64_encode", filters::base64_encode);
         tera.register_filter("base64_decode", filters::base64_decode);
         tera
diff --git a/components/utils/src/de.rs b/components/utils/src/de.rs
index 654eaee1..e294e92a 100644
--- a/components/utils/src/de.rs
+++ b/components/utils/src/de.rs
@@ -1,12 +1,32 @@
 use serde::{Deserialize, Deserializer};
+use serde_derive::Deserialize;
 use tera::{Map, Value};
 
 /// Used as an attribute when we want to convert from TOML to a string date
+/// If a TOML datetime isn't present, it will accept a string and push it through
+/// TOML's date time parser to ensure only valid dates are accepted.
+/// Inspired by this proposal: https://github.com/alexcrichton/toml-rs/issues/269
 pub fn from_toml_datetime<'de, D>(deserializer: D) -> Result, D::Error>
 where
     D: Deserializer<'de>,
 {
-    toml::value::Datetime::deserialize(deserializer).map(|s| Some(s.to_string()))
+    use serde::de::Error;
+    use std::str::FromStr;
+
+    #[derive(Deserialize)]
+    #[serde(untagged)]
+    enum MaybeDatetime {
+        Datetime(toml::value::Datetime),
+        String(String),
+    }
+
+    match MaybeDatetime::deserialize(deserializer)? {
+        MaybeDatetime::Datetime(d) => Ok(Some(d.to_string())),
+        MaybeDatetime::String(s) => match toml::value::Datetime::from_str(&s) {
+            Ok(d) => Ok(Some(d.to_string())),
+            Err(e) => Err(D::Error::custom(e)),
+        },
+    }
 }
 
 /// Returns key/value for a converted date from TOML.
diff --git a/components/utils/src/fs.rs b/components/utils/src/fs.rs
index 2b8e1c5f..a46f6d94 100644
--- a/components/utils/src/fs.rs
+++ b/components/utils/src/fs.rs
@@ -20,8 +20,8 @@ pub fn is_path_in_directory(parent: &Path, path: &Path) -> Result {
 
 /// Create a file with the content given
 pub fn create_file(path: &Path, content: &str) -> Result<()> {
-    let mut file =
-        File::create(&path).map_err(|e| Error::chain(format!("Failed to create {:?}", path), e))?;
+    let mut file = File::create(&path)
+        .map_err(|e| Error::chain(format!("Failed to create file {}", path.display()), e))?;
     file.write_all(content.as_bytes())?;
     Ok(())
 }
@@ -62,7 +62,7 @@ pub fn read_file(path: &Path) -> Result {
 
 /// Return the content of a file, with error handling added.
 /// The default error message is overwritten by the message given.
-/// That means it is allocation 2 strings, oh well
+/// That means it is allocating 2 strings, oh well
 pub fn read_file_with_error(path: &Path, message: &str) -> Result {
     let res = read_file(&path);
     if res.is_ok() {
@@ -101,7 +101,9 @@ pub fn copy_file(src: &Path, dest: &PathBuf, base_path: &PathBuf, hard_link: boo
     let target_path = dest.join(relative_path);
 
     if let Some(parent_directory) = target_path.parent() {
-        create_dir_all(parent_directory)?;
+        create_dir_all(parent_directory).map_err(|e| {
+            Error::chain(format!("Was not able to create folder {}", parent_directory.display()), e)
+        })?;
     }
 
     copy_file_if_needed(src, &target_path, hard_link)
@@ -113,7 +115,9 @@ pub fn copy_file(src: &Path, dest: &PathBuf, base_path: &PathBuf, hard_link: boo
 /// 3. Its filesize is identical to that of the src file.
 pub fn copy_file_if_needed(src: &Path, dest: &PathBuf, hard_link: bool) -> Result<()> {
     if let Some(parent_directory) = dest.parent() {
-        create_dir_all(parent_directory)?;
+        create_dir_all(parent_directory).map_err(|e| {
+            Error::chain(format!("Was not able to create folder {}", parent_directory.display()), e)
+        })?;
     }
 
     if hard_link {
@@ -125,11 +129,25 @@ pub fn copy_file_if_needed(src: &Path, dest: &PathBuf, hard_link: bool) -> Resul
             let target_metadata = metadata(&dest)?;
             let target_mtime = FileTime::from_last_modification_time(&target_metadata);
             if !(src_mtime == target_mtime && src_metadata.len() == target_metadata.len()) {
-                copy(src, &dest)?;
+                copy(src, &dest).map_err(|e| {
+                    Error::chain(
+                        format!(
+                            "Was not able to copy file {} to {}",
+                            src.display(),
+                            dest.display()
+                        ),
+                        e,
+                    )
+                })?;
                 set_file_mtime(&dest, src_mtime)?;
             }
         } else {
-            copy(src, &dest)?;
+            copy(src, &dest).map_err(|e| {
+                Error::chain(
+                    format!("Was not able to copy file {} to {}", src.display(), dest.display()),
+                    e,
+                )
+            })?;
             set_file_mtime(&dest, src_mtime)?;
         }
     }
@@ -146,7 +164,16 @@ pub fn copy_directory(src: &PathBuf, dest: &PathBuf, hard_link: bool) -> Result<
                 create_directory(&target_path)?;
             }
         } else {
-            copy_file(entry.path(), dest, src, hard_link)?;
+            copy_file(entry.path(), dest, src, hard_link).map_err(|e| {
+                Error::chain(
+                    format!(
+                        "Was not able to copy file {} to {}",
+                        entry.path().display(),
+                        dest.display()
+                    ),
+                    e,
+                )
+            })?;
         }
     }
     Ok(())
diff --git a/docs/content/documentation/content/multilingual.md b/docs/content/documentation/content/multilingual.md
index 443e7e49..cd697ff5 100644
--- a/docs/content/documentation/content/multilingual.md
+++ b/docs/content/documentation/content/multilingual.md
@@ -33,8 +33,8 @@ uses the filename to detect the language:
 - `content/an-article.md`: this will be the default language
 - `content/an-article.fr.md`: this will be in French
 
-If the language code in the filename does not correspond to one of the languages configured,
-an error will be shown.
+If the language code in the filename does not correspond to one of the languages or
+the default language configured, an error will be shown.
 
 If your default language has an `_index.md` in a directory, you will need to add an `_index.{code}.md`
 file with the desired front-matter options as there is no language fallback.
diff --git a/docs/content/documentation/content/page.md b/docs/content/documentation/content/page.md
index d04da579..ac6ac242 100644
--- a/docs/content/documentation/content/page.md
+++ b/docs/content/documentation/content/page.md
@@ -79,6 +79,9 @@ by triple pluses (`+++`).
 
 Although none of the front matter variables are mandatory, the opening and closing `+++` are required.
 
+Note that even though the use of TOML is encouraged, YAML front matter is also supported to ease porting
+legacy content. In this case the embedded metadata must be enclosed by triple minuses (`---`).
+
 Here is an example page with all the available variables. The values provided below are the
 default values.
 
diff --git a/docs/content/documentation/content/section.md b/docs/content/documentation/content/section.md
index 80003aea..fd9f9916 100644
--- a/docs/content/documentation/content/section.md
+++ b/docs/content/documentation/content/section.md
@@ -18,6 +18,9 @@ Any non-Markdown file in a section directory is added to the `assets` collection
 [content overview](@/documentation/content/overview.md#asset-colocation). These files are then available in the
 Markdown file using relative links.
 
+## Drafting
+Just like pages sections can be drafted by setting the `draft` option in the front matter. By default this is not done. When a section is drafted it's descendants like pages, subsections and assets will not be processed unless the `--drafts` flag is passed. Note that even pages that don't have a `draft` status will not be processed if one of their parent sections is drafted. 
+
 ## Front matter
 
 The `_index.md` file within a directory defines the content and metadata for that section.  To set
@@ -30,6 +33,9 @@ to your templates through the `section.content` variable.
 
 Although none of the front matter variables are mandatory, the opening and closing `+++` are required.
 
+Note that even though the use of TOML is encouraged, YAML front matter is also supported to ease porting
+legacy content. In this case the embedded metadata must be enclosed by triple minuses (`---`).
+
 Here is an example `_index.md` with all the available variables. The values provided below are the
 default values.
 
@@ -39,6 +45,9 @@ title = ""
 
 description = ""
 
+# A draft section is only loaded if the `--drafts` flag is passed to `zola build`, `zola serve` or `zola check`.
+draft = false
+
 # Used to sort pages by "date", "weight" or "none". See below for more information.
 sort_by = "none"
 
diff --git a/docs/content/documentation/content/shortcodes.md b/docs/content/documentation/content/shortcodes.md
index b0fba7f9..8deb23c3 100644
--- a/docs/content/documentation/content/shortcodes.md
+++ b/docs/content/documentation/content/shortcodes.md
@@ -134,6 +134,24 @@ If you want to have some content that looks like a shortcode but not have Zola t
 you will need to escape it by using `{%/*` and `*/%}` instead of `{%` and `%}`. You won't need to escape
 anything else until the closing tag.
 
+### Invocation Count
+
+Every shortcode context is passed in a variable named `nth` that tracks how many times a particular shortcode has
+been invoked in a Markdown file. Given a shortcode `true_statement.html` template:
+
+```jinja2
+

{{ value }} is equal to {{ nth }}.

+``` + +It could be used in our Markdown as follows: + +```md +{{/* true_statement(value=1) */}} +{{/* true_statement(value=2) */}} +``` + +This is useful when implementing custom markup for features such as sidenotes or end notes. + ## Built-in shortcodes Zola comes with a few built-in shortcodes. If you want to override a default shortcode template, diff --git a/docs/content/documentation/content/taxonomies.md b/docs/content/documentation/content/taxonomies.md index 8cc2a84e..39e78a04 100644 --- a/docs/content/documentation/content/taxonomies.md +++ b/docs/content/documentation/content/taxonomies.md @@ -3,7 +3,62 @@ title = "Taxonomies" weight = 90 +++ -Zola has built-in support for taxonomies. +Zola has built-in support for taxonomies. Taxonomies are a way for users to group content according to user-defined categories. + +## Definitions + +- Taxonomy: A category that can be used to group content +- Term: A specific group within a taxonomy +- Value: A piece of content that can be associated with a term + +## Example: a movie website + +Imagine that you want to make a website to display information about various movies. In that case you could use the following taxonomies: + +- Director +- Genres +- Awards +- Release year + +Then at build time Zola can create pages for each taxonomy listing all of the known terms as well as pages for each term in a taxonomy, listing all of the pieces of content associated with that term. + +Imagine again we have the following movies: +``` +- Shape of water <--- Value + - Director <--- Taxonomy + - Guillermo Del Toro <--- Term + - Genres <--- Taxonomy + - Thriller <--- Term + - Drama <--- Term + - Awards <--- Taxonomy + - Golden globe <--- Term + - Academy award <--- Term + - BAFTA <--- Term + - Release year <--- Taxonomy + - 2017 <--- Term + +- The Room: <--- Value + - Director <--- Taxonomy + - Tommy Wiseau <--- Term + - Genres <--- Taxonomy + - Romance <--- Term + - Drama <--- Term + - Release Year <--- Taxonomy + - 2003 <--- Term + +- Bright <--- Value + - Director <--- Taxonomy + - David Ayer <--- Term + - Genres <--- Taxonomy + - Fantasy <--- Term + - Action <--- Term + - Awards <--- Taxonomy + - California on Location Awards <--- Term + - Release Year <--- Taxonomy + - 2017 <--- Term +``` + +In this example the page for `Release year` would include links to pages for both 2003 and 2017, where the page for 2017 would list both Shape of Water and Bright. ## Configuration @@ -23,16 +78,30 @@ Insert into the configuration file (config.toml): **Example 1:** (one language) ```toml -taxonomies = [ name = "categories", rss = true ] +taxonomies = [ + { name = "director", feed = true}, + { name = "genres", feed = true}, + { name = "awards", feed = true}, + { name = "release-year", feed = true}, +] ``` **Example 2:** (multilingual site) ```toml taxonomies = [ - {name = "tags", lang = "fr"}, - {name = "tags", lang = "eo"}, - {name = "tags", lang = "en"}, + {name = "director", feed = true, lang = "fr"}, + {name = "director", feed = true, lang = "eo"}, + {name = "director", feed = true, lang = "en"}, + {name = "genres", feed = true, lang = "fr"}, + {name = "genres", feed = true, lang = "eo"}, + {name = "genres", feed = true, lang = "en"}, + {name = "awards", feed = true, lang = "fr"}, + {name = "awards", feed = true, lang = "eo"}, + {name = "awards", feed = true, lang = "en"}, + {name = "release-year", feed = true, lang = "fr"}, + {name = "release-year", feed = true, lang = "eo"}, + {name = "release-year", feed = true, lang = "en"}, ] ``` @@ -44,11 +113,13 @@ Once the configuration is done, you can then set taxonomies in your content and ```toml +++ -title = "Writing a static-site generator in Rust" -date = 2019-08-15 +title = "Shape of water" +date = 2019-08-15 # date of the post, not the movie [taxonomies] -tags = ["rust", "web"] -categories = ["programming"] +director=["Guillermo Del Toro"] +genres=["Thriller","Drama"] +awards=["Golden Globe", "Academy award", "BAFTA"] +release-year = ["2017"] +++ ``` diff --git a/docs/content/documentation/getting-started/configuration.md b/docs/content/documentation/getting-started/configuration.md index 96ad924f..b97fd5b6 100644 --- a/docs/content/documentation/getting-started/configuration.md +++ b/docs/content/documentation/getting-started/configuration.md @@ -36,13 +36,6 @@ default_language = "en" # The site theme to use. theme = "" -# When set to "true", all code blocks are highlighted. -highlight_code = false - -# The theme to use for code highlighting. -# See below for list of allowed values. -highlight_theme = "base16-ocean-dark" - # When set to "true", a feed is automatically generated. generate_feed = false @@ -86,6 +79,9 @@ languages = [] # Sass files in theme directories are always compiled. compile_sass = false +# When set to "true", the generated HTML files are minified. +minify_html = false + # A list of glob patterns specifying asset files to ignore when the content # directory is processed. Defaults to none, which means that all asset files are # copied over to the `public` directory. @@ -96,6 +92,36 @@ ignored_content = [] # A list of directories used to search for additional `.sublime-syntax` files. extra_syntaxes = [] +# You can override the default output directory `public` by setting an another value. +# output_dir = "docs" + +# Configuration of the Markdown rendering +[markdown] +# When set to "true", all code blocks are highlighted. +highlight_code = false + +# The theme to use for code highlighting. +# See below for list of allowed values. +highlight_theme = "base16-ocean-dark" + +# When set to "true", emoji aliases translated to their corresponding +# Unicode emoji equivalent in the rendered Markdown files. (e.g.: :smile: => 😄) +render_emoji = false + +# Whether external links are to be opened in a new tab +# If this is true, a `rel="noopener"` will always automatically be added for security reasons +external_links_target_blank = false + +# Whether to set rel="nofollow" for all external links +external_links_no_follow = false + +# Whether to set rel="noreferrer" for all external links +external_links_no_referrer = false + +# Whether smart punctuation is enabled (changing quotes, dashes, dots in their typographic form) +# For example, `...` into `…`, `"quote"` into `“curly”` etc +smart_punctuation = false + # Configuration of the link checker. [link_checker] # Skip link checking for external URLs that start with these prefixes diff --git a/docs/content/documentation/templates/overview.md b/docs/content/documentation/templates/overview.md index 2150225e..24fc0b5f 100644 --- a/docs/content/documentation/templates/overview.md +++ b/docs/content/documentation/templates/overview.md @@ -109,7 +109,7 @@ If you only need the metadata of the section, you can pass `metadata_only=true` {% set section = get_section(path="blog/_index.md", metadata_only=true) %} ``` -### ` get_url` +### `get_url` Gets the permalink for the given path. If the path starts with `@/`, it will be treated as an internal link like the ones used in Markdown, starting from the root `content` directory. @@ -146,7 +146,7 @@ In the case of non-internal links, you can also add a cachebust of the format `? by passing `cachebust=true` to the `get_url` function. -### 'get_file_hash` +### `get_file_hash` Gets the hash digest for a static file. Supported hashes are SHA-256, SHA-384 (default) and SHA-512. Requires `path`. The `sha_type` key is optional and must be one of 256, 384 or 512. @@ -202,7 +202,7 @@ items: Array; See the [Taxonomies documentation](@/documentation/templates/taxonomies.md) for a full documentation of those types. ### `load_data` -Loads data from a file or URL. Supported file types include *toml*, *json* and *csv*. +Loads data from a file or URL. Supported file types include *toml*, *json*, *csv* and *bibtex*. Any other file type will be loaded as plain text. The `path` argument specifies the path to the data file relative to your base directory, where your `config.toml` is. @@ -213,7 +213,7 @@ As a security precaution, if this file is outside the main site directory, your ``` The optional `format` argument allows you to specify and override which data type is contained -within the file specified in the `path` argument. Valid entries are `toml`, `json`, `csv` +within the file specified in the `path` argument. Valid entries are `toml`, `json`, `csv`, `bibtex` or `plain`. If the `format` argument isn't specified, then the path extension is used. ```jinja2 @@ -251,6 +251,58 @@ template: } ``` +The `bibtex` format loads data into a structure matching the format used by the +[nom-bibtex crate](https://crates.io/crates/nom-bibtex). The following is an example of data +in bibtex format: + +``` +@preamble{"A bibtex preamble" # " this is."} + +@Comment{ + Here is a comment. +} + +Another comment! + +@string(name = "Vincent Prouillet") +@string(github = "https://github.com/getzola/zola") + +@misc {my_citation_key, + author= name, + title = "Zola", + note = "github: " # github +} } +``` + +The following is the json-equivalent format of the produced bibtex data structure: +```json +{ + "preambles": ["A bibtex preamble this is."], + "comments": ["Here is a comment.", "Another comment!"], + "variables": { + "name": "Vincent Prouillet", + "github": "https://github.com/getzola/zola" + }, + "bibliographies": [ + { + "entry_type": "misc", + "citation_key": "my_citation_key", + "tags": { + "author": "Vincent Prouillet", + "title": "Zola", + "note": "github: https://github.com/getzola/zola" + } + } + ] +} +``` + +Finally, the bibtex data can be accessed from the template as follows: +```jinja2 +{% set tags = data.bibliographies[0].tags %} +This was generated using {{ tags.title }}, authored by {{ tags.author }}. +``` + #### Remote content Instead of using a file, you can load data from a remote URL. This can be done by specifying a `url` parameter diff --git a/docs/content/documentation/templates/robots.md b/docs/content/documentation/templates/robots.md index a6041f9c..b6e8d684 100644 --- a/docs/content/documentation/templates/robots.md +++ b/docs/content/documentation/templates/robots.md @@ -11,4 +11,6 @@ and the default is what most sites want: ```jinja2 User-agent: * +Allow: / +Sitemap: {{ get_url(path="sitemap.xml") }} ``` diff --git a/docs/content/themes/DeepThought/screenshot.png b/docs/content/themes/DeepThought/screenshot.png deleted file mode 100644 index 43ee5877..00000000 Binary files a/docs/content/themes/DeepThought/screenshot.png and /dev/null differ diff --git a/docs/content/themes/Ergo/screenshot.png b/docs/content/themes/Ergo/screenshot.png deleted file mode 100644 index 6b1d959f..00000000 Binary files a/docs/content/themes/Ergo/screenshot.png and /dev/null differ diff --git a/docs/content/themes/Zulma/screenshot.png b/docs/content/themes/Zulma/screenshot.png deleted file mode 100644 index 5373667a..00000000 Binary files a/docs/content/themes/Zulma/screenshot.png and /dev/null differ diff --git a/docs/content/themes/_index.md b/docs/content/themes/_index.md deleted file mode 100644 index 5aa2e7b5..00000000 --- a/docs/content/themes/_index.md +++ /dev/null @@ -1,6 +0,0 @@ - -+++ -template = "themes.html" -sort_by = "date" -+++ - \ No newline at end of file diff --git a/docs/content/themes/after-dark/screenshot.png b/docs/content/themes/after-dark/screenshot.png deleted file mode 100644 index e34718d6..00000000 Binary files a/docs/content/themes/after-dark/screenshot.png and /dev/null differ diff --git a/docs/content/themes/anpu/screenshot.png b/docs/content/themes/anpu/screenshot.png deleted file mode 100644 index 9d472012..00000000 Binary files a/docs/content/themes/anpu/screenshot.png and /dev/null differ diff --git a/docs/content/themes/book/screenshot.png b/docs/content/themes/book/screenshot.png deleted file mode 100644 index d4dfd078..00000000 Binary files a/docs/content/themes/book/screenshot.png and /dev/null differ diff --git a/docs/content/themes/clean-blog/screenshot.png b/docs/content/themes/clean-blog/screenshot.png deleted file mode 100644 index 394d23ea..00000000 Binary files a/docs/content/themes/clean-blog/screenshot.png and /dev/null differ diff --git a/docs/content/themes/codinfox-zola/screenshot.png b/docs/content/themes/codinfox-zola/screenshot.png deleted file mode 100644 index 135ba71a..00000000 Binary files a/docs/content/themes/codinfox-zola/screenshot.png and /dev/null differ diff --git a/docs/content/themes/dinkleberg/screenshot.png b/docs/content/themes/dinkleberg/screenshot.png deleted file mode 100644 index c19e68e2..00000000 Binary files a/docs/content/themes/dinkleberg/screenshot.png and /dev/null differ diff --git a/docs/content/themes/docsascode-theme/screenshot.png b/docs/content/themes/docsascode-theme/screenshot.png deleted file mode 100644 index 2c27e8ef..00000000 Binary files a/docs/content/themes/docsascode-theme/screenshot.png and /dev/null differ diff --git a/docs/content/themes/even/screenshot.png b/docs/content/themes/even/screenshot.png deleted file mode 100644 index 1acdd623..00000000 Binary files a/docs/content/themes/even/screenshot.png and /dev/null differ diff --git a/docs/content/themes/feather/screenshot.png b/docs/content/themes/feather/screenshot.png deleted file mode 100644 index bb54fef9..00000000 Binary files a/docs/content/themes/feather/screenshot.png and /dev/null differ diff --git a/docs/content/themes/float/screenshot.png b/docs/content/themes/float/screenshot.png deleted file mode 100644 index c4e7c1e3..00000000 Binary files a/docs/content/themes/float/screenshot.png and /dev/null differ diff --git a/docs/content/themes/hallo/screenshot.png b/docs/content/themes/hallo/screenshot.png deleted file mode 100644 index da242a0b..00000000 Binary files a/docs/content/themes/hallo/screenshot.png and /dev/null differ diff --git a/docs/content/themes/hyde/screenshot.png b/docs/content/themes/hyde/screenshot.png deleted file mode 100644 index 5e218059..00000000 Binary files a/docs/content/themes/hyde/screenshot.png and /dev/null differ diff --git a/docs/content/themes/juice/screenshot.png b/docs/content/themes/juice/screenshot.png deleted file mode 100644 index 3150b215..00000000 Binary files a/docs/content/themes/juice/screenshot.png and /dev/null differ diff --git a/docs/content/themes/lightspeed/screenshot.png b/docs/content/themes/lightspeed/screenshot.png deleted file mode 100644 index 6070f1ee..00000000 Binary files a/docs/content/themes/lightspeed/screenshot.png and /dev/null differ diff --git a/docs/content/themes/oceanic-zen/screenshot.png b/docs/content/themes/oceanic-zen/screenshot.png deleted file mode 100644 index 430750ff..00000000 Binary files a/docs/content/themes/oceanic-zen/screenshot.png and /dev/null differ diff --git a/docs/content/themes/sam/screenshot.png b/docs/content/themes/sam/screenshot.png deleted file mode 100644 index 81e8c683..00000000 Binary files a/docs/content/themes/sam/screenshot.png and /dev/null differ diff --git a/docs/content/themes/simple-dev-blog/screenshot.png b/docs/content/themes/simple-dev-blog/screenshot.png deleted file mode 100644 index c5ecdfab..00000000 Binary files a/docs/content/themes/simple-dev-blog/screenshot.png and /dev/null differ diff --git a/docs/content/themes/slim/screenshot.png b/docs/content/themes/slim/screenshot.png deleted file mode 100644 index 5c5c8522..00000000 Binary files a/docs/content/themes/slim/screenshot.png and /dev/null differ diff --git a/docs/content/themes/solar-theme-zola/screenshot.png b/docs/content/themes/solar-theme-zola/screenshot.png deleted file mode 100644 index 05186b54..00000000 Binary files a/docs/content/themes/solar-theme-zola/screenshot.png and /dev/null differ diff --git a/docs/content/themes/toucan/screenshot.png b/docs/content/themes/toucan/screenshot.png deleted file mode 100644 index 87287b0f..00000000 Binary files a/docs/content/themes/toucan/screenshot.png and /dev/null differ diff --git a/docs/content/themes/zerm/screenshot.png b/docs/content/themes/zerm/screenshot.png deleted file mode 100644 index 18513991..00000000 Binary files a/docs/content/themes/zerm/screenshot.png and /dev/null differ diff --git a/docs/content/themes/zola-henry/screenshot.png b/docs/content/themes/zola-henry/screenshot.png deleted file mode 100644 index f12cfa58..00000000 Binary files a/docs/content/themes/zola-henry/screenshot.png and /dev/null differ diff --git a/docs/content/themes/zola-paper/screenshot.png b/docs/content/themes/zola-paper/screenshot.png deleted file mode 100644 index 91973a42..00000000 Binary files a/docs/content/themes/zola-paper/screenshot.png and /dev/null differ diff --git a/docs/content/themes/zola-pickles/screenshot.png b/docs/content/themes/zola-pickles/screenshot.png deleted file mode 100644 index 0e35c36c..00000000 Binary files a/docs/content/themes/zola-pickles/screenshot.png and /dev/null differ diff --git a/docs/content/themes/zola-theme-hikari/screenshot.png b/docs/content/themes/zola-theme-hikari/screenshot.png deleted file mode 100644 index 359baf41..00000000 Binary files a/docs/content/themes/zola-theme-hikari/screenshot.png and /dev/null differ diff --git a/src/cli.rs b/src/cli.rs index bc14a5ab..c76f56dd 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -44,7 +44,6 @@ pub fn build_cli() -> App<'static, 'static> { Arg::with_name("output_dir") .short("o") .long("output-dir") - .default_value("public") .takes_value(true) .help("Outputs the generated site in the given path"), Arg::with_name("drafts") @@ -68,7 +67,6 @@ pub fn build_cli() -> App<'static, 'static> { Arg::with_name("output_dir") .short("o") .long("output-dir") - .default_value("public") .takes_value(true) .help("Outputs the generated site in the given path"), Arg::with_name("base_url") diff --git a/src/cmd/build.rs b/src/cmd/build.rs index 3a2c7634..7a0e85cb 100644 --- a/src/cmd/build.rs +++ b/src/cmd/build.rs @@ -9,11 +9,13 @@ pub fn build( root_dir: &Path, config_file: &Path, base_url: Option<&str>, - output_dir: &Path, + output_dir: Option<&Path>, include_drafts: bool, ) -> Result<()> { let mut site = Site::new(root_dir, config_file)?; - site.set_output_path(output_dir); + if let Some(output_dir) = output_dir { + site.set_output_path(output_dir); + } if let Some(b) = base_url { site.set_base_url(b.to_string()); } diff --git a/src/cmd/init.rs b/src/cmd/init.rs index 07f230e5..1a381737 100644 --- a/src/cmd/init.rs +++ b/src/cmd/init.rs @@ -15,13 +15,14 @@ base_url = "%BASE_URL%" # Whether to automatically compile all Sass files in the sass directory compile_sass = %COMPILE_SASS% +# Whether to build a search index to be used later on by a JavaScript library +build_search_index = %SEARCH% + +[markdown] # Whether to do syntax highlighting # Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola highlight_code = %HIGHLIGHT% -# Whether to build a search index to be used later on by a JavaScript library -build_search_index = %SEARCH% - [extra] # Put all your custom variables here "#; diff --git a/src/cmd/serve.rs b/src/cmd/serve.rs index 2f2f299a..519ac206 100644 --- a/src/cmd/serve.rs +++ b/src/cmd/serve.rs @@ -22,11 +22,11 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. use std::fs::{read_dir, remove_dir_all}; +use std::net::{SocketAddrV4, TcpListener}; use std::path::{Path, PathBuf}; use std::sync::mpsc::channel; use std::thread; use std::time::{Duration, Instant}; -use std::net::{SocketAddrV4, TcpListener}; use hyper::header; use hyper::service::{make_service_fn, service_fn}; @@ -173,7 +173,7 @@ fn create_new_site( root_dir: &Path, interface: &str, interface_port: u16, - output_dir: &Path, + output_dir: Option<&Path>, base_url: &str, config_file: &Path, include_drafts: bool, @@ -192,7 +192,9 @@ fn create_new_site( site.enable_serve_mode(); site.set_base_url(base_url); - site.set_output_path(output_dir); + if let Some(output_dir) = output_dir { + site.set_output_path(output_dir); + } if include_drafts { site.include_drafts(); } @@ -212,7 +214,7 @@ pub fn serve( root_dir: &Path, interface: &str, interface_port: u16, - output_dir: &Path, + output_dir: Option<&Path>, base_url: &str, config_file: &Path, watch_only: bool, @@ -236,7 +238,7 @@ pub fn serve( // Stop right there if we can't bind to the address let bind_address: SocketAddrV4 = address.parse().unwrap(); if (TcpListener::bind(&bind_address)).is_err() { - return Err(format!("Cannot start server on address {}.", address))?; + return Err(format!("Cannot start server on address {}.", address).into()); } // An array of (path, bool, bool) where the path should be watched for changes, and the boolean value @@ -277,7 +279,7 @@ pub fn serve( let ws_port = site.live_reload; let ws_address = format!("{}:{}", interface, ws_port.unwrap()); - let output_path = Path::new(output_dir).to_path_buf(); + let output_path = site.output_path.clone(); // output path is going to need to be moved later on, so clone it for the // http closure to avoid contention. @@ -440,10 +442,7 @@ pub fn serve( loop { match rx.recv() { Ok(event) => { - let can_do_fast_reload = match event { - Remove(_) => false, - _ => true, - }; + let can_do_fast_reload = !matches!(event, Remove(_)); match event { // Intellij does weird things on edit, chmod is there to count those changes @@ -503,10 +502,8 @@ pub fn serve( site = s; } } - } else { - if let Some(s) = recreate_site() { - site = s; - } + } else if let Some(s) = recreate_site() { + site = s; } } (ChangeKind::Templates, partial_path) => { diff --git a/src/main.rs b/src/main.rs index 3eec3ba3..2936354c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ use std::env; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::time::Instant; use utils::net::{get_available_port, port_is_available}; @@ -37,12 +37,12 @@ fn main() { ("build", Some(matches)) => { console::info("Building site..."); let start = Instant::now(); - let output_dir = PathBuf::from(matches.value_of("output_dir").unwrap()); + let output_dir = matches.value_of("output_dir").map(|output_dir| Path::new(output_dir)); match cmd::build( &root_dir, &config_file, matches.value_of("base_url"), - &output_dir, + output_dir, matches.is_present("drafts"), ) { Ok(()) => console::report_elapsed_time(start), @@ -80,14 +80,14 @@ fn main() { ::std::process::exit(1); } } - let output_dir = PathBuf::from(matches.value_of("output_dir").unwrap()); + let output_dir = matches.value_of("output_dir").map(|output_dir| Path::new(output_dir)); let base_url = matches.value_of("base_url").unwrap(); console::info("Building site..."); match cmd::serve( &root_dir, interface, port, - &output_dir, + output_dir, base_url, &config_file, watch_only, diff --git a/test_site/content/secret_section/_index.md b/test_site/content/secret_section/_index.md new file mode 100644 index 00000000..2f0a5945 --- /dev/null +++ b/test_site/content/secret_section/_index.md @@ -0,0 +1,4 @@ ++++ +title="Drafted section" +draft=true ++++ diff --git a/test_site/content/secret_section/draft-page.md b/test_site/content/secret_section/draft-page.md new file mode 100644 index 00000000..30b7f66f --- /dev/null +++ b/test_site/content/secret_section/draft-page.md @@ -0,0 +1,4 @@ ++++ +title="drafted page in drafted section" +draft=true ++++ diff --git a/test_site/content/secret_section/page.md b/test_site/content/secret_section/page.md new file mode 100644 index 00000000..e05a2e13 --- /dev/null +++ b/test_site/content/secret_section/page.md @@ -0,0 +1,3 @@ ++++ +title="non draft page" ++++ diff --git a/test_site/content/secret_section/secret_sub_section/_index.md b/test_site/content/secret_section/secret_sub_section/_index.md new file mode 100644 index 00000000..8bf885a5 --- /dev/null +++ b/test_site/content/secret_section/secret_sub_section/_index.md @@ -0,0 +1,3 @@ ++++ +title="subsection of a secret section" ++++ diff --git a/test_site/content/secret_section/secret_sub_section/hello.md b/test_site/content/secret_section/secret_sub_section/hello.md new file mode 100644 index 00000000..da51880f --- /dev/null +++ b/test_site/content/secret_section/secret_sub_section/hello.md @@ -0,0 +1,3 @@ ++++ +title="Is anyone ever going to read this?" ++++