diff --git a/Cargo.lock b/Cargo.lock index d1c34a6..15c3e87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -169,6 +169,23 @@ name = "byteorder" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "c2-chacha" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cc" version = "1.0.47" @@ -221,6 +238,16 @@ dependencies = [ "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "colored" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "comrak" version = "0.6.2" @@ -283,6 +310,27 @@ name = "fake-simd" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "fern" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "colored 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "filetime" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "flate2" version = "1.0.13" @@ -299,6 +347,37 @@ name = "fnv" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "fsevent" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fsevent-sys 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fsevent-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "fxhash" version = "0.2.1" @@ -315,6 +394,16 @@ dependencies = [ "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "getrandom" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "glob" version = "0.3.0" @@ -328,6 +417,11 @@ dependencies = [ "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "httparse" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "humansize" version = "1.1.0" @@ -341,11 +435,56 @@ dependencies = [ "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "inotify" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "inotify-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "itoa" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -389,6 +528,7 @@ version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -396,6 +536,11 @@ name = "maplit" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "memchr" version = "2.2.1" @@ -409,6 +554,46 @@ dependencies = [ "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "mio" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio-extras" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "mkbook" version = "0.3.0" @@ -417,11 +602,25 @@ dependencies = [ "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "comrak 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "fern 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "notify 4.0.14 (registry+https://github.com/rust-lang/crates.io-index)", "sass-rs 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "syntect 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "ws 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "net2" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -433,6 +632,24 @@ dependencies = [ "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "notify" +version = "4.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "filetime 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "fsevent 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fsevent-sys 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num-integer" version = "0.1.41" @@ -490,6 +707,11 @@ name = "peeking_take_while" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "pest" version = "2.1.2" @@ -547,6 +769,11 @@ dependencies = [ "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ppv-lite86" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "proc-macro2" version = "0.4.30" @@ -584,6 +811,43 @@ dependencies = [ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "redox_syscall" version = "0.1.56" @@ -692,6 +956,16 @@ name = "shlex" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "smallvec" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "strsim" version = "0.8.0" @@ -817,6 +1091,22 @@ name = "unchecked-index" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "unicode-width" version = "0.1.6" @@ -837,6 +1127,16 @@ name = "unicode_categories" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "url" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "vec_map" version = "0.8.1" @@ -857,6 +1157,11 @@ dependencies = [ "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "wasi" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "which" version = "2.0.1" @@ -866,6 +1171,11 @@ dependencies = [ "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "winapi" version = "0.3.8" @@ -875,6 +1185,11 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -902,6 +1217,32 @@ dependencies = [ "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ws" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "xml-rs" version = "0.8.0" @@ -935,12 +1276,15 @@ dependencies = [ "checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" "checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" +"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" "checksum cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)" = "aa87058dce70a3ff5621797f1506cb837edd02ac4c0ae642b4542dce802908b8" "checksum cexpr 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fce5b5fb86b0c57c20c834c1b412fd09c77c8a59b9473f86272709e78874cd1d" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01" "checksum clang-sys 0.28.1 (registry+https://github.com/rust-lang/crates.io-index)" = "81de550971c976f176130da4b2978d3b524eaa0fd9ac31f3ceb5ae1231fb4853" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +"checksum colored 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "433e7ac7d511768127ed85b0c4947f47a254131e37864b2dc13f52aa32cd37e5" "checksum comrak 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ea4c29f52463abf5c7a3ae33dd9b404e2031af82f547cfe65bfac17ba785ea2e" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" @@ -948,15 +1292,28 @@ dependencies = [ "checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" "checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +"checksum fern 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e69ab0d5aca163e388c3a49d284fed6c3d0810700e77c5ae2756a50ec1a4daaa" +"checksum filetime 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1ff6d4dab0aa0c8e6346d46052e93b13a16cf847b54ed357087c35011048cc7d" "checksum flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6bd6d6f4752952feb71363cffc9ebac9411b75b87c6ab6058c40c8900cf43c0f" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +"checksum fsevent 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6" +"checksum fsevent-sys 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0" +"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" "checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" "checksum hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "307c3c9f937f38e3534b1d6447ecf090cafcc9744e4a6360e8b037b2cf5af120" +"checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" "checksum humansize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6cab2627acfc432780848602f3f558f7e9dd427352224b0d9324025796d2a5e" "checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +"checksum inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40b54539f3910d6f84fbf9a643efd6e3aa6e4f001426c0329576128255994718" +"checksum inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0" +"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" "checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8" @@ -965,9 +1322,15 @@ dependencies = [ "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" +"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" "checksum miniz_oxide 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6f3f74f726ae935c3f514300cc6773a0c9492abc5e972d42ba0c0ebb88757625" +"checksum mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)" = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" +"checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40" +"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" "checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" +"checksum notify 4.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "199628fc33b21bc767baa057490b00b382ecbae030803a7b36292422d15b778b" "checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" "checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4" "checksum num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76dac5ed2a876980778b8b85f75a71b6cbf0db0b1232ee12f826bccb00d09d72" @@ -975,17 +1338,23 @@ dependencies = [ "checksum onig_sys 69.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0a8d4efbf5f59cece01f539305191485b651acb3785b9d5eef05749f0496514e" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" "checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" "checksum pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e4fb201c5c22a55d8b24fef95f78be52738e5e1361129be1b5e862ecdb6894a" "checksum pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" "checksum pest_generator 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7b9fcf299b5712d06ee128a556c94709aaa04512c4dffb8ead07c5c998447fc0" "checksum pest_meta 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "df43fd99896fd72c485fe47542c7b500e4ac1e8700bf995544d1317a60ded547" "checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" "checksum plist 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5f2a9f075f6394100e7c105ed1af73fb1859d6fd14e49d4290d578120beb167f" +"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" "checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" +"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" "checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" "checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" @@ -1000,6 +1369,8 @@ dependencies = [ "checksum serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)" = "1a3351dcbc1f067e2c92ab7c3c1f288ad1a4cffc470b5aaddb4c2e0a3ae80043" "checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68" "checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" +"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +"checksum smallvec 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecf3b85f68e8abaa7555aa5abdb1153079387e60b718283d732f03897fcfc86" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" "checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" "checksum syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "661641ea2aa15845cddeb97dad000d22070bb5c1fb456b96c1cba883ec691e92" @@ -1015,18 +1386,26 @@ dependencies = [ "checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" "checksum ucd-trie 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8f00ed7be0c1ff1e24f46c3d2af4859f7e863672ba3a6e92e7cff702bf9f06c2" "checksum unchecked-index 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eeba86d422ce181a719445e51872fa30f1f7413b62becb52e95ec91aa262d85c" +"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +"checksum unicode-normalization 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b561e267b2326bb4cebfc0ef9e68355c7abe6c6f522aeac2f5bf95d56c59bdcf" "checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" "checksum unicode_categories 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" +"checksum url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" "checksum walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e" +"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" "checksum which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b57acb10231b9493c8472b20cb57317d0679a49e0bdbee44b3b803a6473af164" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96f5016b18804d24db43cebf3c77269e7569b8954a8464501c216cc5e070eaa9" +"checksum ws 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a2c47b5798ccc774ffb93ff536aec7c4275d722fd9c740c83cdd1af1f2d94" +"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" "checksum xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "541b12c998c5b56aa2b4e6f18f03664eef9a4fd0a246a55594efae6cc2d964b5" "checksum yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d" diff --git a/Cargo.toml b/Cargo.toml index 7cab0db..c56989a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,10 @@ serde = { version = "1.0", features = ["derive"] } toml = "0.5" lazy_static = "1.4" chrono = "0.4" +notify = "4.0" +ws = "0.9" +fern = { version = "0.5", features = ["colored"] } +log = { version = "0.4", features = ["max_level_debug", "release_max_level_info", "std", "serde"] } [build-dependencies] sass-rs = "0.2" diff --git a/docs-src/01-introduction.md b/docs-src/01-introduction.md index e462677..930717b 100644 --- a/docs-src/01-introduction.md +++ b/docs-src/01-introduction.md @@ -35,6 +35,7 @@ SUBCOMMANDS: build build the book help Prints this message or the help of the given subcommand(s) init initialize a mkbook directory tree + watch build the book and continually rebuild whenever the source changes ``` ### The Init Command @@ -77,3 +78,24 @@ OPTIONS: -i, --in an optional directory to take the book sources from [default: src] -o, --out an optional directory to render the contents into [default: book] ``` + +### The Watch Command + +The watch command is basically the same as the `build` command, however after building it continues to monitor the source directory and if _any_ changes are made (a file is saved, renamed, removed, created, etc), the entire book is re-built. In the future, this will hopefully be smarter but for now it just the whole thing at once. Stop watching using Ctrl+C or sending `SIGINT`. + +```sh +$ mkbook build --help +mkbook-watch +build the book and continually rebuild whenever the source changes + +USAGE: + mkbook watch [OPTIONS] + +FLAGS: + -h, --help Prints help information + -V, --version Prints version information + +OPTIONS: + -i, --in an optional directory to take the book sources from [default: src] + -o, --out an optional directory to render the contents into [default: book] +``` diff --git a/docs/01-introduction/index.html b/docs/01-introduction/index.html index 8b7a325..a7aee1d 100644 --- a/docs/01-introduction/index.html +++ b/docs/01-introduction/index.html @@ -12,7 +12,7 @@ - + @@ -124,6 +124,7 @@ build build the book help Prints this message or the help of the given subcommand(s) init initialize a mkbook directory tree + watch build the book and continually rebuild whenever the source changes

The Init Command

The init command is a tool to help you get started, and will create an initial README.md file and a stub of your first chapter.

@@ -160,6 +161,24 @@ -i, --in <in> an optional directory to take the book sources from [default: src] -o, --out <out> an optional directory to render the contents into [default: book] +

The Watch Command

+

The watch command is basically the same as the build command, however after building it continues to monitor the source directory and if any changes are made (a file is saved, renamed, removed, created, etc), the entire book is re-built. In the future, this will hopefully be smarter but for now it just the whole thing at once. Stop watching using Ctrl+C or sending SIGINT.

+
+$ mkbook build --help
+mkbook-watch 
+build the book and continually rebuild whenever the source changes
+
+USAGE:
+    mkbook watch [OPTIONS]
+
+FLAGS:
+    -h, --help       Prints help information
+    -V, --version    Prints version information
+
+OPTIONS:
+    -i, --in <in>      an optional directory to take the book sources from [default: src]
+    -o, --out <out>    an optional directory to render the contents into [default: book]
+
@@ -176,5 +195,6 @@

© 2019 Kenton Hamaluik

+ \ No newline at end of file diff --git a/docs/02-markdown/01-commonmark.html b/docs/02-markdown/01-commonmark.html index e2f3ab6..46c53a7 100644 --- a/docs/02-markdown/01-commonmark.html +++ b/docs/02-markdown/01-commonmark.html @@ -12,7 +12,7 @@ - + @@ -164,5 +164,6 @@

© 2019 Kenton Hamaluik

+ \ No newline at end of file diff --git a/docs/02-markdown/02-syntax-highlighting.html b/docs/02-markdown/02-syntax-highlighting.html index 415c417..2e34fdf 100644 --- a/docs/02-markdown/02-syntax-highlighting.html +++ b/docs/02-markdown/02-syntax-highlighting.html @@ -12,7 +12,7 @@ - + @@ -625,5 +625,6 @@

© 2019 Kenton Hamaluik

+ \ No newline at end of file diff --git a/docs/02-markdown/03-plantuml-diagrams.html b/docs/02-markdown/03-plantuml-diagrams.html index a5b4ff9..1fee3d2 100644 --- a/docs/02-markdown/03-plantuml-diagrams.html +++ b/docs/02-markdown/03-plantuml-diagrams.html @@ -12,7 +12,7 @@ - + @@ -137,5 +137,6 @@

© 2019 Kenton Hamaluik

+ \ No newline at end of file diff --git a/docs/02-markdown/04-katex-formulas.html b/docs/02-markdown/04-katex-formulas.html index c0f5d60..0905232 100644 --- a/docs/02-markdown/04-katex-formulas.html +++ b/docs/02-markdown/04-katex-formulas.html @@ -12,7 +12,7 @@ - + @@ -145,5 +145,6 @@ M834 80h400000v40h-400000z'/>

© 2019 Kenton Hamaluik

+ \ No newline at end of file diff --git a/docs/02-markdown/index.html b/docs/02-markdown/index.html index f41a19e..7196600 100644 --- a/docs/02-markdown/index.html +++ b/docs/02-markdown/index.html @@ -12,7 +12,7 @@ - + @@ -132,5 +132,6 @@

© 2019 Kenton Hamaluik

+ \ No newline at end of file diff --git a/docs/03-frontmatter/index.html b/docs/03-frontmatter/index.html index 89f74fa..95e3c44 100644 --- a/docs/03-frontmatter/index.html +++ b/docs/03-frontmatter/index.html @@ -12,7 +12,7 @@ - + @@ -162,5 +162,6 @@

© 2019 Kenton Hamaluik

+ \ No newline at end of file diff --git a/docs/04-structure/index.html b/docs/04-structure/index.html index 9c05611..ad38217 100644 --- a/docs/04-structure/index.html +++ b/docs/04-structure/index.html @@ -12,7 +12,7 @@ - + @@ -199,5 +199,6 @@

© 2019 Kenton Hamaluik

+ \ No newline at end of file diff --git a/docs/05-customization/index.html b/docs/05-customization/index.html index 01ef8ce..3da53ed 100644 --- a/docs/05-customization/index.html +++ b/docs/05-customization/index.html @@ -12,7 +12,7 @@ - + @@ -123,5 +123,6 @@

© 2019 Kenton Hamaluik

+ \ No newline at end of file diff --git a/docs/06-how-it-works/index.html b/docs/06-how-it-works/index.html index b669323..59df8d3 100644 --- a/docs/06-how-it-works/index.html +++ b/docs/06-how-it-works/index.html @@ -12,7 +12,7 @@ - + @@ -124,5 +124,6 @@

© 2019 Kenton Hamaluik

+ \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index f63dd63..a801989 100644 --- a/docs/index.html +++ b/docs/index.html @@ -14,14 +14,14 @@ <p>This tool aims to work somewhat similarly to <em>mdbook</em>, but is generally intended to be a more minimal alternative that is customized more towards my needs and desires than anything else.</p> " /> - +

The mkbook Book

by Kenton Hamaluik

- +

mkbook is my simpler alternative to mdbook which is a great tool, but for which I really dislike some of the decisions they took, such as relying on javascript for highlighting and navigation, and including a lot of bells and whistles such as javascript-based search.

@@ -77,5 +77,6 @@

© 2019 Kenton Hamaluik

+ \ No newline at end of file diff --git a/docs/style.css b/docs/style.css index dcc82c0..4e770d6 100644 --- a/docs/style.css +++ b/docs/style.css @@ -1 +1 @@ -@import url("https://fonts.googleapis.com/css?family=Crimson+Pro|Poppins:700|Source+Code+Pro&display=swap");body{margin:0;line-height:1.5;font-size:14pt;color:#222222;background:#eeeeee;padding:0;font-family:"Crimson Pro","Georgia",Georgia,"Times New Roman",Times,serif}h1,h2,h3{margin-top:0;line-height:1.2;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif}a{color:#222222;text-decoration:underline}a:hover{color:#8a2888;text-decoration:none}figure{display:block;text-align:center;overflow-x:auto}figure img,figure svg,figure video{max-width:100%}figure figcaption{display:block;font-size:0.75rem;text-align:center}code,kbd{margin:0 2px;padding:0 2px;border:1px solid #4c566a;border-radius:3px;word-break:break-all;font-family:"Source Code Pro","Courier New",Courier,monospace;font-size:0.75rem}kbd{color:#222222;background:#eeeeee;box-shadow:0px 2px 4px rgba(0,0,0,0.5)}pre{overflow-x:auto;font-family:"Source Code Pro","Courier New",Courier,monospace;padding:0.5rem}dl{display:grid;grid-template-columns:auto auto}dl dt{font-weight:700;margin:0;padding:0 0.5rem 0.25rem;border-right:1px solid #dddddd;text-align:right}dl dd{margin:0;padding:0 0.5rem 0.25rem}dl dt p,dl dd p{margin:0}p{margin-top:0}footer{color:#444444}footer p{margin-right:0.5rem}table{margin:0 auto;border-collapse:collapse;border-spacing:0.5rem}table th{border-bottom:1px solid #eee}table th,table td{vertical-align:top}html,body{width:100%;min-height:100vh}body{display:grid;grid-template-columns:auto 1fr;grid-template-rows:1fr;justify-items:stretch;align-items:stretch}body nav.big{background:#2c2c38;padding:0.5rem 1rem 1rem;max-width:15rem;display:flex;flex-direction:column}body nav.big header{margin:0 0 1rem;display:flex;flex-direction:column;align-items:stretch;border-bottom:2px solid #888888}body nav.big header h1{font-size:1.5rem;font-weight:700;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;margin-bottom:0;text-align:left;color:#eeeeee}body nav.big header h1 a{color:#eeeeee;text-decoration:none}body nav.big header h1 a:hover{color:#cf5ccd}body nav.big header h2{font-size:1rem;font-weight:600;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;margin-bottom:0;text-align:right}body nav.big ol{margin:0 2rem;padding:0}body nav.big ol li{width:100%;font-size:1.25rem;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;color:#eeeeee}body nav.big ol li a{text-decoration:none;margin:0 0 0.25rem 0;color:#5babd1}body nav.big ol li a:hover{color:#cf5ccd}body nav.big ol li a.current{color:#cf5ccd}body nav.big ol li a.current:hover{color:#fefefe}body nav.big ol li ol{margin:0 0 0 1rem}body nav.small{display:none;width:100%;align-items:center;justify-content:space-between;background:#2c2c38;padding:0}body nav.small>*{margin:0.5rem}body nav.small a{text-decoration:none;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;color:#5babd1}body nav.small a:hover{color:#cf5ccd}body nav.small span{display:inline-flex;align-items:center}body nav.small span>*{margin-right:0.5rem}body nav.small span>*:last-child{margin-right:0}body nav.small span.title{text-decoration:none;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;color:#fefefe;justify-self:center}body nav.small span.placeholder{width:1em;height:1em}body>*{min-width:0;min-height:0}body article{max-width:38rem;padding:0.5rem 2rem 0 2rem}body article>*{max-width:100%}body .next-chapter{width:100%}body .next-chapter a{display:flex;align-items:center;justify-content:flex-end;margin-right:0.5rem;font-size:1.2rem;text-decoration:none;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;color:#5babd1}body .next-chapter a:hover{color:#cf5ccd}body .next-chapter a span:first-child{margin-right:0.5rem}body footer{width:100%;text-align:right;display:flex;flex-direction:column;align-items:flex-end;justify-content:flex-end}@media screen and (max-width: 768px){body{grid-template-columns:1fr;grid-template-rows:auto 1fr}body nav.big{display:none}body nav.small{display:flex}body article{padding:1rem 0.5rem 0 0.5rem}}body.toc{display:flex;flex-direction:column;justify-content:flex-start;align-items:center}body.toc>*{width:36rem}body.toc header{margin-top:1rem;display:flex;flex-direction:column;align-items:center}body.toc header h1{font-size:3rem;font-weight:700;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;margin-bottom:0}body.toc header h2{font-size:1rem;font-weight:600;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;margin-bottom:0}body.toc header time{font-size:1rem;font-weight:400;font-family:"Crimson Pro","Georgia",Georgia,"Times New Roman",Times,serif}body.toc nav{background:none;display:flex;flex-direction:column;align-items:stretch}body.toc nav h1{text-align:left;font-size:2.5rem;font-weight:600;margin-bottom:0}body.toc nav ol li{font-size:1.5rem;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif}body.toc nav ol li a{text-decoration:none;margin:0 0 0.25rem 0;color:#5babd1}body.toc nav ol li a:hover{color:#cf5ccd}body.toc nav>ol{margin-left:0.5rem;margin-right:0.5rem}body.toc article,body.toc nav{padding:0}body.toc article>*,body.toc nav>*{margin-left:0.5rem;margin-right:0.5rem}body.toc footer{flex:1}@media screen and (max-width: 768px){body.toc>*{width:100%}body.toc header h1,body.toc header h2,body.toc header time{text-align:center}body.toc nav h1{font-size:1.75rem}}span.icon{display:flex;align-items:center}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:1em;height:1em;stroke-width:0;stroke:currentColor;fill:currentColor}.icon-arrow-left{width:0.875em}.icon-arrow-right{width:0.875em}.icon-arrow-up{width:0.875em}.icon-list-ol{width:1em}@media (prefers-color-scheme: dark){body{background-color:#222222;color:#eeeeee}a{color:#eeeeee}a:hover{color:#5babd1}nav{background:#18181d}img{filter:grayscale(30%)}dl dt{border-right:1px solid #333333}footer{color:#cccccc}}@media print{body{background:#ffffff;color:#000000}body:not(.toc) nav{display:none}a{color:#000000;text-decoration:underline}h2,h3{break-after:avoid-page}figure{break-inside:avoid}p{orphans:2;widows:2}*{overflow:hidden}body{display:block}} +@import url("https://fonts.googleapis.com/css?family=Crimson+Pro|Poppins:700|Source+Code+Pro&display=swap");body{margin:0;line-height:1.5;font-size:14pt;color:#222222;background:#eeeeee;padding:0;font-family:"Crimson Pro","Georgia",Georgia,"Times New Roman",Times,serif}h1,h2,h3{margin-top:0;line-height:1.2;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif}a{color:#222222;text-decoration:underline}a:hover{color:#8a2888;text-decoration:none}figure{display:block;text-align:center;overflow-x:auto}figure img,figure svg,figure video{max-width:100%}figure figcaption{display:block;font-size:0.75rem;text-align:center}code,kbd{margin:0 2px;padding:0 2px;border:1px solid #4c566a;border-radius:3px;word-break:break-all;font-family:"Source Code Pro","Courier New",Courier,monospace;font-size:0.75rem}kbd{color:#222222;background:#eeeeee;box-shadow:0px 2px 4px rgba(0,0,0,0.5)}pre{overflow-x:auto;font-family:"Source Code Pro","Courier New",Courier,monospace;padding:0.5rem}dl{display:grid;grid-template-columns:auto auto}dl dt{font-weight:700;margin:0;padding:0 0.5rem 0.25rem;border-right:1px solid #dddddd;text-align:right}dl dd{margin:0;padding:0 0.5rem 0.25rem}dl dt p,dl dd p{margin:0}p{margin-top:0}footer{color:#444444}footer p{margin-right:0.5rem}table{margin:0 auto;border-collapse:collapse;border-spacing:0.5rem}table th{border-bottom:1px solid #eee}table th,table td{vertical-align:top}html,body{width:100%;min-height:100vh}body{display:grid;grid-template-columns:auto 1fr;grid-template-rows:1fr;justify-items:stretch;align-items:stretch}body nav.big{background:#2c2c38;padding:0.5rem 1rem 1rem;max-width:15rem;display:flex;flex-direction:column}body nav.big header{margin:0 0 1rem;display:flex;flex-direction:column;align-items:stretch;border-bottom:2px solid #888888}body nav.big header h1{font-size:1.5rem;font-weight:700;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;margin-bottom:0;text-align:left;color:#eeeeee}body nav.big header h1 a{color:#eeeeee;text-decoration:none}body nav.big header h1 a:hover{color:#cf5ccd}body nav.big header h2{font-size:1rem;font-weight:600;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;margin-bottom:0;text-align:right;color:#eeeeee}body nav.big ol{margin:0 2rem;padding:0}body nav.big ol li{width:100%;font-size:1.25rem;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;color:#eeeeee}body nav.big ol li a{text-decoration:none;margin:0 0 0.25rem 0;color:#5babd1}body nav.big ol li a:hover{color:#cf5ccd}body nav.big ol li a.current{color:#cf5ccd}body nav.big ol li a.current:hover{color:#fefefe}body nav.big ol li ol{margin:0 0 0 1rem}body nav.small{display:none;width:100%;align-items:center;justify-content:space-between;background:#2c2c38;padding:0}body nav.small>*{margin:0.5rem}body nav.small a{text-decoration:none;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;color:#5babd1}body nav.small a:hover{color:#cf5ccd}body nav.small span{display:inline-flex;align-items:center}body nav.small span>*{margin-right:0.5rem}body nav.small span>*:last-child{margin-right:0}body nav.small span.title{text-decoration:none;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;color:#fefefe;justify-self:center}body nav.small span.placeholder{width:1em;height:1em}body>*{min-width:0;min-height:0}body article{max-width:38rem;padding:0.5rem 2rem 0 2rem}body article>*{max-width:100%}body .next-chapter{width:100%}body .next-chapter a{display:flex;align-items:center;justify-content:flex-end;margin-right:0.5rem;font-size:1.2rem;text-decoration:none;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;color:#5babd1}body .next-chapter a:hover{color:#cf5ccd}body .next-chapter a span:first-child{margin-right:0.5rem}body footer{width:100%;text-align:right;display:flex;flex-direction:column;align-items:flex-end;justify-content:flex-end}@media screen and (max-width: 768px){body{grid-template-columns:1fr;grid-template-rows:auto 1fr}body nav.big{display:none}body nav.small{display:flex}body article{padding:1rem 0.5rem 0 0.5rem}}body.toc{display:flex;flex-direction:column;justify-content:flex-start;align-items:center}body.toc>*{width:36rem}body.toc header{margin-top:1rem;display:flex;flex-direction:column;align-items:center}body.toc header h1{font-size:3rem;font-weight:700;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;margin-bottom:0}body.toc header h2{font-size:1rem;font-weight:600;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;margin-bottom:0}body.toc header time{font-size:1rem;font-weight:400;font-family:"Crimson Pro","Georgia",Georgia,"Times New Roman",Times,serif}body.toc nav{background:none;display:flex;flex-direction:column;align-items:stretch}body.toc nav h1{text-align:left;font-size:2.5rem;font-weight:600;margin-bottom:0}body.toc nav ol li{font-size:1.5rem;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif}body.toc nav ol li a{text-decoration:none;margin:0 0 0.25rem 0;color:#5babd1}body.toc nav ol li a:hover{color:#cf5ccd}body.toc nav>ol{margin-left:0.5rem;margin-right:0.5rem}body.toc article,body.toc nav{padding:0}body.toc article>*,body.toc nav>*{margin-left:0.5rem;margin-right:0.5rem}body.toc footer{flex:1}@media screen and (max-width: 768px){body.toc>*{width:100%}body.toc header h1,body.toc header h2,body.toc header time{text-align:center}body.toc nav h1{font-size:1.75rem}}span.icon{display:flex;align-items:center}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:1em;height:1em;stroke-width:0;stroke:currentColor;fill:currentColor}.icon-arrow-left{width:0.875em}.icon-arrow-right{width:0.875em}.icon-arrow-up{width:0.875em}.icon-list-ol{width:1em}@media print{body{background:#ffffff;color:#000000}body:not(.toc) nav{display:none}a{color:#000000;text-decoration:underline}h2,h3{break-after:avoid-page}figure{break-inside:avoid}p{orphans:2;widows:2}*{overflow:hidden}body{display:block}} diff --git a/src/cli.rs b/src/cli.rs index 6290a77..f077b17 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -29,4 +29,24 @@ pub fn build_cli() -> App<'static, 'static> { .help("an optional directory to render the contents into") ) ) + .subcommand(SubCommand::with_name("watch") + .about("build the book and continually rebuild whenever the source changes") + .arg(Arg::with_name("in") + .short("i") + .long("in") + .default_value("src") + .help("an optional directory to take the book sources from") + ) + .arg(Arg::with_name("out") + .short("o") + .long("out") + .default_value("book") + .help("an optional directory to render the contents into") + ) + .arg(Arg::with_name("reload") + .short("r") + .long("reload") + .help("inject live-reload code so that the page automatically reloads on regeneration") + ) + ) } diff --git a/src/main.rs b/src/main.rs index 10367bf..9b66b93 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ #[macro_use] extern crate lazy_static; -use std::path::PathBuf; +use std::path::{PathBuf, Path}; use std::{fs, io}; use comrak::ComrakOptions; use syntect::{parsing::SyntaxSet, highlighting::{ThemeSet, Theme}}; @@ -85,7 +85,7 @@ fn create_katex_inline(src: &str) -> Result> .spawn() { Ok(c) => c, Err(e) => { - eprintln!("[WARNING] failed to launch katex, not rendering math block: {:?}", e); + log::warn!("failed to launch katex, not rendering math block: {:?}", e); return Ok(format_code("", src)?.output); } }; @@ -95,12 +95,12 @@ fn create_katex_inline(src: &str) -> Result> let output = child.wait_with_output()?; if !output.status.success() { - eprintln!("failed to generate katex, exit code: {:?}", output.status.code()); - eprintln!("katex STDOUT:"); - eprintln!("{}", String::from_utf8_lossy(output.stdout.as_ref())); - eprintln!("katex STDERR:"); - eprintln!("{}", String::from_utf8_lossy(output.stdout.as_ref())); - eprintln!("/katex output"); + log::error!("failed to generate katex, exit code: {:?}", output.status.code()); + log::error!("katex STDOUT:"); + log::error!("{}", String::from_utf8_lossy(output.stdout.as_ref())); + log::error!("katex STDERR:"); + log::error!("{}", String::from_utf8_lossy(output.stdout.as_ref())); + log::error!("/katex output"); return Ok(format_code("", src)?.output); } let rendered: String = String::from_utf8(output.stdout)?; @@ -122,7 +122,7 @@ fn create_plantuml_svg(src: &str) -> Result> .spawn() { Ok(c) => c, Err(e) => { - eprintln!("[WARNING] failed to launch plantuml, not rendering plantuml block: {:?}", e); + log::warn!("failed to launch plantuml, not rendering plantuml block: {:?}", e); return Ok(format_code("", src)?.output); } }; @@ -132,12 +132,12 @@ fn create_plantuml_svg(src: &str) -> Result> let output = child.wait_with_output()?; if !output.status.success() { - eprintln!("failed to generate plantuml, exit code: {:?}", output.status.code()); - eprintln!("plantuml STDOUT:"); - eprintln!("{}", String::from_utf8_lossy(output.stdout.as_ref())); - eprintln!("plantuml STDERR:"); - eprintln!("{}", String::from_utf8_lossy(output.stdout.as_ref())); - eprintln!("/plantuml output"); + log::error!("failed to generate plantuml, exit code: {:?}", output.status.code()); + log::error!("plantuml STDOUT:"); + log::error!("{}", String::from_utf8_lossy(output.stdout.as_ref())); + log::error!("plantuml STDERR:"); + log::error!("{}", String::from_utf8_lossy(output.stdout.as_ref())); + log::error!("/plantuml output"); return Ok(format_code("", src)?.output); } let svg: String = String::from_utf8(output.stdout)?; @@ -278,15 +278,17 @@ struct IndexTemplate<'a, 'b, 'c> { chapters: &'b Vec, book_description: &'c str, include_katex_css: bool, + include_reload_script: bool, } -fn generate_index(book: &FrontMatter, content: String, include_katex_css: bool, chapters: &Vec, mut output: W) -> Result<(), Box> { +fn generate_index(book: &FrontMatter, content: String, include_katex_css: bool, chapters: &Vec, mut output: W, include_reload_script: bool) -> Result<(), Box> { // fill out our template let template = IndexTemplate { book, chapters, book_description: &content, include_katex_css, + include_reload_script, }; // and render! @@ -306,9 +308,10 @@ struct PageTemplate<'a, 'b, 'c, 'd, 'e, 'g> { next_chapter: Option<&'e Chapter>, book: &'g FrontMatter, include_katex_css: bool, + include_reload_script: bool, } -fn format_page(book: &FrontMatter, chapter: &Chapter, chapters: &Vec, prev_chapter: Option<&Chapter>, next_chapter: Option<&Chapter>, content: &str, include_katex_css: bool, mut output: W) -> Result<(), Box> { +fn format_page(book: &FrontMatter, chapter: &Chapter, chapters: &Vec, prev_chapter: Option<&Chapter>, next_chapter: Option<&Chapter>, content: &str, include_katex_css: bool, mut output: W, include_reload_script: bool) -> Result<(), Box> { // fill out our template let template = PageTemplate { chapter, @@ -318,6 +321,7 @@ fn format_page(book: &FrontMatter, chapter: &Chapter, chapters: &V next_chapter, book, include_katex_css, + include_reload_script, }; // and render! @@ -327,27 +331,278 @@ fn format_page(book: &FrontMatter, chapter: &Chapter, chapters: &V Ok(()) } +fn build, POut: AsRef>(src: PIn, dest: POut, include_reload_script: bool) -> Result<(), Box> { + let src = PathBuf::from(src.as_ref()); + let dest = PathBuf::from(dest.as_ref()); + std::fs::create_dir_all(&dest)?; + + // load our book + let book_readme_path = src.join("README.md"); + let (book_front, book_description) = if book_readme_path.exists() { + let contents = fs::read_to_string(&book_readme_path)?; + let (front, contents) = extract_frontmatter(&contents)?; + (front, contents) + } + else { + let content = String::new(); + (None, content) + }; + let book_front = FrontMatter::from_root(book_front.unwrap_or_default()); + let FormatResponse { output, include_katex_css } = format_markdown(&book_description)?; + let book_description = output; + + // load all our chapters + let mut chapters: Vec = Vec::default(); + for entry in src.read_dir()? { + let entry = entry?; + let path = entry.path(); + if entry.file_type()?.is_dir() { + // try to find a `README.md` file and parse it to get the chapter's title, fall back to the directory + // name if we can't do that + let chapter_name = path.file_name().map(std::ffi::OsStr::to_str).flatten().unwrap_or_default(); + let index_path = path.join("README.md"); + let (front, contents) = if index_path.exists() { + let contents = fs::read_to_string(&index_path)?; + let (front, contents) = extract_frontmatter(&contents)?; + let front = front.unwrap_or_default().into_front(&book_front, chapter_name, &format!("{}/index.html", chapter_name)); + (front, contents) + } + else { + (ParsedFrontMatter::default().into_front(&book_front, chapter_name, &format!("{}/index.html", chapter_name)), String::new()) + }; + + let mut chapter: Chapter = Chapter { + front, + sections: Vec::default(), + source: path.clone(), + contents, + }; + + for entry in path.read_dir()? { + let entry = entry?; + let path = entry.path(); + if let Some("md") = path.extension().map(std::ffi::OsStr::to_str).flatten() { + let name = path.file_stem().map(std::ffi::OsStr::to_str).flatten(); + if name.is_none() { continue; } + let name = name.unwrap(); + if name == "README" { + continue; + } + + let contents = fs::read_to_string(&path)?; + let (front, contents) = extract_frontmatter(&contents)?; + let front = front.unwrap_or_default().into_front(&book_front, name, &format!("{}/{}.html", chapter_name, name)); + chapter.sections.push(Chapter { + front, + sections: Vec::new(), + source: path, + contents, + }); + } + } + + chapters.push(chapter); + } + else if let Some("md") = path.extension().map(std::ffi::OsStr::to_str).flatten() { + let name = path.file_stem().map(std::ffi::OsStr::to_str).flatten(); + if name.is_none() { continue; } + let name = name.unwrap(); + if name == "README" { + continue; + } + + let contents = fs::read_to_string(&path)?; + let (front, contents) = extract_frontmatter(&contents)?; + let front = front.unwrap_or_default().into_front(&book_front, name, &format!("{}/index.html", name)); + chapters.push(Chapter { + front, + sections: Vec::new(), + source: path, + contents, + }); + } + } + + // sort all the chapters + chapters.sort_by(|a, b| a.front.url.cmp(&b.front.url)); + for chapter in chapters.iter_mut() { + chapter.sections.sort_by(|a, b| a.front.url.cmp(&b.front.url)); + } + + // generate our index + let index_out_path = dest.join("index.html"); + let index_out = fs::File::create(&index_out_path)?; + let index_out = io::BufWriter::new(index_out); + generate_index(&book_front, book_description, include_katex_css, &chapters, index_out, include_reload_script)?; + log::info!("Rendered index into `{}`", index_out_path.display()); + + // compile markdown and write the actual pages + let mut prev_chapter = None; + for (chapter_index, chapter) in chapters.iter().enumerate() { + // render the index + let chapter_root = dest.join(chapter.source.file_stem().map(std::ffi::OsStr::to_str).flatten().unwrap()); + let out = chapter_root.join("index.html"); + log::info!("Rendering `{}` into `{}`...", chapter.source.display(), out.display()); + fs::create_dir_all(&chapter_root)?; + + let outfile = fs::File::create(&out)?; + let outfile = io::BufWriter::new(outfile); + + let FormatResponse { output, include_katex_css } = format_markdown(&chapter.contents)?; + + let next_chapter = + if chapter.sections.len() > 0 { + Some(chapter.sections.iter().nth(0).expect("section 0 exists")) + } + else if chapter_index < chapters.len() - 1 { + Some(chapters.iter().nth(chapter_index + 1).expect("chapter n+1 exists")) + } + else { + None + }; + + format_page(&book_front, &chapter, &chapters, prev_chapter, next_chapter, &output, include_katex_css, outfile, include_reload_script)?; + prev_chapter = Some(chapter); + + // now the sections + for (section_index, section) in chapter.sections.iter().enumerate() { + let name = section.source.file_stem().map(std::ffi::OsStr::to_str).flatten().unwrap(); + let out = chapter_root.join(&format!("{}.html", name)); + log::info!("Rendering `{}` into `{}`...", section.source.display(), out.display()); + + let outfile = fs::File::create(&out)?; + let outfile = io::BufWriter::new(outfile); + + let FormatResponse { output, include_katex_css } = format_markdown(§ion.contents)?; + + let next_chapter = if section_index < chapter.sections.len() - 1 { + Some(chapter.sections.iter().nth(section_index + 1).expect("chapter n+1 exists")) + } + else if chapter_index < chapters.len() - 1 { + Some(chapters.iter().nth(chapter_index + 1).expect("chapter n+1 exists")) + } + else { + None + }; + + format_page(&book_front, §ion, &chapters, prev_chapter, next_chapter, &output, include_katex_css, outfile, include_reload_script)?; + prev_chapter = Some(section); + + } + } + + // save the assets + fs::write(dest.join("style.css"), STYLESHEET)?; + log::info!("Wrote {}", dest.join("style.css").display()); + fs::write(dest.join("favicon.ico"), ASSET_FAVICON)?; + log::info!("Wrote {}", dest.join("favicon.ico").display()); + fs::write(dest.join("icons.svg"), ASSET_ICONS)?; + log::info!("Wrote {}", dest.join("icons.svg").display()); + + log::info!("Done!"); + Ok(()) +} + +struct ReloadClient { + sender: std::sync::Arc, + reload: std::sync::Arc, + quitloop: std::sync::Arc, +} + +impl ReloadClient { + pub fn new(sender: ws::Sender, reload: std::sync::Arc) -> ReloadClient { + ReloadClient { + sender: std::sync::Arc::new(sender), + reload, + quitloop: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)), + } + } +} + +impl ws::Handler for ReloadClient { + fn on_open(&mut self, _: ws::Handshake) -> ws::Result<()> { + log::info!("reload client connected"); + let out = self.sender.clone(); + let reload = self.reload.clone(); + let quitloop = self.quitloop.clone(); + std::thread::spawn(move || { + 'sendloop: loop { + let send_reload = reload.load(std::sync::atomic::Ordering::SeqCst); + if send_reload { + log::debug!("sending reload signal..."); + out.send("reload").expect("can send reload signal"); + log::debug!(" ok!"); + } + + let quit = quitloop.load(std::sync::atomic::Ordering::SeqCst); + if quit { + break 'sendloop; + } + + // check at 10 Hz + std::thread::sleep(std::time::Duration::from_millis(100)); + } + log::warn!("shutting down reload connection"); + if let Err(e) = out.shutdown() { + log::error!("failed to shut down reload connection: {:?}", e); + } + }); + Ok(()) + } + + fn on_close(&mut self, code: ws::CloseCode, reason: &str) { + log::debug!("reload connection closed: {:?}: {}", code, reason); + //self.quitloop.store(true, std::sync::atomic::Ordering::SeqCst); + } + + fn on_shutdown(&mut self) { + log::debug!("reload connection shutdown"); + self.quitloop.store(true, std::sync::atomic::Ordering::SeqCst); + } +} + fn main() -> Result<(), Box> { let matches = cli::build_cli().get_matches(); + use fern::colors::{Color, ColoredLevelConfig}; + let colors_level = ColoredLevelConfig::new() + .error(Color::Red) + .warn(Color::Yellow) + .info(Color::Cyan) + .debug(Color::Magenta) + .trace(Color::BrightBlack); + fern::Dispatch::new() + .format(move |out, message, record| { + out.finish(format_args!( + "[{date}][\x1B[96m{target}\x1B[0m][{level}\x1B[0m] {message}", + date = chrono::Local::now().format("%Y-%m-%d %H:%M:%S"), + target = record.target(), + level = colors_level.color(record.level()), + message = message, + )); + }) + .level_for("ws", log::LevelFilter::Info) + .chain(std::io::stdout()) + .apply()?; + if let Some(submatches) = matches.subcommand_matches("init") { let dest = submatches.value_of("directory").expect("directory value"); let dest = PathBuf::from(dest); - println!("Initializing a book into {}...", dest.display()); + log::info!("Initializing a book into {}...", dest.display()); fs::create_dir_all(&dest)?; let book_readme_path = dest.join("README.md"); fs::write(&book_readme_path, ASSET_DEFAULT_README)?; let intro_path = dest.join("01-introduction.md"); fs::write(&intro_path, ASSET_DEFAULT_INTRODUCTION)?; - println!("Done!"); + log::info!("Done!"); - println!("You can now build your book by running:"); + log::info!("You can now build your book by running:"); if dest.display().to_string() != "src" { - println!("mkbook build -i {}", dest.display()); + log::info!("mkbook build -i {}", dest.display()); } else { - println!("mkbook build"); + log::info!("mkbook build"); } Ok(()) @@ -355,179 +610,45 @@ fn main() -> Result<(), Box> { else if let Some(submatches) = matches.subcommand_matches("build") { let src = submatches.value_of("in").expect("in value"); let dest = submatches.value_of("out").expect("out value"); - - let src = PathBuf::from(src); - let dest = PathBuf::from(dest); - std::fs::create_dir_all(&dest)?; - - // load our book - let book_readme_path = src.join("README.md"); - let (book_front, book_description) = if book_readme_path.exists() { - let contents = fs::read_to_string(&book_readme_path)?; - let (front, contents) = extract_frontmatter(&contents)?; - (front, contents) - } - else { - let content = String::new(); - (None, content) - }; - let book_front = FrontMatter::from_root(book_front.unwrap_or_default()); - let FormatResponse { output, include_katex_css } = format_markdown(&book_description)?; - let book_description = output; - - // load all our chapters - let mut chapters: Vec = Vec::default(); - for entry in src.read_dir()? { - let entry = entry?; - let path = entry.path(); - if entry.file_type()?.is_dir() { - // try to find a `README.md` file and parse it to get the chapter's title, fall back to the directory - // name if we can't do that - let chapter_name = path.file_name().map(std::ffi::OsStr::to_str).flatten().unwrap_or_default(); - let index_path = path.join("README.md"); - let (front, contents) = if index_path.exists() { - let contents = fs::read_to_string(&index_path)?; - let (front, contents) = extract_frontmatter(&contents)?; - let front = front.unwrap_or_default().into_front(&book_front, chapter_name, &format!("{}/index.html", chapter_name)); - (front, contents) - } - else { - (ParsedFrontMatter::default().into_front(&book_front, chapter_name, &format!("{}/index.html", chapter_name)), String::new()) - }; - - let mut chapter: Chapter = Chapter { - front, - sections: Vec::default(), - source: path.clone(), - contents, - }; - - for entry in path.read_dir()? { - let entry = entry?; - let path = entry.path(); - if let Some("md") = path.extension().map(std::ffi::OsStr::to_str).flatten() { - let name = path.file_stem().map(std::ffi::OsStr::to_str).flatten(); - if name.is_none() { continue; } - let name = name.unwrap(); - if name == "README" { - continue; - } + build(src, dest, false) + } + else if let Some(submatches) = matches.subcommand_matches("watch") { + let reload_trigger = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)); + let do_reload = submatches.is_present("reload"); - let contents = fs::read_to_string(&path)?; - let (front, contents) = extract_frontmatter(&contents)?; - let front = front.unwrap_or_default().into_front(&book_front, name, &format!("{}/{}.html", chapter_name, name)); - chapter.sections.push(Chapter { - front, - sections: Vec::new(), - source: path, - contents, - }); - } - } - - chapters.push(chapter); - } - else if let Some("md") = path.extension().map(std::ffi::OsStr::to_str).flatten() { - let name = path.file_stem().map(std::ffi::OsStr::to_str).flatten(); - if name.is_none() { continue; } - let name = name.unwrap(); - if name == "README" { - continue; - } - - let contents = fs::read_to_string(&path)?; - let (front, contents) = extract_frontmatter(&contents)?; - let front = front.unwrap_or_default().into_front(&book_front, name, &format!("{}/index.html", name)); - chapters.push(Chapter { - front, - sections: Vec::new(), - source: path, - contents, - }); - } - } - - // sort all the chapters - chapters.sort_by(|a, b| a.front.url.cmp(&b.front.url)); - for chapter in chapters.iter_mut() { - chapter.sections.sort_by(|a, b| a.front.url.cmp(&b.front.url)); + if do_reload { + let reload_trigger = reload_trigger.clone(); + std::thread::spawn(move || { + log::info!("starting livereload service"); + ws::listen("127.0.0.1:3456", |out| ReloadClient::new(out, reload_trigger.clone())).expect("can launch livereload service"); + }); } - // generate our index - let index_out_path = dest.join("index.html"); - let index_out = fs::File::create(&index_out_path)?; - let index_out = io::BufWriter::new(index_out); - generate_index(&book_front, book_description, include_katex_css, &chapters, index_out)?; - println!("Rendered index into `{}`", index_out_path.display()); - - // compile markdown and write the actual pages - let mut prev_chapter = None; - for (chapter_index, chapter) in chapters.iter().enumerate() { - // render the index - let chapter_root = dest.join(chapter.source.file_stem().map(std::ffi::OsStr::to_str).flatten().unwrap()); - let out = chapter_root.join("index.html"); - print!("Rendering `{}` into `{}`...", chapter.source.display(), out.display()); - fs::create_dir_all(&chapter_root)?; - - let outfile = fs::File::create(&out)?; - let outfile = io::BufWriter::new(outfile); - - let FormatResponse { output, include_katex_css } = format_markdown(&chapter.contents)?; - - let next_chapter = - if chapter.sections.len() > 0 { - Some(chapter.sections.iter().nth(0).expect("section 0 exists")) - } - else if chapter_index < chapters.len() - 1 { - Some(chapters.iter().nth(chapter_index + 1).expect("chapter n+1 exists")) - } - else { - None - }; - - format_page(&book_front, &chapter, &chapters, prev_chapter, next_chapter, &output, include_katex_css, outfile)?; - prev_chapter = Some(chapter); + use notify::{RecommendedWatcher, RecursiveMode, Watcher}; - println!(" done!"); - - // now the sections - for (section_index, section) in chapter.sections.iter().enumerate() { - let name = section.source.file_stem().map(std::ffi::OsStr::to_str).flatten().unwrap(); - let out = chapter_root.join(&format!("{}.html", name)); - print!("Rendering `{}` into `{}`...", section.source.display(), out.display()); - - let outfile = fs::File::create(&out)?; - let outfile = io::BufWriter::new(outfile); - - let FormatResponse { output, include_katex_css } = format_markdown(§ion.contents)?; - - let next_chapter = if section_index < chapter.sections.len() - 1 { - Some(chapter.sections.iter().nth(section_index + 1).expect("chapter n+1 exists")) - } - else if chapter_index < chapters.len() - 1 { - Some(chapters.iter().nth(chapter_index + 1).expect("chapter n+1 exists")) + let src = submatches.value_of("in").expect("in value"); + let dest = submatches.value_of("out").expect("out value"); + build(src, dest, do_reload)?; + + let (tx, rx) = std::sync::mpsc::channel(); + let mut watcher: RecommendedWatcher = Watcher::new(tx, std::time::Duration::from_secs(1))?; + watcher.watch(src, RecursiveMode::Recursive)?; + + loop { + match rx.recv() { + Ok(notify::DebouncedEvent::NoticeWrite(_)) | Ok(notify::DebouncedEvent::NoticeRemove(_)) => {}, + Ok(_) => { + build(src, dest, do_reload)?; + reload_trigger.store(true, std::sync::atomic::Ordering::SeqCst); + std::thread::sleep(std::time::Duration::from_millis(150)); + reload_trigger.store(false, std::sync::atomic::Ordering::SeqCst); + }, + Err(e) => { + log::error!("watch error: {:?}", e); + return Err(Box::from(e)); } - else { - None - }; - - format_page(&book_front, §ion, &chapters, prev_chapter, next_chapter, &output, include_katex_css, outfile)?; - prev_chapter = Some(section); - - println!(" done!"); } } - - // save the assets - fs::write(dest.join("style.css"), STYLESHEET)?; - println!("Wrote {}", dest.join("style.css").display()); - fs::write(dest.join("favicon.ico"), ASSET_FAVICON)?; - println!("Wrote {}", dest.join("favicon.ico").display()); - fs::write(dest.join("icons.svg"), ASSET_ICONS)?; - println!("Wrote {}", dest.join("icons.svg").display()); - - println!("Done!"); - Ok(()) } else { cli::build_cli().print_long_help()?; diff --git a/style/layout.scss b/style/layout.scss index 257b65b..ef36560 100644 --- a/style/layout.scss +++ b/style/layout.scss @@ -50,6 +50,7 @@ body { font-family: $font-sansserif; margin-bottom: 0; text-align: right; + color: #eeeeee; } } diff --git a/style/style.scss b/style/style.scss index bda2090..885e7f9 100644 --- a/style/style.scss +++ b/style/style.scss @@ -3,5 +3,5 @@ @import 'layout'; @import 'toc'; @import 'icons'; -@import 'darktheme'; +//@import 'darktheme'; @import 'print'; \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 8c84e3f..a61af0f 100644 --- a/templates/index.html +++ b/templates/index.html @@ -42,5 +42,8 @@

© {{ book.pubdate|year }} {{ book.author }}

+ {% if include_reload_script %} + {% include "reload.html" %} + {% endif %} \ No newline at end of file diff --git a/templates/page.html b/templates/page.html index d571ea6..bd9597c 100644 --- a/templates/page.html +++ b/templates/page.html @@ -99,5 +99,8 @@ {% endmatch %}

© {{ book.pubdate|year }} {{ book.author }}

+ {% if include_reload_script %} + {% include "reload.html" %} + {% endif %} \ No newline at end of file diff --git a/templates/reload.html b/templates/reload.html new file mode 100644 index 0000000..8832329 --- /dev/null +++ b/templates/reload.html @@ -0,0 +1,17 @@ + \ No newline at end of file