@@ -1,3 +1,8 @@ | |||||
[[package]] | |||||
name = "adler32" | |||||
version = "1.0.3" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
[[package]] | [[package]] | ||||
name = "aho-corasick" | name = "aho-corasick" | ||||
version = "0.6.4" | version = "0.6.4" | ||||
@@ -183,6 +188,11 @@ dependencies = [ | |||||
"cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", | "cc 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
] | ] | ||||
[[package]] | |||||
name = "color_quant" | |||||
version = "1.0.1" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
[[package]] | [[package]] | ||||
name = "config" | name = "config" | ||||
version = "0.1.0" | version = "0.1.0" | ||||
@@ -204,6 +214,7 @@ dependencies = [ | |||||
"errors 0.1.0", | "errors 0.1.0", | ||||
"front_matter 0.1.0", | "front_matter 0.1.0", | ||||
"globset 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | "globset 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"imageproc 0.1.0", | |||||
"rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | "rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"rendering 0.1.0", | "rendering 0.1.0", | ||||
"serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", | "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -254,6 +265,15 @@ dependencies = [ | |||||
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", | "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
] | ] | ||||
[[package]] | |||||
name = "deflate" | |||||
version = "0.7.18" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
dependencies = [ | |||||
"adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
] | |||||
[[package]] | [[package]] | ||||
name = "dtoa" | name = "dtoa" | ||||
version = "0.4.2" | version = "0.4.2" | ||||
@@ -290,6 +310,14 @@ dependencies = [ | |||||
"strum_macros 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", | "strum_macros 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
] | ] | ||||
[[package]] | |||||
name = "enum_primitive" | |||||
version = "0.1.1" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
dependencies = [ | |||||
"num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
] | |||||
[[package]] | [[package]] | ||||
name = "error-chain" | name = "error-chain" | ||||
version = "0.11.0" | version = "0.11.0" | ||||
@@ -303,6 +331,7 @@ name = "errors" | |||||
version = "0.1.0" | version = "0.1.0" | ||||
dependencies = [ | dependencies = [ | ||||
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", | "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"image 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"tera 0.11.8 (registry+https://github.com/rust-lang/crates.io-index)", | "tera 0.11.8 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", | "toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
] | ] | ||||
@@ -401,6 +430,15 @@ name = "getopts" | |||||
version = "0.2.17" | version = "0.2.17" | ||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
[[package]] | |||||
name = "gif" | |||||
version = "0.9.2" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
dependencies = [ | |||||
"color_quant 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
] | |||||
[[package]] | [[package]] | ||||
name = "glob" | name = "glob" | ||||
version = "0.2.11" | version = "0.2.11" | ||||
@@ -500,6 +538,44 @@ dependencies = [ | |||||
"unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", | "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
] | ] | ||||
[[package]] | |||||
name = "image" | |||||
version = "0.18.0" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
dependencies = [ | |||||
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"enum_primitive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"gif 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"jpeg-decoder 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"num-rational 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"png 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
] | |||||
[[package]] | |||||
name = "imageproc" | |||||
version = "0.1.0" | |||||
dependencies = [ | |||||
"errors 0.1.0", | |||||
"image 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"rayon 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"tera 0.11.8 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"utils 0.1.0", | |||||
] | |||||
[[package]] | |||||
name = "inflate" | |||||
version = "0.3.4" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
dependencies = [ | |||||
"adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
] | |||||
[[package]] | [[package]] | ||||
name = "inotify" | name = "inotify" | ||||
version = "0.3.0" | version = "0.3.0" | ||||
@@ -537,6 +613,15 @@ name = "itoa" | |||||
version = "0.4.1" | version = "0.4.1" | ||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
[[package]] | |||||
name = "jpeg-decoder" | |||||
version = "0.1.15" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
dependencies = [ | |||||
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
] | |||||
[[package]] | [[package]] | ||||
name = "kernel32-sys" | name = "kernel32-sys" | ||||
version = "0.2.2" | version = "0.2.2" | ||||
@@ -587,6 +672,11 @@ dependencies = [ | |||||
"cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", | "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
] | ] | ||||
[[package]] | |||||
name = "lzw" | |||||
version = "0.10.0" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
[[package]] | [[package]] | ||||
name = "mac" | name = "mac" | ||||
version = "0.1.1" | version = "0.1.1" | ||||
@@ -810,6 +900,32 @@ dependencies = [ | |||||
"num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", | "num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
] | ] | ||||
[[package]] | |||||
name = "num-iter" | |||||
version = "0.1.37" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
dependencies = [ | |||||
"num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
] | |||||
[[package]] | |||||
name = "num-rational" | |||||
version = "0.1.42" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
dependencies = [ | |||||
"num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
] | |||||
[[package]] | |||||
name = "num-traits" | |||||
version = "0.1.43" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
dependencies = [ | |||||
"num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
] | |||||
[[package]] | [[package]] | ||||
name = "num-traits" | name = "num-traits" | ||||
version = "0.2.5" | version = "0.2.5" | ||||
@@ -948,6 +1064,17 @@ dependencies = [ | |||||
"typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", | "typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
] | ] | ||||
[[package]] | |||||
name = "png" | |||||
version = "0.11.0" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
dependencies = [ | |||||
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"deflate 0.7.18 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"inflate 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
] | |||||
[[package]] | [[package]] | ||||
name = "precomputed-hash" | name = "precomputed-hash" | ||||
version = "0.1.1" | version = "0.1.1" | ||||
@@ -999,6 +1126,16 @@ dependencies = [ | |||||
"proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", | "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
] | ] | ||||
[[package]] | |||||
name = "rand" | |||||
version = "0.3.22" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
dependencies = [ | |||||
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
] | |||||
[[package]] | [[package]] | ||||
name = "rand" | name = "rand" | ||||
version = "0.4.2" | version = "0.4.2" | ||||
@@ -1009,6 +1146,15 @@ dependencies = [ | |||||
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", | "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
] | ] | ||||
[[package]] | |||||
name = "rayon" | |||||
version = "0.9.0" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
dependencies = [ | |||||
"either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
"rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
] | |||||
[[package]] | [[package]] | ||||
name = "rayon" | name = "rayon" | ||||
version = "1.0.1" | version = "1.0.1" | ||||
@@ -1175,6 +1321,11 @@ dependencies = [ | |||||
"pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", | "pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
] | ] | ||||
[[package]] | |||||
name = "scoped_threadpool" | |||||
version = "0.1.9" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
[[package]] | [[package]] | ||||
name = "scopeguard" | name = "scopeguard" | ||||
version = "0.3.3" | version = "0.3.3" | ||||
@@ -1250,6 +1401,7 @@ dependencies = [ | |||||
"errors 0.1.0", | "errors 0.1.0", | ||||
"front_matter 0.1.0", | "front_matter 0.1.0", | ||||
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", | "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"imageproc 0.1.0", | |||||
"pagination 0.1.0", | "pagination 0.1.0", | ||||
"rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | "rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"sass-rs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | "sass-rs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
@@ -1441,6 +1593,7 @@ dependencies = [ | |||||
"config 0.1.0", | "config 0.1.0", | ||||
"content 0.1.0", | "content 0.1.0", | ||||
"errors 0.1.0", | "errors 0.1.0", | ||||
"imageproc 0.1.0", | |||||
"lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", | "pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"taxonomies 0.1.0", | "taxonomies 0.1.0", | ||||
@@ -1544,6 +1697,14 @@ name = "traitobject" | |||||
version = "0.1.0" | version = "0.1.0" | ||||
source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
[[package]] | |||||
name = "twox-hash" | |||||
version = "1.1.0" | |||||
source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
dependencies = [ | |||||
"rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
] | |||||
[[package]] | [[package]] | ||||
name = "typeable" | name = "typeable" | ||||
version = "0.1.2" | version = "0.1.2" | ||||
@@ -1750,6 +1911,7 @@ dependencies = [ | |||||
] | ] | ||||
[metadata] | [metadata] | ||||
"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" | |||||
"checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" | "checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" | ||||
"checksum ammonia 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fd4c682378117e4186a492b2252b9537990e1617f44aed9788b9a1149de45477" | "checksum ammonia 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fd4c682378117e4186a492b2252b9537990e1617f44aed9788b9a1149de45477" | ||||
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" | "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" | ||||
@@ -1773,14 +1935,17 @@ dependencies = [ | |||||
"checksum chrono 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6962c635d530328acc53ac6a955e83093fedc91c5809dfac1fa60fa470830a37" | "checksum chrono 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6962c635d530328acc53ac6a955e83093fedc91c5809dfac1fa60fa470830a37" | ||||
"checksum clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536" | "checksum clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536" | ||||
"checksum cmake 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "95470235c31c726d72bf2e1f421adc1e65b9d561bf5529612cbe1a72da1467b3" | "checksum cmake 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "95470235c31c726d72bf2e1f421adc1e65b9d561bf5529612cbe1a72da1467b3" | ||||
"checksum color_quant 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd" | |||||
"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" | "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" | ||||
"checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" | "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" | ||||
"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" | "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" | ||||
"checksum ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "630391922b1b893692c6334369ff528dcc3a9d8061ccf4c803aa8f83cb13db5e" | "checksum ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "630391922b1b893692c6334369ff528dcc3a9d8061ccf4c803aa8f83cb13db5e" | ||||
"checksum deflate 0.7.18 (registry+https://github.com/rust-lang/crates.io-index)" = "32c8120d981901a9970a3a1c97cf8b630e0fa8c3ca31e75b6fd6fd5f9f427b31" | |||||
"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" | "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" | ||||
"checksum duct 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "166298c17c5b4fe5997b962c2f22e887c7c5adc44308eb9103ce5b66af45a423" | "checksum duct 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "166298c17c5b4fe5997b962c2f22e887c7c5adc44308eb9103ce5b66af45a423" | ||||
"checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" | "checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" | ||||
"checksum elasticlunr-rs 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4837d77a1e157489a3933b743fd774ae75074e0e390b2b7f071530048a0d87ee" | "checksum elasticlunr-rs 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4837d77a1e157489a3933b743fd774ae75074e0e390b2b7f071530048a0d87ee" | ||||
"checksum enum_primitive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180" | |||||
"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" | "checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" | ||||
"checksum filetime 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "714653f3e34871534de23771ac7b26e999651a0a228f47beb324dfdf1dd4b10f" | "checksum filetime 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "714653f3e34871534de23771ac7b26e999651a0a228f47beb324dfdf1dd4b10f" | ||||
"checksum flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fac2277e84e5e858483756647a9d0aa8d9a2b7cba517fd84325a0aaa69a0909" | "checksum flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fac2277e84e5e858483756647a9d0aa8d9a2b7cba517fd84325a0aaa69a0909" | ||||
@@ -1793,6 +1958,7 @@ dependencies = [ | |||||
"checksum futf 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7c9c1ce3fa9336301af935ab852c437817d14cd33690446569392e65170aac3b" | "checksum futf 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7c9c1ce3fa9336301af935ab852c437817d14cd33690446569392e65170aac3b" | ||||
"checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" | "checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" | ||||
"checksum getopts 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "b900c08c1939860ce8b54dc6a89e26e00c04c380fd0e09796799bd7f12861e05" | "checksum getopts 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "b900c08c1939860ce8b54dc6a89e26e00c04c380fd0e09796799bd7f12861e05" | ||||
"checksum gif 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2e41945ba23db3bf51b24756d73d81acb4f28d85c3dccc32c6fae904438c25f" | |||||
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" | "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" | ||||
"checksum globset 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "142754da2c9b3722affd909f9e27f2a6700a7a303f362971e0a74c652005a43d" | "checksum globset 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "142754da2c9b3722affd909f9e27f2a6700a7a303f362971e0a74c652005a43d" | ||||
"checksum html5ever 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b04478cf718862650a0bf66acaf8f2f8c906fbc703f35c916c1f4211b069a364" | "checksum html5ever 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b04478cf718862650a0bf66acaf8f2f8c906fbc703f35c916c1f4211b069a364" | ||||
@@ -1800,10 +1966,13 @@ dependencies = [ | |||||
"checksum humansize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6cab2627acfc432780848602f3f558f7e9dd427352224b0d9324025796d2a5e" | "checksum humansize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6cab2627acfc432780848602f3f558f7e9dd427352224b0d9324025796d2a5e" | ||||
"checksum hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "368cb56b2740ebf4230520e2b90ebb0461e69034d85d1945febd9b3971426db2" | "checksum hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "368cb56b2740ebf4230520e2b90ebb0461e69034d85d1945febd9b3971426db2" | ||||
"checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d" | "checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d" | ||||
"checksum image 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "545f000e8aa4e569e93f49c446987133452e0091c2494ac3efd3606aa3d309f2" | |||||
"checksum inflate 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f5f9f47468e9a76a6452271efadc88fe865a82be91fe75e6c0c57b87ccea59d4" | |||||
"checksum inotify 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887fcc180136e77a85e6a6128579a719027b1bab9b1c38ea4444244fe262c20c" | "checksum inotify 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887fcc180136e77a85e6a6128579a719027b1bab9b1c38ea4444244fe262c20c" | ||||
"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" | "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" | ||||
"checksum iron 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d8e17268922834707e1c29e8badbf9c712c9c43378e1b6a3388946baff10be2" | "checksum iron 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d8e17268922834707e1c29e8badbf9c712c9c43378e1b6a3388946baff10be2" | ||||
"checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682" | "checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682" | ||||
"checksum jpeg-decoder 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c8b7d43206b34b3f94ea9445174bda196e772049b9bddbc620c9d29b2d20110d" | |||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" | "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" | ||||
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" | "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" | ||||
"checksum lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e6412c5e2ad9584b0b8e979393122026cdd6d2a80b933f890dcd694ddbe73739" | "checksum lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e6412c5e2ad9584b0b8e979393122026cdd6d2a80b933f890dcd694ddbe73739" | ||||
@@ -1812,6 +1981,7 @@ dependencies = [ | |||||
"checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e" | "checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e" | ||||
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" | "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" | ||||
"checksum log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6fddaa003a65722a7fb9e26b0ce95921fe4ba590542ced664d8ce2fa26f9f3ac" | "checksum log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6fddaa003a65722a7fb9e26b0ce95921fe4ba590542ced664d8ce2fa26f9f3ac" | ||||
"checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" | |||||
"checksum mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" | "checksum mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" | ||||
"checksum maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08cbb6b4fef96b6d77bfc40ec491b1690c779e77b05cd9f07f787ed376fd4c43" | "checksum maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08cbb6b4fef96b6d77bfc40ec491b1690c779e77b05cd9f07f787ed376fd4c43" | ||||
"checksum markup5ever 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfedc97d5a503e96816d10fedcd5b42f760b2e525ce2f7ec71f6a41780548475" | "checksum markup5ever 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfedc97d5a503e96816d10fedcd5b42f760b2e525ce2f7ec71f6a41780548475" | ||||
@@ -1835,6 +2005,9 @@ dependencies = [ | |||||
"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" | "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" | ||||
"checksum notify 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5c3812da3098f210a0bb440f9c008471a031aa4c1de07a264fdd75456c95a4eb" | "checksum notify 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5c3812da3098f210a0bb440f9c008471a031aa4c1de07a264fdd75456c95a4eb" | ||||
"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" | "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" | ||||
"checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" | |||||
"checksum num-rational 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "ee314c74bd753fc86b4780aa9475da469155f3848473a261d2d18e35245a784e" | |||||
"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" | |||||
"checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe" | "checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe" | ||||
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" | "checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" | ||||
"checksum onig 3.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f5eeb268a4620c74ea5768c6d2ccd492d60a47a8754666b91a46bfc35cd4d1ba" | "checksum onig 3.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f5eeb268a4620c74ea5768c6d2ccd492d60a47a8754666b91a46bfc35cd4d1ba" | ||||
@@ -1850,6 +2023,7 @@ dependencies = [ | |||||
"checksum pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "110d5ee3593dbb73f56294327fe5668bcc997897097cbc76b51e7aed3f52452f" | "checksum pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "110d5ee3593dbb73f56294327fe5668bcc997897097cbc76b51e7aed3f52452f" | ||||
"checksum plist 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c61ac2afed2856590ae79d6f358a24b85ece246d2aa134741a66d589519b7503" | "checksum plist 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c61ac2afed2856590ae79d6f358a24b85ece246d2aa134741a66d589519b7503" | ||||
"checksum plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1a6a0dc3910bc8db877ffed8e457763b317cf880df4ae19109b9f77d277cf6e0" | "checksum plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1a6a0dc3910bc8db877ffed8e457763b317cf880df4ae19109b9f77d277cf6e0" | ||||
"checksum png 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f0b0cabbbd20c2d7f06dbf015e06aad59b6ca3d9ed14848783e98af9aaf19925" | |||||
"checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" | "checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" | ||||
"checksum proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1b06e2f335f48d24442b35a19df506a835fb3547bc3c06ef27340da9acf5cae7" | "checksum proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1b06e2f335f48d24442b35a19df506a835fb3547bc3c06ef27340da9acf5cae7" | ||||
"checksum proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "effdb53b25cdad54f8f48843d67398f7ef2e14f12c1b4cb4effc549a6462a4d6" | "checksum proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "effdb53b25cdad54f8f48843d67398f7ef2e14f12c1b4cb4effc549a6462a4d6" | ||||
@@ -1857,7 +2031,9 @@ dependencies = [ | |||||
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" | "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" | ||||
"checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8" | "checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8" | ||||
"checksum quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e44651a0dc4cdd99f71c83b561e221f714912d11af1a4dff0631f923d53af035" | "checksum quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e44651a0dc4cdd99f71c83b561e221f714912d11af1a4dff0631f923d53af035" | ||||
"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" | |||||
"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" | "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" | ||||
"checksum rayon 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed02d09394c94ffbdfdc755ad62a132e94c3224a8354e78a1200ced34df12edf" | |||||
"checksum rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80e811e76f1dbf68abf87a759083d34600017fc4e10b6bd5ad84a700f9dba4b1" | "checksum rayon 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80e811e76f1dbf68abf87a759083d34600017fc4e10b6bd5ad84a700f9dba4b1" | ||||
"checksum rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d24ad214285a7729b174ed6d3bcfcb80177807f959d95fafd5bfc5c4f201ac8" | "checksum rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d24ad214285a7729b174ed6d3bcfcb80177807f959d95fafd5bfc5c4f201ac8" | ||||
"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" | "checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" | ||||
@@ -1874,6 +2050,7 @@ dependencies = [ | |||||
"checksum same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfb6eded0b06a0b512c8ddbcf04089138c9b4362c2f696f3c3d76039d68f3637" | "checksum same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfb6eded0b06a0b512c8ddbcf04089138c9b4362c2f696f3c3d76039d68f3637" | ||||
"checksum sass-rs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90f8cf6e645aa843ffffcbdc1e8752b1f221dfa314c81895aeb229a77aea7e05" | "checksum sass-rs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90f8cf6e645aa843ffffcbdc1e8752b1f221dfa314c81895aeb229a77aea7e05" | ||||
"checksum sass-sys 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "bae88baa915f59c39820e544cfd9296d815a6b8efc3f276a78a0505866d2b4e9" | "checksum sass-sys 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "bae88baa915f59c39820e544cfd9296d815a6b8efc3f276a78a0505866d2b4e9" | ||||
"checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" | |||||
"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" | "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" | ||||
"checksum sequence_trie 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "32157204e5c9d3c04007bd7e56e96e987635ce0e8e23c085b1e403861b76c351" | "checksum sequence_trie 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "32157204e5c9d3c04007bd7e56e96e987635ce0e8e23c085b1e403861b76c351" | ||||
"checksum serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)" = "e9a2d9a9ac5120e0f768801ca2b58ad6eec929dc9d1d616c162f208869c2ce95" | "checksum serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)" = "e9a2d9a9ac5120e0f768801ca2b58ad6eec929dc9d1d616c162f208869c2ce95" | ||||
@@ -1909,6 +2086,7 @@ dependencies = [ | |||||
"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" | "checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" | ||||
"checksum toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a0263c6c02c4db6c8f7681f9fd35e90de799ebd4cfdeab77a38f4ff6b3d8c0d9" | "checksum toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a0263c6c02c4db6c8f7681f9fd35e90de799ebd4cfdeab77a38f4ff6b3d8c0d9" | ||||
"checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" | "checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" | ||||
"checksum twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "475352206e7a290c5fccc27624a163e8d0d115f7bb60ca18a64fc9ce056d7435" | |||||
"checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" | "checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" | ||||
"checksum typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6" | "checksum typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6" | ||||
"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" | "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" | ||||
@@ -53,4 +53,5 @@ members = [ | |||||
"components/templates", | "components/templates", | ||||
"components/utils", | "components/utils", | ||||
"components/search", | "components/search", | ||||
"components/imageproc", | |||||
] | ] |
@@ -14,6 +14,7 @@ config = { path = "../config" } | |||||
utils = { path = "../utils" } | utils = { path = "../utils" } | ||||
rendering = { path = "../rendering" } | rendering = { path = "../rendering" } | ||||
front_matter = { path = "../front_matter" } | front_matter = { path = "../front_matter" } | ||||
imageproc = { path = "../imageproc" } | |||||
[dev-dependencies] | [dev-dependencies] | ||||
tempfile = "3" | tempfile = "3" | ||||
@@ -7,6 +7,7 @@ extern crate errors; | |||||
extern crate config; | extern crate config; | ||||
extern crate front_matter; | extern crate front_matter; | ||||
extern crate rendering; | extern crate rendering; | ||||
extern crate imageproc; | |||||
extern crate utils; | extern crate utils; | ||||
#[cfg(test)] | #[cfg(test)] | ||||
@@ -15,6 +15,7 @@ use utils::site::get_reading_analytics; | |||||
use utils::templates::render_template; | use utils::templates::render_template; | ||||
use front_matter::{PageFrontMatter, InsertAnchor, split_page_content}; | use front_matter::{PageFrontMatter, InsertAnchor, split_page_content}; | ||||
use rendering::{RenderContext, Header, render_content}; | use rendering::{RenderContext, Header, render_content}; | ||||
use imageproc; | |||||
use file_info::FileInfo; | use file_info::FileInfo; | ||||
@@ -162,13 +163,16 @@ impl Page { | |||||
/// We need access to all pages url to render links relative to content | /// We need access to all pages url to render links relative to content | ||||
/// so that can't happen at the same time as parsing | /// so that can't happen at the same time as parsing | ||||
pub fn render_markdown(&mut self, permalinks: &HashMap<String, String>, tera: &Tera, config: &Config, anchor_insert: InsertAnchor) -> Result<()> { | pub fn render_markdown(&mut self, permalinks: &HashMap<String, String>, tera: &Tera, config: &Config, anchor_insert: InsertAnchor) -> Result<()> { | ||||
let context = RenderContext::new( | |||||
let mut context = RenderContext::new( | |||||
tera, | tera, | ||||
config, | config, | ||||
&self.permalink, | &self.permalink, | ||||
permalinks, | permalinks, | ||||
anchor_insert | anchor_insert | ||||
); | ); | ||||
context.teracontext.add("page", self); | |||||
let res = render_content( | let res = render_content( | ||||
&self.raw_content.replacen("<!-- more -->", "<a name=\"continue-reading\"></a>", 1), | &self.raw_content.replacen("<!-- more -->", "<a name=\"continue-reading\"></a>", 1), | ||||
&context | &context | ||||
@@ -202,6 +206,23 @@ impl Page { | |||||
render_template(&tpl_name, tera, &context, &config.theme) | render_template(&tpl_name, tera, &context, &config.theme) | ||||
.chain_err(|| format!("Failed to render page '{}'", self.file.path.display())) | .chain_err(|| format!("Failed to render page '{}'", self.file.path.display())) | ||||
} | } | ||||
/// Creates two vectors of asset URLs. The first one contains all asset files, | |||||
/// the second one that which appear to be an image file. | |||||
fn serialize_assets(&self) -> (Vec<String>, Vec<String>) { | |||||
let mut assets = vec![]; | |||||
let mut images = vec![]; | |||||
for asset in self.assets.iter() { | |||||
if let Some(filename) = asset.file_name().and_then(|f| f.to_str()) { | |||||
let url = self.path.clone() + filename; | |||||
if imageproc::file_is_img(&asset) { | |||||
images.push(url.clone()); | |||||
} | |||||
assets.push(url); | |||||
} | |||||
} | |||||
(assets, images) | |||||
} | |||||
} | } | ||||
impl Default for Page { | impl Default for Page { | ||||
@@ -246,6 +267,9 @@ impl ser::Serialize for Page { | |||||
state.serialize_field("next", &self.next)?; | state.serialize_field("next", &self.next)?; | ||||
state.serialize_field("toc", &self.toc)?; | state.serialize_field("toc", &self.toc)?; | ||||
state.serialize_field("draft", &self.is_draft())?; | state.serialize_field("draft", &self.is_draft())?; | ||||
let (assets, assets_imgs) = self.serialize_assets(); | |||||
state.serialize_field("assets", &assets)?; | |||||
state.serialize_field("assets_imgs", &assets_imgs)?; | |||||
state.end() | state.end() | ||||
} | } | ||||
} | } | ||||
@@ -98,13 +98,16 @@ impl Section { | |||||
/// We need access to all pages url to render links relative to content | /// We need access to all pages url to render links relative to content | ||||
/// so that can't happen at the same time as parsing | /// so that can't happen at the same time as parsing | ||||
pub fn render_markdown(&mut self, permalinks: &HashMap<String, String>, tera: &Tera, config: &Config) -> Result<()> { | pub fn render_markdown(&mut self, permalinks: &HashMap<String, String>, tera: &Tera, config: &Config) -> Result<()> { | ||||
let context = RenderContext::new( | |||||
let mut context = RenderContext::new( | |||||
tera, | tera, | ||||
config, | config, | ||||
&self.permalink, | &self.permalink, | ||||
permalinks, | permalinks, | ||||
self.meta.insert_anchor_links, | self.meta.insert_anchor_links, | ||||
); | ); | ||||
context.teracontext.add("section", self); | |||||
let res = render_content(&self.raw_content, &context) | let res = render_content(&self.raw_content, &context) | ||||
.chain_err(|| format!("Failed to render content of {}", self.file.path.display()))?; | .chain_err(|| format!("Failed to render content of {}", self.file.path.display()))?; | ||||
self.content = res.0; | self.content = res.0; | ||||
@@ -7,3 +7,4 @@ authors = ["Vincent Prouillet <prouillet.vincent@gmail.com>"] | |||||
error-chain = "0.11" | error-chain = "0.11" | ||||
tera = "0.11" | tera = "0.11" | ||||
toml = "0.4" | toml = "0.4" | ||||
image = "0.18.0" |
@@ -4,6 +4,7 @@ | |||||
extern crate error_chain; | extern crate error_chain; | ||||
extern crate tera; | extern crate tera; | ||||
extern crate toml; | extern crate toml; | ||||
extern crate image; | |||||
error_chain! { | error_chain! { | ||||
errors {} | errors {} | ||||
@@ -15,6 +16,7 @@ error_chain! { | |||||
foreign_links { | foreign_links { | ||||
Io(::std::io::Error); | Io(::std::io::Error); | ||||
Toml(toml::de::Error); | Toml(toml::de::Error); | ||||
Image(image::ImageError); | |||||
} | } | ||||
} | } | ||||
@@ -0,0 +1,15 @@ | |||||
[package] | |||||
name = "imageproc" | |||||
version = "0.1.0" | |||||
authors = ["Vojtěch Král <vojtech@kral.hk>"] | |||||
[dependencies] | |||||
lazy_static = "1" | |||||
regex = "0.2" | |||||
tera = "0.11.0" | |||||
image = "0.18.0" | |||||
rayon = "0.9" | |||||
twox-hash = "1.1" | |||||
errors = { path = "../errors" } | |||||
utils = { path = "../utils" } |
@@ -0,0 +1,327 @@ | |||||
#[macro_use] | |||||
extern crate lazy_static; | |||||
extern crate regex; | |||||
extern crate image; | |||||
extern crate rayon; | |||||
extern crate twox_hash; | |||||
extern crate utils; | |||||
extern crate errors; | |||||
use std::path::{Path, PathBuf}; | |||||
use std::hash::{Hash, Hasher}; | |||||
use std::collections::HashMap; | |||||
use std::collections::hash_map::Entry as HEntry; | |||||
use std::fs::{self, File}; | |||||
use regex::Regex; | |||||
use image::{GenericImage, FilterType}; | |||||
use image::jpeg::JPEGEncoder; | |||||
use rayon::prelude::*; | |||||
use twox_hash::XxHash; | |||||
use utils::fs as ufs; | |||||
use errors::{Result, ResultExt}; | |||||
static RESIZED_SUBDIR: &'static str = "_resized_images"; | |||||
lazy_static!{ | |||||
pub static ref RESIZED_FILENAME: Regex = Regex::new(r#"([0-9a-f]{16})([0-9a-f]{2})[.]jpg"#).unwrap(); | |||||
} | |||||
/// Describes the precise kind of a resize operation | |||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | |||||
pub enum ResizeOp { | |||||
/// A simple scale operation that doesn't take aspect ratio into account | |||||
Scale(u32, u32), | |||||
/// Scales the image to a specified width with height computed such that aspect ratio is preserved | |||||
FitWidth(u32), | |||||
/// Scales the image to a specified height with width computed such that aspect ratio is preserved | |||||
FitHeight(u32), | |||||
/// Scales the image such that it fits within the specified width and height preserving aspect ratio. | |||||
/// Either dimension may end up being smaller, but never larger than specified. | |||||
Fit(u32, u32), | |||||
/// Scales the image such that it fills the specified width and height. Output will always have the exact dimensions specified. | |||||
/// The part of the image that doesn't fit in the thumbnail due to differing aspect ratio will be cropped away, if any. | |||||
Fill(u32, u32), | |||||
} | |||||
impl ResizeOp { | |||||
pub fn from_args(op: &str, width: Option<u32>, height: Option<u32>) -> Result<ResizeOp> { | |||||
use ResizeOp::*; | |||||
// Validate args: | |||||
match op { | |||||
"fitwidth" => if width.is_none() { return Err(format!("op=fitwidth requires a `width` argument").into()) }, | |||||
"fitheight" => if height.is_none() { return Err(format!("op=fitwidth requires a `height` argument").into()) }, | |||||
"scale" | "fit" | "fill" => if width.is_none() || height.is_none() { | |||||
return Err(format!("op={} requires a `width` and `height` argument", op).into()) | |||||
}, | |||||
_ => return Err(format!("Invalid image resize operation: {}", op).into()) | |||||
}; | |||||
Ok(match op { | |||||
"scale" => Scale(width.unwrap(), height.unwrap()), | |||||
"fitwidth" => FitWidth(width.unwrap()), | |||||
"fitheight" => FitHeight(height.unwrap()), | |||||
"fit" => Fit(width.unwrap(), height.unwrap()), | |||||
"fill" => Fill(width.unwrap(), height.unwrap()), | |||||
_ => unreachable!(), | |||||
}) | |||||
} | |||||
pub fn width(self) -> Option<u32> { | |||||
use ResizeOp::*; | |||||
match self { | |||||
Scale(w, _) => Some(w), | |||||
FitWidth(w) => Some(w), | |||||
FitHeight(_) => None, | |||||
Fit(w, _) => Some(w), | |||||
Fill(w, _) => Some(w), | |||||
} | |||||
} | |||||
pub fn height(self) -> Option<u32> { | |||||
use ResizeOp::*; | |||||
match self { | |||||
Scale(_, h) => Some(h), | |||||
FitWidth(_) => None, | |||||
FitHeight(h) => Some(h), | |||||
Fit(_, h) => Some(h), | |||||
Fill(_, h) => Some(h), | |||||
} | |||||
} | |||||
} | |||||
impl From<ResizeOp> for u8 { | |||||
fn from(op: ResizeOp) -> u8 { | |||||
use ResizeOp::*; | |||||
match op { | |||||
Scale(_, _) => 1, | |||||
FitWidth(_) => 2, | |||||
FitHeight(_) => 3, | |||||
Fit(_, _) => 4, | |||||
Fill(_, _) => 5, | |||||
} | |||||
} | |||||
} | |||||
impl Hash for ResizeOp { | |||||
fn hash<H: Hasher>(&self, hasher: &mut H) { | |||||
hasher.write_u8(u8::from(*self)); | |||||
if let Some(w) = self.width() { hasher.write_u32(w); } | |||||
if let Some(h) = self.height() { hasher.write_u32(h); } | |||||
} | |||||
} | |||||
/// Holds all data needed to perform a resize operation | |||||
#[derive(Debug, PartialEq, Eq)] | |||||
pub struct ImageOp { | |||||
source: String, | |||||
op: ResizeOp, | |||||
quality: u8, | |||||
hash: u64, | |||||
collision: Option<u32>, | |||||
} | |||||
impl ImageOp { | |||||
pub fn new(source: String, op: ResizeOp, quality: u8) -> ImageOp { | |||||
let mut hasher = XxHash::with_seed(0); | |||||
hasher.write(source.as_ref()); | |||||
op.hash(&mut hasher); | |||||
hasher.write_u8(quality); | |||||
let hash = hasher.finish(); | |||||
ImageOp { source, op, quality, hash, collision: None } | |||||
} | |||||
pub fn from_args(source: String, op: &str, width: Option<u32>, height: Option<u32>, quality: u8) -> Result<ImageOp> { | |||||
let op = ResizeOp::from_args(op, width, height)?; | |||||
Ok(Self::new(source, op, quality)) | |||||
} | |||||
fn num_colli(&self) -> u32 { self.collision.unwrap_or(0) } | |||||
fn perform(&self, content_path: &Path, target_path: &Path) -> Result<()> { | |||||
use ResizeOp::*; | |||||
let src_path = content_path.join(&self.source); | |||||
if !ufs::file_stale(&src_path, target_path) { | |||||
return Ok(()) | |||||
} | |||||
let mut img = image::open(&src_path)?; | |||||
let (img_w, img_h) = img.dimensions(); | |||||
const RESIZE_FILTER: FilterType = FilterType::Gaussian; | |||||
const RATIO_EPSILLION: f32 = 0.1; | |||||
let img = match self.op { | |||||
Scale(w, h) => img.resize_exact(w, h, RESIZE_FILTER), | |||||
FitWidth(w) => img.resize(w, u32::max_value(), RESIZE_FILTER), | |||||
FitHeight(h) => img.resize(u32::max_value(), h, RESIZE_FILTER), | |||||
Fit(w, h) => img.resize(w, h, RESIZE_FILTER), | |||||
Fill(w, h) => { | |||||
let fw = img_w as f32 / w as f32; | |||||
let fh = img_h as f32 / h as f32; | |||||
if (fw - fh).abs() <= RATIO_EPSILLION { | |||||
// The aspect is similar enough that there's not much point in cropping | |||||
img.resize_exact(w, h, RESIZE_FILTER) | |||||
} else { | |||||
// We perform the fill such that a crop is performed first and then resize_exact can be used, | |||||
// which should be cheaper than resizing and then cropping (smaller number of pixels to resize). | |||||
let (crop_w, crop_h) = match fw < fh { | |||||
true => (img_w, (fw * h as f32).round() as u32), | |||||
false => ((fh * w as f32).round() as u32, img_h), | |||||
}; | |||||
let (off_w, off_h) = match fw < fh { | |||||
true => (0, (img_h - crop_h) / 2), | |||||
false => ((img_w - crop_w) / 2, 0), | |||||
}; | |||||
img.crop(off_w, off_h, crop_w, crop_h).resize_exact(w, h, RESIZE_FILTER) | |||||
} | |||||
}, | |||||
}; | |||||
let mut f = File::create(target_path)?; | |||||
let mut enc = JPEGEncoder::new_with_quality(&mut f, self.quality); | |||||
let (img_w, img_h) = img.dimensions(); | |||||
enc.encode(&img.raw_pixels(), img_w, img_h, img.color())?; | |||||
Ok(()) | |||||
} | |||||
} | |||||
/// A strcture into which image operations can be enqueued and then performed. | |||||
/// All output is written in a subdirectory in `static_path`, | |||||
/// taking care of file stale status based on timestamps and possible hash collisions. | |||||
#[derive(Debug)] | |||||
pub struct Processor { | |||||
content_path: PathBuf, | |||||
resized_path: PathBuf, | |||||
resized_url: String, | |||||
img_ops: HashMap<u64, ImageOp>, | |||||
// Hash collisions go here: | |||||
img_ops_colls: Vec<ImageOp>, | |||||
} | |||||
impl Processor { | |||||
pub fn new(content_path: PathBuf, static_path: &Path, base_url: &str) -> Processor { | |||||
Processor { | |||||
content_path, | |||||
resized_path: static_path.join(RESIZED_SUBDIR), | |||||
resized_url: Self::resized_url(base_url), | |||||
img_ops: HashMap::new(), | |||||
img_ops_colls: Vec::new(), | |||||
} | |||||
} | |||||
fn resized_url(base_url: &str) -> String { | |||||
match base_url.ends_with('/') { | |||||
true => format!("{}{}", base_url, RESIZED_SUBDIR), | |||||
false => format!("{}/{}", base_url, RESIZED_SUBDIR), | |||||
} | |||||
} | |||||
pub fn set_base_url(&mut self, base_url: &str) { | |||||
self.resized_url = Self::resized_url(base_url); | |||||
} | |||||
pub fn source_exists(&self, source: &str) -> bool { | |||||
self.content_path.join(source).exists() | |||||
} | |||||
pub fn num_img_ops(&self) -> usize { | |||||
self.img_ops.len() + self.img_ops_colls.len() | |||||
} | |||||
fn insert_with_colls(&mut self, mut img_op: ImageOp) -> u32 { | |||||
match self.img_ops.entry(img_op.hash) { | |||||
HEntry::Occupied(entry) => if *entry.get() == img_op { return 0; }, | |||||
HEntry::Vacant(entry) => { | |||||
entry.insert(img_op); | |||||
return 0; | |||||
}, | |||||
} | |||||
// If we get here, that means a hash collision. | |||||
let mut num = 1; | |||||
for op in self.img_ops_colls.iter().filter(|op| op.hash == img_op.hash) { | |||||
if *op == img_op { | |||||
return num; | |||||
} else { | |||||
num += 1; | |||||
} | |||||
} | |||||
if num == 1 { | |||||
self.img_ops.get_mut(&img_op.hash).unwrap().collision = Some(0); | |||||
} | |||||
img_op.collision = Some(num); | |||||
self.img_ops_colls.push(img_op); | |||||
num | |||||
} | |||||
fn op_filename(hash: u64, colli_num: u32) -> String { | |||||
// Please keep this in sync with RESIZED_FILENAME | |||||
assert!(colli_num < 256, "Unexpectedly large number of collisions: {}", colli_num); | |||||
format!("{:016x}{:02x}.jpg", hash, colli_num) | |||||
} | |||||
fn op_url(&self, hash: u64, colli_num: u32) -> String { | |||||
format!("{}/{}", &self.resized_url, Self::op_filename(hash, colli_num)) | |||||
} | |||||
pub fn insert(&mut self, img_op: ImageOp) -> String { | |||||
let hash = img_op.hash; | |||||
let num_colli = self.insert_with_colls(img_op); | |||||
self.op_url(hash, num_colli) | |||||
} | |||||
pub fn prune(&self) -> Result<()> { | |||||
ufs::ensure_directory_exists(&self.resized_path)?; | |||||
let entries = fs::read_dir(&self.resized_path)?; | |||||
for entry in entries { | |||||
let entry_path = entry?.path(); | |||||
if entry_path.is_file() { | |||||
let filename = entry_path.file_name().unwrap().to_string_lossy(); | |||||
if let Some(capts) = RESIZED_FILENAME.captures(filename.as_ref()) { | |||||
let hash = u64::from_str_radix(capts.get(1).unwrap().as_str(), 16).unwrap(); | |||||
let num_colli = u32::from_str_radix(capts.get(2).unwrap().as_str(), 16).unwrap(); | |||||
if num_colli > 0 || !self.img_ops.contains_key(&hash) { | |||||
fs::remove_file(&entry_path)?; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
Ok(()) | |||||
} | |||||
pub fn do_process(&mut self) -> Result<()> { | |||||
self.img_ops.par_iter().map(|(hash, op)| { | |||||
let target = self.resized_path.join(Self::op_filename(*hash, op.num_colli())); | |||||
op.perform(&self.content_path, &target) | |||||
.chain_err(|| format!("Failed to process image: {}", op.source)) | |||||
}) | |||||
.fold(|| Ok(()), Result::and) | |||||
.reduce(|| Ok(()), Result::and) | |||||
} | |||||
} | |||||
/// Looks at file's extension and returns whether it's a supported image format | |||||
pub fn file_is_img<P: AsRef<Path>>(p: P) -> bool { | |||||
p.as_ref().extension().and_then(|s| s.to_str()).map(|ext| { | |||||
match ext.to_lowercase().as_str() { | |||||
"jpg" | "jpeg" => true, | |||||
"png" => true, | |||||
"gif" => true, | |||||
"bmp" => true, | |||||
_ => false, | |||||
} | |||||
}).unwrap_or(false) | |||||
} |
@@ -1,6 +1,6 @@ | |||||
use std::collections::HashMap; | use std::collections::HashMap; | ||||
use tera::Tera; | |||||
use tera::{Tera, Context}; | |||||
use front_matter::InsertAnchor; | use front_matter::InsertAnchor; | ||||
use config::Config; | use config::Config; | ||||
@@ -10,6 +10,7 @@ use config::Config; | |||||
pub struct RenderContext<'a> { | pub struct RenderContext<'a> { | ||||
pub tera: &'a Tera, | pub tera: &'a Tera, | ||||
pub config: &'a Config, | pub config: &'a Config, | ||||
pub teracontext: Context, | |||||
pub current_page_permalink: &'a str, | pub current_page_permalink: &'a str, | ||||
pub permalinks: &'a HashMap<String, String>, | pub permalinks: &'a HashMap<String, String>, | ||||
pub insert_anchor: InsertAnchor, | pub insert_anchor: InsertAnchor, | ||||
@@ -23,8 +24,11 @@ impl<'a> RenderContext<'a> { | |||||
permalinks: &'a HashMap<String, String>, | permalinks: &'a HashMap<String, String>, | ||||
insert_anchor: InsertAnchor, | insert_anchor: InsertAnchor, | ||||
) -> RenderContext<'a> { | ) -> RenderContext<'a> { | ||||
let mut teracontext = Context::new(); | |||||
teracontext.insert("config", config); | |||||
RenderContext { | RenderContext { | ||||
tera, | tera, | ||||
teracontext, | |||||
current_page_permalink, | current_page_permalink, | ||||
permalinks, | permalinks, | ||||
insert_anchor, | insert_anchor, | ||||
@@ -34,7 +34,7 @@ pub use context::RenderContext; | |||||
pub fn render_content(content: &str, context: &RenderContext) -> Result<(String, Vec<Header>)> { | pub fn render_content(content: &str, context: &RenderContext) -> Result<(String, Vec<Header>)> { | ||||
// Don't do anything if there is nothing like a shortcode in the content | // Don't do anything if there is nothing like a shortcode in the content | ||||
if content.contains("{{") || content.contains("{%") { | if content.contains("{{") || content.contains("{%") { | ||||
let rendered = render_shortcodes(content, context.tera, context.config)?; | |||||
let rendered = render_shortcodes(content, context)?; | |||||
return markdown_to_html(&rendered, context); | return markdown_to_html(&rendered, context); | ||||
} | } | ||||
@@ -1,9 +1,9 @@ | |||||
use pest::Parser; | use pest::Parser; | ||||
use pest::iterators::Pair; | use pest::iterators::Pair; | ||||
use tera::{Tera, Map, Context, Value, to_value}; | |||||
use tera::{Map, Context, Value, to_value}; | |||||
use errors::{Result, ResultExt}; | use errors::{Result, ResultExt}; | ||||
use config::Config; | |||||
use ::context::RenderContext; | |||||
// This include forces recompiling this source file if the grammar file changes. | // This include forces recompiling this source file if the grammar file changes. | ||||
// Uncomment it when doing changes to the .pest file | // Uncomment it when doing changes to the .pest file | ||||
@@ -84,20 +84,20 @@ fn parse_shortcode_call(pair: Pair<Rule>) -> (String, Map<String, Value>) { | |||||
} | } | ||||
fn render_shortcode(name: String, args: Map<String, Value>, tera: &Tera, config: &Config, body: Option<&str>) -> Result<String> { | |||||
let mut context = Context::new(); | |||||
fn render_shortcode(name: String, args: Map<String, Value>, context: &RenderContext, body: Option<&str>) -> Result<String> { | |||||
let mut teracontext = Context::new(); | |||||
for (key, value) in args.iter() { | for (key, value) in args.iter() { | ||||
context.insert(key, value); | |||||
teracontext.insert(key, value); | |||||
} | } | ||||
if let Some(ref b) = body { | if let Some(ref b) = body { | ||||
// Trimming right to avoid most shortcodes with bodies ending up with a HTML new line | // Trimming right to avoid most shortcodes with bodies ending up with a HTML new line | ||||
context.insert("body", b.trim_right()); | |||||
teracontext.insert("body", b.trim_right()); | |||||
} | } | ||||
context.insert("config", config); | |||||
teracontext.extend(context.teracontext.clone()); | |||||
let tpl_name = format!("shortcodes/{}.html", name); | let tpl_name = format!("shortcodes/{}.html", name); | ||||
let res = tera | |||||
.render(&tpl_name, &context) | |||||
let res = context.tera | |||||
.render(&tpl_name, &teracontext) | |||||
.chain_err(|| format!("Failed to render {} shortcode", name))?; | .chain_err(|| format!("Failed to render {} shortcode", name))?; | ||||
// We trim left every single line of a shortcode to avoid the accidental | // We trim left every single line of a shortcode to avoid the accidental | ||||
@@ -105,7 +105,7 @@ fn render_shortcode(name: String, args: Map<String, Value>, tera: &Tera, config: | |||||
Ok(res.lines().map(|s| s.trim_left()).collect()) | Ok(res.lines().map(|s| s.trim_left()).collect()) | ||||
} | } | ||||
pub fn render_shortcodes(content: &str, tera: &Tera, config: &Config) -> Result<String> { | |||||
pub fn render_shortcodes(content: &str, context: &RenderContext) -> Result<String> { | |||||
let mut res = String::with_capacity(content.len()); | let mut res = String::with_capacity(content.len()); | ||||
let mut pairs = match ContentParser::parse(Rule::page, content) { | let mut pairs = match ContentParser::parse(Rule::page, content) { | ||||
@@ -138,7 +138,7 @@ pub fn render_shortcodes(content: &str, tera: &Tera, config: &Config) -> Result< | |||||
Rule::text | Rule::text_in_ignored_body_sc | Rule::text_in_body_sc => res.push_str(p.into_span().as_str()), | Rule::text | Rule::text_in_ignored_body_sc | Rule::text_in_body_sc => res.push_str(p.into_span().as_str()), | ||||
Rule::inline_shortcode => { | Rule::inline_shortcode => { | ||||
let (name, args) = parse_shortcode_call(p); | let (name, args) = parse_shortcode_call(p); | ||||
res.push_str(&render_shortcode(name, args, tera, config, None)?); | |||||
res.push_str(&render_shortcode(name, args, context, None)?); | |||||
}, | }, | ||||
Rule::shortcode_with_body => { | Rule::shortcode_with_body => { | ||||
let mut inner = p.into_inner(); | let mut inner = p.into_inner(); | ||||
@@ -146,7 +146,7 @@ pub fn render_shortcodes(content: &str, tera: &Tera, config: &Config) -> Result< | |||||
// we don't care about the closing tag | // we don't care about the closing tag | ||||
let (name, args) = parse_shortcode_call(inner.next().unwrap()); | let (name, args) = parse_shortcode_call(inner.next().unwrap()); | ||||
let body = inner.next().unwrap().into_span().as_str(); | let body = inner.next().unwrap().into_span().as_str(); | ||||
res.push_str(&render_shortcode(name, args, tera, config, Some(body))?); | |||||
res.push_str(&render_shortcode(name, args, context, Some(body))?); | |||||
}, | }, | ||||
Rule::ignored_inline_shortcode => { | Rule::ignored_inline_shortcode => { | ||||
res.push_str( | res.push_str( | ||||
@@ -179,6 +179,10 @@ pub fn render_shortcodes(content: &str, tera: &Tera, config: &Config) -> Result< | |||||
#[cfg(test)] | #[cfg(test)] | ||||
mod tests { | mod tests { | ||||
use std::collections::HashMap; | |||||
use tera::Tera; | |||||
use config::Config; | |||||
use front_matter::InsertAnchor; | |||||
use super::*; | use super::*; | ||||
macro_rules! assert_lex_rule { | macro_rules! assert_lex_rule { | ||||
@@ -195,6 +199,13 @@ mod tests { | |||||
}; | }; | ||||
} | } | ||||
fn render_shortcodes(code: &str, tera: &Tera) -> String { | |||||
let config = Config::default(); | |||||
let permalinks = HashMap::new(); | |||||
let context = RenderContext::new(&tera, &config, "", &permalinks, InsertAnchor::None); | |||||
super::render_shortcodes(code, &context).unwrap() | |||||
} | |||||
#[test] | #[test] | ||||
fn lex_text() { | fn lex_text() { | ||||
let inputs = vec!["Hello world", "HEllo \n world", "Hello 1 2 true false 'hey'"]; | let inputs = vec!["Hello world", "HEllo \n world", "Hello 1 2 true false 'hey'"]; | ||||
@@ -281,26 +292,22 @@ mod tests { | |||||
#[test] | #[test] | ||||
fn does_nothing_with_no_shortcodes() { | fn does_nothing_with_no_shortcodes() { | ||||
let res = render_shortcodes("Hello World", &Tera::default(), &Config::default()); | |||||
assert_eq!(res.unwrap(), "Hello World"); | |||||
let res = render_shortcodes("Hello World", &Tera::default()); | |||||
assert_eq!(res, "Hello World"); | |||||
} | } | ||||
#[test] | #[test] | ||||
fn can_unignore_inline_shortcode() { | fn can_unignore_inline_shortcode() { | ||||
let res = render_shortcodes( | |||||
"Hello World {{/* youtube() */}}", | |||||
&Tera::default(), | |||||
&Config::default(), | |||||
); | |||||
assert_eq!(res.unwrap(), "Hello World {{ youtube() }}"); | |||||
let res = render_shortcodes("Hello World {{/* youtube() */}}", &Tera::default()); | |||||
assert_eq!(res, "Hello World {{ youtube() }}"); | |||||
} | } | ||||
#[test] | #[test] | ||||
fn can_unignore_shortcode_with_body() { | fn can_unignore_shortcode_with_body() { | ||||
let res = render_shortcodes(r#" | let res = render_shortcodes(r#" | ||||
Hello World | Hello World | ||||
{%/* youtube() */%}Some body {{ hello() }}{%/* end */%}"#, &Tera::default(), &Config::default()); | |||||
assert_eq!(res.unwrap(), "\nHello World\n{% youtube() %}Some body {{ hello() }}{% end %}"); | |||||
{%/* youtube() */%}Some body {{ hello() }}{%/* end */%}"#, &Tera::default()); | |||||
assert_eq!(res, "\nHello World\n{% youtube() %}Some body {{ hello() }}{% end %}"); | |||||
} | } | ||||
#[test] | #[test] | ||||
@@ -343,7 +350,7 @@ Hello World | |||||
fn can_render_inline_shortcodes() { | fn can_render_inline_shortcodes() { | ||||
let mut tera = Tera::default(); | let mut tera = Tera::default(); | ||||
tera.add_raw_template("shortcodes/youtube.html", "Hello {{id}}").unwrap(); | tera.add_raw_template("shortcodes/youtube.html", "Hello {{id}}").unwrap(); | ||||
let res = render_shortcodes("Inline {{ youtube(id=1) }}.", &tera, &Config::default()).unwrap(); | |||||
let res = render_shortcodes("Inline {{ youtube(id=1) }}.", &tera); | |||||
assert_eq!(res, "Inline Hello 1."); | assert_eq!(res, "Inline Hello 1."); | ||||
} | } | ||||
@@ -351,7 +358,7 @@ Hello World | |||||
fn can_render_shortcodes_with_body() { | fn can_render_shortcodes_with_body() { | ||||
let mut tera = Tera::default(); | let mut tera = Tera::default(); | ||||
tera.add_raw_template("shortcodes/youtube.html", "{{body}}").unwrap(); | tera.add_raw_template("shortcodes/youtube.html", "{{body}}").unwrap(); | ||||
let res = render_shortcodes("Body\n {% youtube() %}Hey!{% end %}", &tera, &Config::default()).unwrap(); | |||||
let res = render_shortcodes("Body\n {% youtube() %}Hey!{% end %}", &tera); | |||||
assert_eq!(res, "Body\n Hey!"); | assert_eq!(res, "Body\n Hey!"); | ||||
} | } | ||||
} | } |
@@ -20,6 +20,7 @@ pagination = { path = "../pagination" } | |||||
taxonomies = { path = "../taxonomies" } | taxonomies = { path = "../taxonomies" } | ||||
content = { path = "../content" } | content = { path = "../content" } | ||||
search = { path = "../search" } | search = { path = "../search" } | ||||
imageproc = { path = "../imageproc" } | |||||
[dev-dependencies] | [dev-dependencies] | ||||
tempfile = "3" | tempfile = "3" |
@@ -16,6 +16,7 @@ extern crate pagination; | |||||
extern crate taxonomies; | extern crate taxonomies; | ||||
extern crate content; | extern crate content; | ||||
extern crate search; | extern crate search; | ||||
extern crate imageproc; | |||||
#[cfg(test)] | #[cfg(test)] | ||||
extern crate tempfile; | extern crate tempfile; | ||||
@@ -24,6 +25,7 @@ use std::collections::HashMap; | |||||
use std::fs::{create_dir_all, remove_dir_all, copy}; | use std::fs::{create_dir_all, remove_dir_all, copy}; | ||||
use std::mem; | use std::mem; | ||||
use std::path::{Path, PathBuf}; | use std::path::{Path, PathBuf}; | ||||
use std::sync::{Arc, Mutex}; | |||||
use glob::glob; | use glob::glob; | ||||
use tera::{Tera, Context}; | use tera::{Tera, Context}; | ||||
@@ -66,9 +68,11 @@ pub struct Site { | |||||
pub pages: HashMap<PathBuf, Page>, | pub pages: HashMap<PathBuf, Page>, | ||||
pub sections: HashMap<PathBuf, Section>, | pub sections: HashMap<PathBuf, Section>, | ||||
pub tera: Tera, | pub tera: Tera, | ||||
imageproc: Arc<Mutex<imageproc::Processor>>, | |||||
// the live reload port to be used if there is one | // the live reload port to be used if there is one | ||||
pub live_reload: Option<u16>, | pub live_reload: Option<u16>, | ||||
pub output_path: PathBuf, | pub output_path: PathBuf, | ||||
content_path: PathBuf, | |||||
pub static_path: PathBuf, | pub static_path: PathBuf, | ||||
pub tags: Option<Taxonomy>, | pub tags: Option<Taxonomy>, | ||||
pub categories: Option<Taxonomy>, | pub categories: Option<Taxonomy>, | ||||
@@ -109,15 +113,21 @@ impl Site { | |||||
// the `extend` above already does it but hey | // the `extend` above already does it but hey | ||||
tera.build_inheritance_chains()?; | tera.build_inheritance_chains()?; | ||||
let content_path = path.join("content"); | |||||
let static_path = path.join("static"); | |||||
let imageproc = imageproc::Processor::new(content_path.clone(), &static_path, &config.base_url); | |||||
let site = Site { | let site = Site { | ||||
base_path: path.to_path_buf(), | base_path: path.to_path_buf(), | ||||
config, | config, | ||||
tera, | tera, | ||||
pages: HashMap::new(), | pages: HashMap::new(), | ||||
sections: HashMap::new(), | sections: HashMap::new(), | ||||
imageproc: Arc::new(Mutex::new(imageproc)), | |||||
live_reload: None, | live_reload: None, | ||||
output_path: path.join("public"), | output_path: path.join("public"), | ||||
static_path: path.join("static"), | |||||
content_path, | |||||
static_path, | |||||
tags: None, | tags: None, | ||||
categories: None, | categories: None, | ||||
permalinks: HashMap::new(), | permalinks: HashMap::new(), | ||||
@@ -128,7 +138,7 @@ impl Site { | |||||
/// The index section is ALWAYS at that path | /// The index section is ALWAYS at that path | ||||
pub fn index_section_path(&self) -> PathBuf { | pub fn index_section_path(&self) -> PathBuf { | ||||
self.base_path.join("content").join("_index.md") | |||||
self.content_path.join("_index.md") | |||||
} | } | ||||
pub fn enable_live_reload(&mut self) { | pub fn enable_live_reload(&mut self) { | ||||
@@ -153,6 +163,12 @@ impl Site { | |||||
orphans | orphans | ||||
} | } | ||||
pub fn set_base_url(&mut self, base_url: String) { | |||||
let mut imageproc = self.imageproc.lock().unwrap(); | |||||
imageproc.set_base_url(&base_url); | |||||
self.config.base_url = base_url; | |||||
} | |||||
pub fn set_output_path<P: AsRef<Path>>(&mut self, path: P) { | pub fn set_output_path<P: AsRef<Path>>(&mut self, path: P) { | ||||
self.output_path = path.as_ref().to_path_buf(); | self.output_path = path.as_ref().to_path_buf(); | ||||
} | } | ||||
@@ -217,7 +233,7 @@ impl Site { | |||||
if !self.sections.contains_key(&index_path) { | if !self.sections.contains_key(&index_path) { | ||||
let mut index_section = Section::default(); | let mut index_section = Section::default(); | ||||
index_section.permalink = self.config.make_permalink(""); | index_section.permalink = self.config.make_permalink(""); | ||||
index_section.file.parent = self.base_path.join("content"); | |||||
index_section.file.parent = self.content_path.clone(); | |||||
index_section.file.relative = "_index.md".to_string(); | index_section.file.relative = "_index.md".to_string(); | ||||
self.sections.insert(index_path, index_section); | self.sections.insert(index_path, index_section); | ||||
} | } | ||||
@@ -229,10 +245,10 @@ impl Site { | |||||
self.add_page(p, false)?; | self.add_page(p, false)?; | ||||
} | } | ||||
self.register_early_global_fns(); | |||||
self.render_markdown()?; | self.render_markdown()?; | ||||
self.populate_sections(); | self.populate_sections(); | ||||
self.populate_tags_and_categories(); | self.populate_tags_and_categories(); | ||||
self.register_tera_global_fns(); | self.register_tera_global_fns(); | ||||
Ok(()) | Ok(()) | ||||
@@ -270,6 +286,16 @@ impl Site { | |||||
Ok(()) | Ok(()) | ||||
} | } | ||||
/// Adds global fns that are to be available to shortcodes while rendering markdown | |||||
pub fn register_early_global_fns(&mut self) { | |||||
self.tera.register_global_function( | |||||
"get_url", global_fns::make_get_url(self.permalinks.clone(), self.config.clone()) | |||||
); | |||||
self.tera.register_global_function( | |||||
"resize_image", global_fns::make_resize_image(self.imageproc.clone()) | |||||
); | |||||
} | |||||
pub fn register_tera_global_fns(&mut self) { | pub fn register_tera_global_fns(&mut self) { | ||||
self.tera.register_global_function("trans", global_fns::make_trans(self.config.clone())); | self.tera.register_global_function("trans", global_fns::make_trans(self.config.clone())); | ||||
self.tera.register_global_function("get_page", global_fns::make_get_page(&self.pages)); | self.tera.register_global_function("get_page", global_fns::make_get_page(&self.pages)); | ||||
@@ -278,10 +304,6 @@ impl Site { | |||||
"get_taxonomy_url", | "get_taxonomy_url", | ||||
global_fns::make_get_taxonomy_url(self.tags.clone(), self.categories.clone()) | global_fns::make_get_taxonomy_url(self.tags.clone(), self.categories.clone()) | ||||
); | ); | ||||
self.tera.register_global_function( | |||||
"get_url", | |||||
global_fns::make_get_url(self.permalinks.clone(), self.config.clone()) | |||||
); | |||||
} | } | ||||
/// Add a page to the site | /// Add a page to the site | ||||
@@ -441,6 +463,17 @@ impl Site { | |||||
Ok(()) | Ok(()) | ||||
} | } | ||||
pub fn num_img_ops(&self) -> usize { | |||||
let imageproc = self.imageproc.lock().unwrap(); | |||||
imageproc.num_img_ops() | |||||
} | |||||
pub fn process_images(&self) -> Result<()> { | |||||
let mut imageproc = self.imageproc.lock().unwrap(); | |||||
imageproc.prune()?; | |||||
imageproc.do_process() | |||||
} | |||||
/// Deletes the `public` directory if it exists | /// Deletes the `public` directory if it exists | ||||
pub fn clean(&self) -> Result<()> { | pub fn clean(&self) -> Result<()> { | ||||
if self.output_path.exists() { | if self.output_path.exists() { | ||||
@@ -510,6 +543,7 @@ impl Site { | |||||
self.compile_sass(&self.base_path)?; | self.compile_sass(&self.base_path)?; | ||||
} | } | ||||
self.process_images()?; | |||||
self.copy_static_directories()?; | self.copy_static_directories()?; | ||||
if self.config.build_search_index { | if self.config.build_search_index { | ||||
@@ -820,7 +854,7 @@ impl Site { | |||||
/// Used only on reload | /// Used only on reload | ||||
pub fn render_index(&self) -> Result<()> { | pub fn render_index(&self) -> Result<()> { | ||||
self.render_section( | self.render_section( | ||||
&self.sections[&self.base_path.join("content").join("_index.md")], | |||||
&self.sections[&self.content_path.join("_index.md")], | |||||
false | false | ||||
) | ) | ||||
} | } | ||||
@@ -14,3 +14,4 @@ utils = { path = "../utils" } | |||||
content = { path = "../content" } | content = { path = "../content" } | ||||
config = { path = "../config" } | config = { path = "../config" } | ||||
taxonomies = { path = "../taxonomies" } | taxonomies = { path = "../taxonomies" } | ||||
imageproc = { path = "../imageproc" } |
@@ -1,5 +1,6 @@ | |||||
use std::collections::HashMap; | use std::collections::HashMap; | ||||
use std::path::{PathBuf}; | |||||
use std::path::PathBuf; | |||||
use std::sync::{Arc, Mutex}; | |||||
use tera::{GlobalFn, Value, from_value, to_value, Result}; | use tera::{GlobalFn, Value, from_value, to_value, Result}; | ||||
@@ -7,29 +8,41 @@ use content::{Page, Section}; | |||||
use config::Config; | use config::Config; | ||||
use utils::site::resolve_internal_link; | use utils::site::resolve_internal_link; | ||||
use taxonomies::Taxonomy; | use taxonomies::Taxonomy; | ||||
use imageproc; | |||||
macro_rules! required_string_arg { | |||||
($e: expr, $err: expr) => { | |||||
macro_rules! required_arg { | |||||
($ty: ty, $e: expr, $err: expr) => { | |||||
match $e { | match $e { | ||||
Some(v) => match from_value::<String>(v.clone()) { | |||||
Some(v) => match from_value::<$ty>(v.clone()) { | |||||
Ok(u) => u, | Ok(u) => u, | ||||
Err(_) => return Err($err.into()) | Err(_) => return Err($err.into()) | ||||
}, | }, | ||||
None => return Err($err.into()) | None => return Err($err.into()) | ||||
}; | |||||
} | |||||
}; | |||||
} | |||||
macro_rules! optional_arg { | |||||
($ty: ty, $e: expr, $err: expr) => { | |||||
match $e { | |||||
Some(v) => match from_value::<$ty>(v.clone()) { | |||||
Ok(u) => Some(u), | |||||
Err(_) => return Err($err.into()) | |||||
}, | |||||
None => None | |||||
} | |||||
}; | }; | ||||
} | } | ||||
pub fn make_trans(config: Config) -> GlobalFn { | pub fn make_trans(config: Config) -> GlobalFn { | ||||
let translations_config = config.translations; | let translations_config = config.translations; | ||||
let default_lang = to_value(config.default_language).unwrap(); | |||||
let default_lang = config.default_language.clone(); | |||||
Box::new(move |args| -> Result<Value> { | Box::new(move |args| -> Result<Value> { | ||||
let key = required_string_arg!(args.get("key"), "`trans` requires a `key` argument."); | |||||
let lang_arg = args.get("lang").unwrap_or(&default_lang).clone(); | |||||
let lang = from_value::<String>(lang_arg).unwrap(); | |||||
let key = required_arg!(String, args.get("key"), "`trans` requires a `key` argument."); | |||||
let lang = optional_arg!(String, args.get("lang"), "`trans`: `lang` must be a string.").unwrap_or(default_lang.clone()); | |||||
let translations = &translations_config[lang.as_str()]; | let translations = &translations_config[lang.as_str()]; | ||||
Ok(to_value(&translations[key.as_str()]).unwrap()) | Ok(to_value(&translations[key.as_str()]).unwrap()) | ||||
}) | }) | ||||
@@ -43,7 +56,7 @@ pub fn make_get_page(all_pages: &HashMap<PathBuf, Page>) -> GlobalFn { | |||||
} | } | ||||
Box::new(move |args| -> Result<Value> { | Box::new(move |args| -> Result<Value> { | ||||
let path = required_string_arg!(args.get("path"), "`get_page` requires a `path` argument with a string value"); | |||||
let path = required_arg!(String, args.get("path"), "`get_page` requires a `path` argument with a string value"); | |||||
match pages.get(&path) { | match pages.get(&path) { | ||||
Some(p) => Ok(to_value(p).unwrap()), | Some(p) => Ok(to_value(p).unwrap()), | ||||
None => Err(format!("Page `{}` not found.", path).into()) | None => Err(format!("Page `{}` not found.", path).into()) | ||||
@@ -61,7 +74,7 @@ pub fn make_get_section(all_sections: &HashMap<PathBuf, Section>) -> GlobalFn { | |||||
} | } | ||||
Box::new(move |args| -> Result<Value> { | Box::new(move |args| -> Result<Value> { | ||||
let path = required_string_arg!(args.get("path"), "`get_section` requires a `path` argument with a string value"); | |||||
let path = required_arg!(String, args.get("path"), "`get_section` requires a `path` argument with a string value"); | |||||
//println!("Found {:#?}", sections.get(&path).unwrap().pages[0]); | //println!("Found {:#?}", sections.get(&path).unwrap().pages[0]); | ||||
match sections.get(&path) { | match sections.get(&path) { | ||||
Some(p) => Ok(to_value(p).unwrap()), | Some(p) => Ok(to_value(p).unwrap()), | ||||
@@ -84,7 +97,7 @@ pub fn make_get_url(permalinks: HashMap<String, String>, config: Config) -> Glob | |||||
from_value::<bool>(c.clone()).unwrap_or(true) | from_value::<bool>(c.clone()).unwrap_or(true) | ||||
}); | }); | ||||
let path = required_string_arg!(args.get("path"), "`get_url` requires a `path` argument with a string value"); | |||||
let path = required_arg!(String, args.get("path"), "`get_url` requires a `path` argument with a string value"); | |||||
if path.starts_with("./") { | if path.starts_with("./") { | ||||
match resolve_internal_link(&path, &permalinks) { | match resolve_internal_link(&path, &permalinks) { | ||||
Ok(url) => Ok(to_value(url).unwrap()), | Ok(url) => Ok(to_value(url).unwrap()), | ||||
@@ -107,8 +120,8 @@ pub fn make_get_url(permalinks: HashMap<String, String>, config: Config) -> Glob | |||||
pub fn make_get_taxonomy_url(tags: Option<Taxonomy>, categories: Option<Taxonomy>) -> GlobalFn { | pub fn make_get_taxonomy_url(tags: Option<Taxonomy>, categories: Option<Taxonomy>) -> GlobalFn { | ||||
Box::new(move |args| -> Result<Value> { | Box::new(move |args| -> Result<Value> { | ||||
let kind = required_string_arg!(args.get("kind"), "`get_taxonomy_url` requires a `kind` argument with a string value"); | |||||
let name = required_string_arg!(args.get("name"), "`get_taxonomy_url` requires a `name` argument with a string value"); | |||||
let kind = required_arg!(String, args.get("kind"), "`get_taxonomy_url` requires a `kind` argument with a string value"); | |||||
let name = required_arg!(String, args.get("name"), "`get_taxonomy_url` requires a `name` argument with a string value"); | |||||
let container = match kind.as_ref() { | let container = match kind.as_ref() { | ||||
"tag" => &tags, | "tag" => &tags, | ||||
"category" => &categories, | "category" => &categories, | ||||
@@ -128,6 +141,33 @@ pub fn make_get_taxonomy_url(tags: Option<Taxonomy>, categories: Option<Taxonomy | |||||
}) | }) | ||||
} | } | ||||
pub fn make_resize_image(imageproc: Arc<Mutex<imageproc::Processor>>) -> GlobalFn { | |||||
static DEFAULT_OP: &'static str = "fill"; | |||||
const DEFAULT_Q: u8 = 75; | |||||
Box::new(move |args| -> Result<Value> { | |||||
let path = required_arg!(String, args.get("path"), "`resize_image` requires a `path` argument with a string value"); | |||||
let width = optional_arg!(u32, args.get("width"), "`resize_image`: `width` must be a non-negative integer"); | |||||
let height = optional_arg!(u32, args.get("height"), "`resize_image`: `height` must be a non-negative integer"); | |||||
let op = optional_arg!(String, args.get("op"), "`resize_image`: `op` must be a string").unwrap_or(DEFAULT_OP.to_owned()); | |||||
let quality = optional_arg!(u8, args.get("quality"), "`resize_image`: `quality` must be a number").unwrap_or(DEFAULT_Q); | |||||
if quality == 0 || quality > 100 { | |||||
return Err("`resize_image`: `quality` must be in range 1-100".to_string().into()); | |||||
} | |||||
let mut imageproc = imageproc.lock().unwrap(); | |||||
if !imageproc.source_exists(&path) { | |||||
return Err(format!("`resize_image`: Cannot find path: {}", path).into()); | |||||
} | |||||
let imageop = imageproc::ImageOp::from_args(path.clone(), &op, width, height, quality).map_err(|e| format!("`resize_image`: {}", e))?; | |||||
let url = imageproc.insert(imageop); | |||||
to_value(url).map_err(|err| err.into()) | |||||
}) | |||||
} | |||||
#[cfg(test)] | #[cfg(test)] | ||||
mod tests { | mod tests { | ||||
use super::{make_get_url, make_get_taxonomy_url, make_trans}; | use super::{make_get_url, make_get_taxonomy_url, make_trans}; | ||||
@@ -11,6 +11,7 @@ extern crate utils; | |||||
extern crate content; | extern crate content; | ||||
extern crate config; | extern crate config; | ||||
extern crate taxonomies; | extern crate taxonomies; | ||||
extern crate imageproc; | |||||
pub mod filters; | pub mod filters; | ||||
pub mod global_fns; | pub mod global_fns; | ||||
@@ -93,6 +93,32 @@ pub fn copy_directory(src: &PathBuf, dest: &PathBuf) -> Result<()> { | |||||
Ok(()) | Ok(()) | ||||
} | } | ||||
/// Compares source and target files' timestamps and returns true if the source file | |||||
/// has been created _or_ updated after the target file has | |||||
pub fn file_stale<PS, PT>(p_source: PS, p_target: PT) -> bool where PS: AsRef<Path>, PT: AsRef<Path> { | |||||
let p_source = p_source.as_ref(); | |||||
let p_target = p_target.as_ref(); | |||||
if ! p_target.exists() { | |||||
return true; | |||||
} | |||||
let get_time = |path: &Path| path.metadata().ok().and_then(|meta| { | |||||
Some(match (meta.created().ok(), meta.modified().ok()) { | |||||
(Some(tc), Some(tm)) => tc.max(tm), | |||||
(Some(tc), None) => tc, | |||||
(None, Some(tm)) => tm, | |||||
(None, None) => return None, | |||||
}) | |||||
}); | |||||
let time_source = get_time(p_source); | |||||
let time_target = get_time(p_target); | |||||
time_source.and_then(|ts| time_target.map(|tt| ts > tt)).unwrap_or(true) | |||||
} | |||||
#[cfg(test)] | #[cfg(test)] | ||||
mod tests { | mod tests { | ||||
use std::fs::File; | use std::fs::File; | ||||
@@ -0,0 +1,8 @@ | |||||
+++ | |||||
title = "Image Resizing" | |||||
weight = 120 | |||||
+++ | |||||
TODO: talk about resize_image | |||||
{{ gallery() }} |
@@ -37,6 +37,7 @@ previous: Page?; | |||||
next: Page?; | next: Page?; | ||||
// See the Table of contents section below for more details | // See the Table of contents section below for more details | ||||
toc: Array<Header>; | toc: Array<Header>; | ||||
// TODO: add assets & assets_imgs (also draft is missing?) | |||||
``` | ``` | ||||
## Section variables | ## Section variables | ||||
@@ -0,0 +1,6 @@ | |||||
{% for img in page.assets_imgs %} | |||||
<a href="{{ config.base_url }}/{{ img }}"> | |||||
<img src="{{ resize_image(path=img, width=240, height=180, op="fill") }}" /> | |||||
</a> | |||||
  | |||||
{% endfor %} |
@@ -0,0 +1 @@ | |||||
<img src="{{ resize_image(path=path, width=width, height=height, op=op) }}" /> |
@@ -86,13 +86,13 @@ fn create_new_site(interface: &str, port: &str, output_dir: &str, base_url: &str | |||||
let base_address = format!("{}:{}", base_url, port); | let base_address = format!("{}:{}", base_url, port); | ||||
let address = format!("{}:{}", interface, port); | let address = format!("{}:{}", interface, port); | ||||
site.config.base_url = if site.config.base_url.ends_with('/') { | |||||
let base_url = if site.config.base_url.ends_with('/') { | |||||
format!("http://{}/", base_address) | format!("http://{}/", base_address) | ||||
} else { | } else { | ||||
format!("http://{}", base_address) | format!("http://{}", base_address) | ||||
}; | }; | ||||
site.set_base_url(base_url); | |||||
site.set_output_path(output_dir); | site.set_output_path(output_dir); | ||||
site.load()?; | site.load()?; | ||||
site.enable_live_reload(); | site.enable_live_reload(); | ||||
@@ -27,10 +27,11 @@ pub fn error(message: &str) { | |||||
/// Display in the console the number of pages/sections in the site | /// Display in the console the number of pages/sections in the site | ||||
pub fn notify_site_size(site: &Site) { | pub fn notify_site_size(site: &Site) { | ||||
println!( | println!( | ||||
"-> Creating {} pages ({} orphan) and {} sections", | |||||
"-> Creating {} pages ({} orphan), {} sections, and processing {} images", | |||||
site.pages.len(), | site.pages.len(), | ||||
site.get_all_orphan_pages().len(), | site.get_all_orphan_pages().len(), | ||||
site.sections.len() - 1, // -1 since we do not the index as a section | site.sections.len() - 1, // -1 since we do not the index as a section | ||||
site.num_img_ops(), | |||||
); | ); | ||||
} | } | ||||