diff --git a/Cargo.lock b/Cargo.lock index 021c7c6..0d92dd2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,7 +9,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", - "getrandom", "once_cell", "version_check", ] @@ -170,6 +169,28 @@ dependencies = [ "winapi", ] +[[package]] +name = "chrono-tz" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1369bc6b9e9a7dfdae2055f6ec151fe9c554a9d23d357c0237cee2e25eaabb7" +dependencies = [ + "chrono", + "chrono-tz-build", + "phf 0.11.2", +] + +[[package]] +name = "chrono-tz-build" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2f5ebdc942f57ed96d560a6d1a459bae5851102a25d5bf89dc04ae453e31ecf" +dependencies = [ + "parse-zoneinfo", + "phf 0.11.2", + "phf_codegen 0.11.2", +] + [[package]] name = "clap" version = "4.3.23" @@ -214,6 +235,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "core-foundation-sys" version = "0.8.4" @@ -241,15 +268,19 @@ dependencies = [ [[package]] name = "cssparser" -version = "0.31.2" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b3df4f93e5fbbe73ec01ec8d3f68bba73107993a5b1e7519273c32db9b0d5be" +checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" dependencies = [ "cssparser-macros", "dtoa-short", - "itoa", - "phf", + "itoa 0.4.8", + "matches", + "phf 0.8.0", + "proc-macro2", + "quote", "smallvec", + "syn 1.0.109", ] [[package]] @@ -268,11 +299,19 @@ version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ + "convert_case", "proc-macro2", "quote", + "rustc_version", "syn 1.0.109", ] +[[package]] +name = "deunicode" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95203a6a50906215a502507c0f879a0ce7ff205a6111e2db2a5ef8e4bb92e43" + [[package]] name = "diff" version = "0.1.13" @@ -305,10 +344,13 @@ dependencies = [ ] [[package]] -name = "ego-tree" -version = "0.6.2" +name = "encoding_rs" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68a4904193147e0a8dec3314640e6db742afd5f6e634f428a6af230d9b3591" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] [[package]] name = "env_logger" @@ -351,14 +393,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" [[package]] -name = "futf" -version = "0.1.5" +name = "fnv" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" -dependencies = [ - "mac", - "new_debug_unreachable", -] +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fxhash" @@ -380,12 +418,14 @@ dependencies = [ ] [[package]] -name = "getopts" -version = "0.2.21" +name = "getrandom" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "unicode-width", + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] @@ -396,7 +436,31 @@ checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "globset" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags 1.3.2", + "ignore", + "walkdir", ] [[package]] @@ -413,6 +477,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + [[package]] name = "hermit-abi" version = "0.3.2" @@ -420,17 +493,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] -name = "html5ever" -version = "0.26.0" +name = "humansize" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" dependencies = [ - "log", - "mac", - "markup5ever", - "proc-macro2", - "quote", - "syn 1.0.109", + "libm", ] [[package]] @@ -462,6 +530,23 @@ dependencies = [ "cc", ] +[[package]] +name = "ignore" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" +dependencies = [ + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + [[package]] name = "io-lifetimes" version = "1.0.11" @@ -484,6 +569,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + [[package]] name = "itoa" version = "1.0.9" @@ -499,12 +590,30 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + [[package]] name = "linereader" version = "0.4.0" @@ -526,16 +635,6 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" -[[package]] -name = "lock_api" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.20" @@ -543,36 +642,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] -name = "mac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" - -[[package]] -name = "markup5ever" -version = "0.11.0" +name = "lol_html" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +checksum = "10662f7aad081ec900fd735be33076da75e0389400277dc3734e2b0aa02bb115" dependencies = [ - "log", - "phf", - "phf_codegen", - "string_cache", - "string_cache_codegen", - "tendril", + "bitflags 2.4.0", + "cfg-if", + "cssparser", + "encoding_rs", + "hashbrown", + "lazy_static", + "lazycell", + "memchr", + "mime", + "safemem", + "selectors", + "thiserror", ] [[package]] -name = "markup5ever_rcdom" -version = "0.2.0" +name = "matches" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9521dd6750f8e80ee6c53d65e2e4656d7de37064f3a7a5d2d11d05df93839c2" -dependencies = [ - "html5ever", - "markup5ever", - "tendril", - "xml5ever", -] +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "mdbook" @@ -605,20 +698,18 @@ name = "mdbook-i18n-helpers" version = "0.2.2" dependencies = [ "anyhow", - "ego-tree", - "markup5ever", - "markup5ever_rcdom", + "lol_html", "mdbook", "polib", "pretty_assertions", "pulldown-cmark", "pulldown-cmark-to-cmark", "regex", - "scraper", "semver", "serde", "serde_json", "tempfile", + "tera", "thiserror", ] @@ -629,10 +720,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] -name = "new_debug_unreachable" -version = "1.0.4" +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "nodrop" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" [[package]] name = "normpath" @@ -670,27 +767,19 @@ dependencies = [ ] [[package]] -name = "parking_lot" -version = "0.12.1" +name = "parse-zoneinfo" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" dependencies = [ - "lock_api", - "parking_lot_core", + "regex", ] [[package]] -name = "parking_lot_core" -version = "0.9.8" +name = "percent-encoding" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", -] +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" @@ -738,43 +827,72 @@ dependencies = [ [[package]] name = "phf" -version = "0.10.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" dependencies = [ "phf_macros", - "phf_shared", + "phf_shared 0.8.0", "proc-macro-hack", ] +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_shared 0.11.2", +] + [[package]] name = "phf_codegen" -version = "0.10.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_codegen" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +dependencies = [ + "phf_generator 0.11.2", + "phf_shared 0.11.2", ] [[package]] name = "phf_generator" -version = "0.10.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" dependencies = [ - "phf_shared", - "rand", + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared 0.11.2", + "rand 0.8.5", ] [[package]] name = "phf_macros" -version = "0.10.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.8.0", + "phf_shared 0.8.0", "proc-macro-hack", "proc-macro2", "quote", @@ -783,9 +901,18 @@ dependencies = [ [[package]] name = "phf_shared" -version = "0.10.0" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" dependencies = [ "siphasher", ] @@ -865,6 +992,20 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + [[package]] name = "rand" version = "0.8.5" @@ -872,8 +1013,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", ] [[package]] @@ -883,7 +1034,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", ] [[package]] @@ -892,7 +1052,25 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.10", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", ] [[package]] @@ -933,6 +1111,15 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.37.23" @@ -967,45 +1154,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] -name = "scopeguard" -version = "1.2.0" +name = "safemem" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" [[package]] -name = "scraper" -version = "0.17.1" +name = "same-file" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c95a930e03325234c18c7071fd2b60118307e025d6fff3e12745ffbf63a3d29c" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ - "ahash", - "cssparser", - "ego-tree", - "getopts", - "html5ever", - "once_cell", - "selectors", - "smallvec", - "tendril", + "winapi-util", ] [[package]] name = "selectors" -version = "0.25.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb30575f3638fc8f6815f448d50cb1a2e255b0897985c8c59f4d37b72a07b06" +checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" dependencies = [ - "bitflags 2.4.0", + "bitflags 1.3.2", "cssparser", "derive_more", "fxhash", "log", - "new_debug_unreachable", - "phf", - "phf_codegen", + "matches", + "phf 0.8.0", + "phf_codegen 0.8.0", "precomputed-hash", "servo_arc", "smallvec", + "thin-slice", ] [[package]] @@ -1040,17 +1220,18 @@ version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" dependencies = [ - "itoa", + "itoa 1.0.9", "ryu", "serde", ] [[package]] name = "servo_arc" -version = "0.3.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d036d71a959e00c77a63538b90a6c2390969f9772b096ea837205c6bd0491a44" +checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" dependencies = [ + "nodrop", "stable_deref_trait", ] @@ -1077,6 +1258,15 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "slug" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bc762e6a4b6c6fcaade73e77f9ebc6991b676f88bb2358bddb56560f073373" +dependencies = [ + "deunicode", +] + [[package]] name = "smallvec" version = "1.11.0" @@ -1089,32 +1279,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot", - "phf_shared", - "precomputed-hash", - "serde", -] - -[[package]] -name = "string_cache_codegen" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro2", - "quote", -] - [[package]] name = "strsim" version = "0.10.0" @@ -1157,14 +1321,25 @@ dependencies = [ ] [[package]] -name = "tendril" -version = "0.4.3" +name = "tera" +version = "1.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +checksum = "970dff17c11e884a4a09bc76e3a17ef71e01bb13447a11e85226e254fe6d10b8" dependencies = [ - "futf", - "mac", - "utf-8", + "chrono", + "chrono-tz", + "globwalk", + "humansize", + "lazy_static", + "percent-encoding", + "pest", + "pest_derive", + "rand 0.8.5", + "regex", + "serde", + "serde_json", + "slug", + "unic-segment", ] [[package]] @@ -1186,6 +1361,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "thin-slice" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" + [[package]] name = "thiserror" version = "1.0.47" @@ -1206,6 +1387,16 @@ dependencies = [ "syn 2.0.29", ] +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "toml" version = "0.5.11" @@ -1233,6 +1424,56 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-segment" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23" +dependencies = [ + "unic-ucd-segment", +] + +[[package]] +name = "unic-ucd-segment" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + [[package]] name = "unicase" version = "2.7.0" @@ -1248,18 +1489,6 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - [[package]] name = "utf8parse" version = "0.2.1" @@ -1272,6 +1501,22 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1438,17 +1683,6 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" -[[package]] -name = "xml5ever" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4034e1d05af98b51ad7214527730626f019682d797ba38b51689212118d8e650" -dependencies = [ - "log", - "mac", - "markup5ever", -] - [[package]] name = "yansi" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index fb87253..b89dbdf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,18 +11,16 @@ description = "Plugins for a mdbook translation workflow based on Gettext." [dependencies] anyhow = "1.0.68" -ego-tree = "0.6.2" -markup5ever = "0.11.0" -markup5ever_rcdom = "0.2.0" +lol_html = "1.2.0" mdbook = { version = "0.4.25", default-features = false } polib = "0.2.0" pulldown-cmark = { version = "0.9.2", default-features = false } pulldown-cmark-to-cmark = "10.0.4" regex = "1.9.4" -scraper = "0.17.1" semver = "1.0.16" serde = "1.0.130" serde_json = "1.0.91" +tera = "1.19.1" thiserror = "1.0.30" [dev-dependencies] diff --git a/src/custom_component_renderer/book_directory_renderer.rs b/src/custom_component_renderer/book_directory_renderer.rs index 271654a..cc40187 100644 --- a/src/custom_component_renderer/book_directory_renderer.rs +++ b/src/custom_component_renderer/book_directory_renderer.rs @@ -1,12 +1,11 @@ -use super::dom_manipulator::NodeManipulator; use super::error::RendererError; -use super::Component; +use super::CustomComponent; use crate::custom_component_renderer::error::Result; -use scraper::{Html, Selector}; +use lol_html::html_content::ContentType; +use lol_html::{element, RewriteStrSettings}; use std::fs; use std::io::{Read, Write}; use std::path::{Path, PathBuf}; -use std::process::Command; use std::collections::BTreeMap; @@ -17,138 +16,120 @@ use serde::Deserialize; pub struct I18nConfiguration { pub languages: BTreeMap, pub default_language: Option, + #[serde(default)] + pub translate_all_languages: bool, } -/// Configuration from the book.toml file. -pub struct RendererConfiguration { - pub i18n: I18nConfiguration, - pub destination: PathBuf, - pub current_language: Option, - pub root: PathBuf, -} - -impl RendererConfiguration { - pub fn new( - i18n: I18nConfiguration, - destination: PathBuf, - current_language: Option, - root: PathBuf, - ) -> Self { - RendererConfiguration { - i18n, - destination, - current_language, - root, - } - } -} - -pub struct RenderingContext { +pub struct RenderingContext<'a> { pub path: PathBuf, - pub destination: PathBuf, pub language: String, + pub i18_config: &'a I18nConfiguration, + pub language_to_rendered_path: BTreeMap, } -impl RenderingContext { - fn new(path: PathBuf, destination: PathBuf, language: String) -> Self { - RenderingContext { +impl<'a> RenderingContext<'a> { + fn new( + path: PathBuf, + book_dir: PathBuf, + language: String, + i18_config: &'a I18nConfiguration, + ) -> Result { + let html_dir = book_dir.join("html"); + let mut language_to_rendered_path: BTreeMap = BTreeMap::new(); + for identifier in i18_config.languages.keys() { + let mut relative_path = path.strip_prefix(&html_dir)?.to_owned(); + if let Ok(without_lang) = relative_path.strip_prefix(&language) { + relative_path = without_lang.to_owned(); + } + if Some(identifier) != i18_config.default_language.as_ref() { + relative_path = Path::new(identifier).join(relative_path).to_owned(); + } + language_to_rendered_path.insert( + identifier.clone(), + Path::new("/").join(relative_path).to_owned(), + ); + } + Ok(RenderingContext { path, - destination, language, - } - } - - pub fn rendered_path(&self) -> String { - let mut relative_path = PathBuf::from( - self.path - .strip_prefix(&self.destination) - .expect("Invalid path"), - ); - if let Ok(stripped) = relative_path.strip_prefix(&self.language) { - relative_path = stripped.to_owned(); - } - String::from( - relative_path - .to_str() - .expect("Failed to convert path to rendered path"), - ) + i18_config, + language_to_rendered_path, + }) } } pub(crate) struct BookDirectoryRenderer { - config: RendererConfiguration, - components: Vec>, + config: I18nConfiguration, + book: mdbook::MDBook, + book_dir: PathBuf, + components: Vec, languages_paths: BTreeMap, } impl BookDirectoryRenderer { - pub(crate) fn new(config: RendererConfiguration) -> BookDirectoryRenderer { - let default_language = config.i18n.default_language.clone(); + pub(crate) fn new( + config: I18nConfiguration, + book: mdbook::MDBook, + book_dir: PathBuf, + ) -> BookDirectoryRenderer { + let default_language = config.default_language.clone(); let languages_paths = config - .i18n .languages .keys() .filter(|language| { default_language.is_none() || *language != default_language.as_ref().unwrap() }) - .map(|language| { - ( - language.clone(), - config.destination.join("html").join(language), - ) - }) + .map(|language| (language.clone(), book_dir.join("html").join(language))) .collect::>(); BookDirectoryRenderer { config, + book, languages_paths, + book_dir, components: Vec::new(), } } - pub fn translate(&self) -> Result<()> { - let default_language = &self.config.i18n.default_language; - for (identifier, _) in &self.config.i18n.languages { + pub fn translate(&mut self) -> Result<()> { + let default_language = &self.config.default_language; + let original_language = self.book.config.book.language.clone(); + let book_dir = self.book_dir.as_path(); + + for identifier in self.config.languages.keys() { if let Some(default_language) = default_language { if default_language == identifier { continue; } } - let destination = self.config.destination.as_path().display().to_string(); - let book_folder = self.config.root.as_path().display().to_string(); - - Command::new("mdbook") - .arg("build") - .arg(&book_folder) - .arg("-d") - .arg(&format!("{destination}/{identifier}")) - .env("MDBOOK_BOOK__LANGUAGE", identifier) - .env( - "MDBOOK_OUTPUT__HTML__SITE_URL", - &format!("/comprehensive-rust/{identifier}/"), - ) - .output()?; + let translation_path = book_dir.join(identifier); + self.book.config.book.language = Some(identifier.clone()); + self.book.config.book.multilingual = true; + self.book.config.build.build_dir = translation_path; + self.book.build()?; std::fs::rename( - &format!("{destination}/{identifier}/html"), - &format!("{destination}/html/{identifier}"), + book_dir.join(identifier).join("html"), + book_dir.join("html").join(identifier), )?; } - + self.book.config.book.language = original_language; + self.book.config.build.build_dir = book_dir.to_owned(); Ok(()) } pub(crate) fn render_book(&mut self) -> Result<()> { - if !self.config.destination.is_dir() { + let html_dir = self.book_dir.join("html"); + if !html_dir.is_dir() { return Err(RendererError::InvalidPath(format!( "{:?} is not a directory", - self.config.destination + self.book_dir ))); } - self.render_book_directory(&self.config.destination.clone()) + self.render_book_directory(&html_dir) } - pub(crate) fn add_component(&mut self, component: Box) { + pub(crate) fn add_component(&mut self, component: CustomComponent) { self.components.push(component); } @@ -158,35 +139,35 @@ impl BookDirectoryRenderer { return language.clone(); } } - self.config - .i18n - .default_language - .clone() - .unwrap_or_default() + self.config.default_language.clone().unwrap_or_default() } fn render_components(&mut self, file_content: &str, path: &Path) -> Result { - let path_buf = path.to_owned(); - let destination = self.config.destination.join("html"); - let language = self.extract_language_from_path(&path_buf); - - let rendering_context = RenderingContext::new(path_buf, destination, language); - let mut document = Html::parse_document(file_content); - for custom_component in &mut self.components { - let mut node_ids = Vec::new(); - - let selector = Selector::parse(&custom_component.identifier()) - .map_err(|err| RendererError::InvalidIdentifier(err.to_string()))?; - for node in document.select(&selector) { - node_ids.push(node.id()); - } - let tree = &mut document.tree; - for id in node_ids { - let dom_manipulator = NodeManipulator::new(tree, id); - custom_component.render(dom_manipulator, &self.config, &rendering_context)?; - } - } - Ok(document.html()) + let rendering_context = RenderingContext::new( + path.to_owned(), + self.book_dir.clone(), + self.extract_language_from_path(path), + &self.config, + )?; + let custom_components_handlers = self + .components + .iter() + .map(|component| { + element!(component.component_name(), |el| { + let rendered = component.render(&rendering_context)?; + el.replace(&rendered, ContentType::Html); + Ok(()) + }) + }) + .collect(); + let output = lol_html::rewrite_str( + file_content, + RewriteStrSettings { + element_content_handlers: custom_components_handlers, + ..RewriteStrSettings::default() + }, + )?; + Ok(output) } fn process_file(&mut self, path: &Path) -> Result<()> { @@ -198,9 +179,10 @@ impl BookDirectoryRenderer { let mut file = fs::File::open(path)?; file.read_to_string(&mut file_content)?; } - let output_html = self.render_components(&file_content, path)?; - let mut file = fs::File::create(path)?; - file.write_all(output_html.as_bytes())?; + + let output = self.render_components(&file_content, path)?; + let mut output_file = fs::File::create(path)?; + output_file.write_all(output.as_bytes())?; Ok(()) } @@ -220,45 +202,79 @@ impl BookDirectoryRenderer { #[cfg(test)] mod tests { + use crate::custom_component_renderer::standard_templates; + + const FAKE_BOOK_TOML: &str = r#" + [book] + src = "src" + + [rust] + edition = "2021" + + [build] + extra-watch-dirs = ["po", "third_party"] + + [preprocessor.gettext] + after = ["links"] + + [preprocessor.svgbob] + renderers = ["html"] + after = ["gettext"] + class = "bob" + + [output.html] + curly-quotes = true + + [output.i18n-helpers] + default_language = "en" + translate_all_languages = false + + [output.i18n-helpers.languages] + "en" = "English" + "es" = "Spanish (Español)" + "ko" = "Korean (한국어)" + "pt-BR" = "Brazilian Portuguese (Português do Brasil)" + "#; + #[test] fn test_render_book() { use super::*; - use crate::custom_components::test_component::TestComponent; use std::fs::File; use tempfile::tempdir; - const INITIAL_HTML: &[u8] = b"
TOREMOVE
"; + const INITIAL_HTML: &[u8] = b""; let dir = tempdir().unwrap(); - std::fs::write(dir.path().join("test.html"), INITIAL_HTML) + std::fs::create_dir(dir.path().join("html")).expect("Failed to create html directory"); + std::fs::create_dir(dir.path().join("src")).expect("Failed to create src directory"); + + std::fs::write(dir.path().join("html/test.html"), INITIAL_HTML) .expect("Failed to write initial html"); + std::fs::write(dir.path().join("book.toml"), FAKE_BOOK_TOML) + .expect("Failed to write initial book.toml"); + std::fs::write(dir.path().join("src/SUMMARY.md"), "") + .expect("Failed to write initial SUMMARY.md"); let mut languages = BTreeMap::new(); languages.insert(String::from("en"), String::from("English")); languages.insert(String::from("fr"), String::from("French")); - let mock_config = RendererConfiguration::new( - I18nConfiguration { - languages, - default_language: Some(String::from("en")), - }, - dir.path().to_owned(), - Some(String::from("en")), - dir.path().to_owned(), - ); - - let mut renderer = BookDirectoryRenderer::new(mock_config); - let test_component = Box::new(TestComponent::new()); - renderer.add_component(test_component); + let mock_config = I18nConfiguration { + languages, + default_language: Some(String::from("en")), + translate_all_languages: true, + }; + let mdbook = mdbook::MDBook::load(&dir.path()).expect("Failed to load book"); + + let mut renderer = BookDirectoryRenderer::new(mock_config, mdbook, dir.path().to_owned()); + renderer.add_component(standard_templates::create_language_picker_component()); renderer.render_book().expect("Failed to render book"); let mut output = String::new(); - let mut file = File::open(dir.path().join("test.html")).unwrap(); + let mut file = File::open(dir.path().join("html/test.html")).unwrap(); file.read_to_string(&mut output).unwrap(); - const EXPECTED: &str = "
  • en: English
  • fr: French
"; + const EXPECTED: &str = "\n\n\n\n\n\n"; - let output_document = Html::parse_document(&output); - let expected_document = Html::parse_document(EXPECTED); - assert_eq!(output_document, expected_document); + assert_eq!(output, EXPECTED); } } diff --git a/src/custom_component_renderer/component_trait.rs b/src/custom_component_renderer/component_trait.rs deleted file mode 100644 index 66b89a5..0000000 --- a/src/custom_component_renderer/component_trait.rs +++ /dev/null @@ -1,16 +0,0 @@ -use super::dom_manipulator::NodeManipulator; -use super::RenderingContext; -use crate::custom_component_renderer::error::Result; -use crate::RendererConfiguration; - -pub trait Component { - /// Returns the identifier of the component. ie `` -> `i18n-helpers` - fn identifier(&self) -> String; - - fn render( - &mut self, - node: NodeManipulator<'_>, - config: &RendererConfiguration, - rendering_context: &RenderingContext, - ) -> Result<()>; -} diff --git a/src/custom_component_renderer/custom_component.rs b/src/custom_component_renderer/custom_component.rs new file mode 100644 index 0000000..78daa94 --- /dev/null +++ b/src/custom_component_renderer/custom_component.rs @@ -0,0 +1,92 @@ +use std::cell::RefCell; +use std::collections::{BTreeMap, HashMap}; +use std::path::PathBuf; + +use serde_json::{from_value, to_value}; +use tera::Tera; + +use crate::Result; + +use super::RenderingContext; + +pub struct CustomComponent { + template: String, + name: String, + id: RefCell, +} + +impl CustomComponent { + pub fn new(name: &str, template: &str) -> CustomComponent { + CustomComponent { + name: String::from(name), + template: String::from(template), + id: RefCell::new(0), + } + } + + fn make_language_to_rendered_path_function( + language_to_rendered_path: BTreeMap, + ) -> impl tera::Function { + Box::new( + move |args: &HashMap| -> tera::Result { + match args.get("identifier") { + Some(val) => match from_value::(val.clone()) { + Ok(v) => Ok(to_value(language_to_rendered_path.get(&v).ok_or_else( + || { + tera::Error::from( + "No language with the provided indentifier was found", + ) + }, + )?)?), + Err(_) => Err("Failed to deserialize argument".into()), + }, + None => Err("Identifier argument not provided".into()), + } + }, + ) + } + + fn create_context( + &self, + tera_obj: &mut Tera, + rendering_context: &RenderingContext, + ) -> tera::Context { + let id = self.id.replace_with(|&mut id| id + 1); + let mut context = tera::Context::new(); + context.insert("id", &id); + if rendering_context.language != "en" { + println!("LANGUAGE {}", rendering_context.language); + } + + context.insert("language", &rendering_context.language); + context.insert( + "default_language", + &rendering_context.i18_config.default_language, + ); + context.insert("languages", &rendering_context.i18_config.languages); + context.insert("path", &rendering_context.path); + + let language_to_rendered_path = rendering_context.language_to_rendered_path.clone(); + let language_to_rendered_path_function = + CustomComponent::make_language_to_rendered_path_function(language_to_rendered_path); + tera_obj.register_function( + "language_to_rendered_path", + language_to_rendered_path_function, + ); + + context + } + + pub fn render(&self, rendering_context: &RenderingContext) -> Result { + let mut tera = Tera::default(); + tera.add_raw_template("template", &self.template)?; + + let context = self.create_context(&mut tera, rendering_context); + let output = tera.render("template", &context)?; + Ok(output) + } + + pub fn component_name(&self) -> String { + self.name.clone() + } +} diff --git a/src/custom_component_renderer/dom_manipulator.rs b/src/custom_component_renderer/dom_manipulator.rs deleted file mode 100644 index 0d9d003..0000000 --- a/src/custom_component_renderer/dom_manipulator.rs +++ /dev/null @@ -1,208 +0,0 @@ -use super::error::RendererError; -use crate::custom_component_renderer::error::Result; -use ego_tree::iter::Children; -use ego_tree::{NodeId, NodeMut, NodeRef, Tree}; -use markup5ever::{namespace_url, ns, Attribute, LocalName, QualName}; -use scraper::node::{Element, Text}; -use scraper::{Html, Node}; - -pub struct NodeManipulator<'a> { - tree: &'a mut Tree, - node_id: NodeId, - append_children_builder: Option, -} - -impl<'a> NodeManipulator<'a> { - pub fn new(tree: &'a mut Tree, node_id: NodeId) -> NodeManipulator<'a> { - NodeManipulator { - tree, - node_id, - append_children_builder: None, - } - } - - fn get_node(&'a self) -> Result> { - self.tree.get(self.node_id).ok_or_else(|| { - RendererError::InternalError(format!("Node with id {:?} does not exist", self.node_id)) - }) - } - - fn get_node_mut(&mut self) -> Result> { - self.tree.get_mut(self.node_id).ok_or_else(|| { - RendererError::InternalError(format!("Node with id {:?} does not exist", self.node_id)) - }) - } - - pub fn get_attribute(&self, attr: &str) -> Result> { - let node = self.get_node()?; - match node.value() { - Node::Element(element) => { - let attr = element.attr(attr); - Ok(attr) - } - _ => Err(RendererError::InternalError(format!( - "Node with id {:?} is not an element", - self.node_id - ))), - } - } - - /// Appends a child node and returns a reference to the new node. - pub fn append_child(&'a mut self, new_node: Node) -> Result { - let mut node = self.get_node_mut()?; - let inserted_id = node.append(new_node).id(); - Ok(Self::new(self.tree, inserted_id)) - } - - /// Appends a sibling node and returns a reference to the new node. - pub fn append_sibling(&'a mut self, new_node: Node) -> Result { - let mut node = self.get_node_mut()?; - let inserted_id = node.insert_after(new_node).id(); - Ok(Self::new(self.tree, inserted_id)) - } - - pub fn builder(&mut self) -> &mut AppendChildrenBuilder { - let builder = AppendChildrenBuilder::new(None); - self.append_children_builder = Some(builder); - self.append_children_builder.as_mut().unwrap() - } - - fn build_children_impl(&mut self, builder: AppendChildrenBuilder) -> Result<()> { - let mut node = self.get_node_mut()?; - let mut builder_to_nodeid = Vec::new(); - for mut child in builder.children { - let inserted_id = node.append(child.value.take().unwrap()).id(); - builder_to_nodeid.push((child, inserted_id)); - } - let original_node_id = self.node_id; - for (child, inserted_id) in builder_to_nodeid { - self.node_id = inserted_id; - self.build_children_impl(child)?; - } - self.node_id = original_node_id; - Ok(()) - } - - pub fn build_children(&mut self) -> Result<&mut Self> { - let builder = self.append_children_builder.take().ok_or_else(|| { - RendererError::InternalError(String::from( - "Missing children builder in build_children call", - )) - })?; - self.build_children_impl(builder)?; - Ok(self) - } - - pub fn replace_with(mut self, new_node: Node) -> Result { - let mut node = self.get_node_mut()?; - let inserted_id = node.insert_after(new_node).id(); - node.detach(); - let Self { tree, .. } = self; - Ok(Self::new(tree, inserted_id)) - } - - /// Adds the nodes in `to_add` as children to `target` recursively. - fn merge_trees(target: &mut NodeMut, to_add: Children) { - for node in to_add { - let mut inserted_node = target.append(node.value().clone()); - Self::merge_trees(&mut inserted_node, node.children()); - } - } - - /// Replaces the node with the given HTML. - /// - /// We need to recursively add the children of the HTML node to the parent of the node we are replacing. - pub fn replace_with_html(mut self, html: Html) -> Result { - let mut node = self.get_node_mut()?; - let html_root = html.tree.root(); - let inserted_id = node.insert_after(html_root.value().clone()).id(); - node.detach(); - let mut new_node = self.tree.get_mut(inserted_id).ok_or_else(|| { - RendererError::InternalError(format!("Node with id {:?} does not exist", self.node_id)) - })?; - Self::merge_trees(&mut new_node, html_root.children()); - let Self { tree, .. } = self; - Ok(Self::new(tree, inserted_id)) - } - - /// Appends a sibling node from HTML. - pub fn append_sibling_html(&mut self, html: Html) -> Result<()> { - let mut node = self.get_node_mut()?; - let html_root = html.tree.root(); - let inserted_id = node.insert_after(html_root.value().clone()).id(); - let mut new_node = self.tree.get_mut(inserted_id).ok_or_else(|| { - RendererError::InternalError(format!("Node with id {:?} does not exist", self.node_id)) - })?; - Self::merge_trees(&mut new_node, html_root.children()); - Ok(()) - } -} - -pub struct AppendChildrenBuilder { - children: Vec, - value: Option, -} - -impl AppendChildrenBuilder { - fn new(value: Option) -> Self { - Self { - value, - children: Vec::new(), - } - } - - pub fn append_child(&mut self, new_node: Node) -> &mut AppendChildrenBuilder { - let new_builder = Self::new(Some(new_node)); - self.children.push(new_builder); - self.children.last_mut().unwrap() - } - - /// Adds the nodes in `to_add` as children to `target` recursively. - fn append_html_tree(&mut self, to_add: Children) { - for node in to_add { - let mut inserted_node = self.append_child(node.value().clone()); - Self::append_html_tree(&mut inserted_node, node.children()); - } - } - - pub fn append_html(&mut self, html: &str) { - let parsed = Html::parse_fragment(html); - let mut newly_added = self.append_child(parsed.tree.root().value().clone()); - let root_children = parsed.tree.root().children(); - Self::append_html_tree(&mut newly_added, root_children); - } -} - -pub struct NodeAttribute { - pub name: String, - pub value: String, -} - -impl NodeAttribute { - pub fn new(name: &str, value: &str) -> Self { - Self { - name: String::from(name), - value: String::from(value), - } - } -} - -impl From for Attribute { - fn from(value: NodeAttribute) -> Self { - Attribute { - name: QualName::new(None, ns!(), LocalName::from(value.name)), - value: value.value.into(), - } - } -} - -pub fn create_node(name: &str, attributes: Vec) -> Node { - Node::Element(Element::new( - QualName::new(None, ns!(), LocalName::from(name)), - attributes.into_iter().map(Into::into).collect(), - )) -} - -pub fn create_text_node(text: &str) -> Node { - Node::Text(Text { text: text.into() }) -} diff --git a/src/custom_component_renderer/error.rs b/src/custom_component_renderer/error.rs index 5301488..ed47fbd 100644 --- a/src/custom_component_renderer/error.rs +++ b/src/custom_component_renderer/error.rs @@ -1,17 +1,21 @@ +use std::path::StripPrefixError; + use thiserror::Error; #[derive(Error, Debug)] pub enum RendererError { #[error("IO Error: {0}")] IoError(#[from] std::io::Error), - #[error("Invalid Identifier: {0}")] - InvalidIdentifier(String), #[error("Invalid path: {0}")] InvalidPath(String), - #[error("Internal Error: {0}")] - InternalError(String), - #[error("Component Rendering Error: {0}")] - ComponentError(String), + #[error("Error rendering tera template: {0}")] + TeraError(#[from] tera::Error), + #[error("HTML Error: {0}")] + HtmlRewritingError(#[from] lol_html::errors::RewritingError), + #[error("Mdbook Error: {0}")] + Mdbook(#[from] mdbook::errors::Error), + #[error("Error in strip_prefix call: {0}")] + StripPrefixError(#[from] StripPrefixError), } pub type Result = std::result::Result; diff --git a/src/custom_component_renderer/mod.rs b/src/custom_component_renderer/mod.rs index 28faed7..7ea5239 100644 --- a/src/custom_component_renderer/mod.rs +++ b/src/custom_component_renderer/mod.rs @@ -1,9 +1,8 @@ mod book_directory_renderer; -mod component_trait; -mod dom_manipulator; +mod custom_component; mod error; +pub(crate) mod standard_templates; pub(crate) use book_directory_renderer::*; -pub(crate) use component_trait::*; -pub(crate) use dom_manipulator::*; +pub(crate) use custom_component::*; pub(crate) use error::*; diff --git a/src/custom_component_renderer/standard_templates.rs b/src/custom_component_renderer/standard_templates.rs new file mode 100644 index 0000000..a558d7d --- /dev/null +++ b/src/custom_component_renderer/standard_templates.rs @@ -0,0 +1,9 @@ +use super::CustomComponent; + +mod templates { + pub const LANGUAGE_PICKER: &str = include_str!("standard_templates/language_picker.html"); +} + +pub fn create_language_picker_component() -> CustomComponent { + CustomComponent::new("LanguagePicker", templates::LANGUAGE_PICKER) +} diff --git a/src/custom_component_renderer/standard_templates/language_picker.html b/src/custom_component_renderer/standard_templates/language_picker.html new file mode 100644 index 0000000..db1c16d --- /dev/null +++ b/src/custom_component_renderer/standard_templates/language_picker.html @@ -0,0 +1,38 @@ + + + + + + diff --git a/src/custom_components/mod.rs b/src/custom_components/mod.rs deleted file mode 100644 index 12bcade..0000000 --- a/src/custom_components/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod language_picker; -#[cfg(test)] -pub(crate) mod test_component; - -pub(crate) use language_picker::LanguagePickerComponent; diff --git a/src/custom_components/test_component.rs b/src/custom_components/test_component.rs deleted file mode 100644 index f1a2ac3..0000000 --- a/src/custom_components/test_component.rs +++ /dev/null @@ -1,41 +0,0 @@ -use crate::custom_component_renderer::RenderingContext; -use crate::{ - create_node, create_text_node, Component, NodeAttribute, NodeManipulator, - RendererConfiguration, RendererError, Result, -}; - -pub struct TestComponent {} - -impl TestComponent { - pub fn new() -> TestComponent { - TestComponent {} - } -} - -impl Component for TestComponent { - fn identifier(&self) -> String { - String::from("TestComponent") - } - - fn render( - &mut self, - node: NodeManipulator<'_>, - config: &RendererConfiguration, - _: &RenderingContext, - ) -> Result<()> { - let name = node - .get_attribute("name")? - .ok_or_else(|| RendererError::ComponentError(String::from("Missing attribute name")))?; - let new_node = create_node("div", vec![NodeAttribute::new("name", name)]); - let mut new_node = node.replace_with(new_node)?; - let mut ul = new_node.append_child(create_node("ul", Vec::new()))?; - - let append_builder = ul.builder(); - for (identifier, language) in &config.i18n.languages { - let li = append_builder.append_child(create_node("li", Vec::new())); - li.append_child(create_text_node(&format!("{}: {}", identifier, language))); - } - ul.build_children()?; - Ok(()) - } -} diff --git a/src/main.rs b/src/main.rs index dbfe437..2b8e0a2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,12 @@ mod custom_component_renderer; -mod custom_components; use mdbook::renderer::RenderContext; use std::io; use crate::custom_component_renderer::*; -fn create_components() -> Vec> { - vec![Box::new(custom_components::LanguagePickerComponent::new())] +fn create_components() -> Vec { + vec![standard_templates::create_language_picker_component()] } fn main() { @@ -20,30 +19,30 @@ fn main() { .get_deserialized_opt("output.i18n-helpers") .unwrap() .unwrap(); - let destination = ctx - .destination - .parent() - .expect("Missing destination parent") - .to_owned(); - let root = ctx.root.clone(); - - let renderer_config = RendererConfiguration::new( - i18n_config, - destination, - ctx.config.book.language.clone(), - root, - ); - let default_language = renderer_config.i18n.default_language.clone(); - - let mut renderer = BookDirectoryRenderer::new(renderer_config); + + let default_language = i18n_config.default_language.clone(); + + let mut mdbook = mdbook::MDBook::load(&ctx.root).expect("Failed to load book"); + mdbook.book = ctx.book.clone(); + mdbook.config = ctx.config.clone(); + mdbook.root = ctx.root.clone(); + + let language = ctx.config.book.language.clone(); + + let destination = ctx.destination.parent().unwrap().to_owned(); + let translate_all_languages = i18n_config.translate_all_languages; + + let mut renderer = BookDirectoryRenderer::new(i18n_config, mdbook, destination); let components = create_components(); for component in components { renderer.add_component(component); } - if ctx.config.book.language.is_none() || ctx.config.book.language == default_language { - renderer.translate().expect("Failed to run translations."); + if language.is_none() || language == default_language { + if translate_all_languages { + renderer.translate().expect("Failed to run translations."); + } renderer.render_book().expect("Failed to render book"); } }