move client && cache to global and update crates

pull/337/head
GitHub 11 months ago
parent 37416f827b
commit 83d8ee757d

464
Cargo.lock generated

@ -71,15 +71,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "autocfg"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78"
dependencies = [
"autocfg 1.1.0",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@ -203,15 +194,6 @@ dependencies = [
"os_str_bytes",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
dependencies = [
"bitflags",
]
[[package]]
name = "codespan-reporting"
version = "0.11.1"
@ -268,29 +250,25 @@ dependencies = [
[[package]]
name = "cssparser"
version = "0.29.6"
version = "0.31.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93d03419cb5950ccfd3daf3ff1c7a36ace64609a1a8746d493df1ca0afde0fa"
checksum = "5b3df4f93e5fbbe73ec01ec8d3f68bba73107993a5b1e7519273c32db9b0d5be"
dependencies = [
"cssparser-macros",
"dtoa-short",
"itoa",
"matches",
"phf 0.10.1",
"proc-macro2",
"quote",
"phf",
"smallvec",
"syn",
]
[[package]]
name = "cssparser-macros"
version = "0.6.0"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfae75de57f2b2e85e8768c3ea840fd159c8f33e2b6522c7835b7abac81be16e"
checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
dependencies = [
"quote",
"syn",
"syn 2.0.18",
]
[[package]]
@ -317,7 +295,7 @@ dependencies = [
"proc-macro2",
"quote",
"scratch",
"syn",
"syn 1.0.103",
]
[[package]]
@ -334,7 +312,7 @@ checksum = "b846f081361125bfc8dc9d3940c84e1fd83ba54bbca7b17cd29483c828be0704"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.103",
]
[[package]]
@ -382,9 +360,9 @@ checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]]
name = "encoding_rs"
version = "0.8.31"
version = "0.8.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b"
checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394"
dependencies = [
"cfg-if",
]
@ -431,19 +409,13 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "form_urlencoded"
version = "1.1.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
dependencies = [
"percent-encoding",
]
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]]
name = "futf"
version = "0.1.5"
@ -559,16 +531,16 @@ dependencies = [
[[package]]
name = "html5ever"
version = "0.24.1"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "025483b0a1e4577bb28578318c886ee5f817dda6eb62473269349044406644cb"
checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7"
dependencies = [
"log",
"mac",
"markup5ever",
"proc-macro2",
"quote",
"syn",
"syn 1.0.103",
]
[[package]]
@ -668,9 +640,9 @@ dependencies = [
[[package]]
name = "idna"
version = "0.3.0"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
dependencies = [
"unicode-bidi",
"unicode-normalization",
@ -682,7 +654,7 @@ version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"hashbrown",
]
@ -746,6 +718,16 @@ dependencies = [
"cc",
]
[[package]]
name = "lock_api"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.17"
@ -763,26 +745,29 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
[[package]]
name = "markup5ever"
version = "0.9.0"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65381d9d47506b8592b97c4efd936afcf673b09b059f2bef39c7211ee78b9d03"
checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016"
dependencies = [
"log",
"phf 0.7.24",
"phf",
"phf_codegen",
"serde",
"serde_derive",
"serde_json",
"string_cache",
"string_cache_codegen",
"tendril",
]
[[package]]
name = "matches"
version = "0.1.9"
name = "markup5ever_rcdom"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
checksum = "b9521dd6750f8e80ee6c53d65e2e4656d7de37064f3a7a5d2d11d05df93839c2"
dependencies = [
"html5ever",
"markup5ever",
"tendril",
"xml5ever",
]
[[package]]
name = "memchr"
@ -829,6 +814,8 @@ dependencies = [
"cssparser",
"encoding_rs",
"html5ever",
"markup5ever_rcdom",
"once_cell",
"percent-encoding",
"regex",
"reqwest",
@ -866,7 +853,7 @@ version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"num-traits",
]
@ -876,7 +863,7 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg 1.1.0",
"autocfg",
]
[[package]]
@ -918,7 +905,7 @@ checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.103",
]
[[package]]
@ -933,7 +920,7 @@ version = "0.9.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b03b84c3b2d099b81f0953422b4d4ad58761589d0229b5506356afca05a3670a"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"cc",
"libc",
"pkg-config",
@ -947,20 +934,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9"
[[package]]
name = "percent-encoding"
version = "2.2.0"
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "phf"
version = "0.7.24"
name = "parking_lot_core"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18"
checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
dependencies = [
"phf_shared 0.7.24",
"cfg-if",
"libc",
"redox_syscall 0.3.5",
"smallvec",
"windows-targets",
]
[[package]]
name = "percent-encoding"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
[[package]]
name = "phf"
version = "0.10.1"
@ -968,28 +969,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
dependencies = [
"phf_macros",
"phf_shared 0.10.0",
"phf_shared",
"proc-macro-hack",
]
[[package]]
name = "phf_codegen"
version = "0.7.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e"
dependencies = [
"phf_generator 0.7.24",
"phf_shared 0.7.24",
]
[[package]]
name = "phf_generator"
version = "0.7.24"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662"
checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
dependencies = [
"phf_shared 0.7.24",
"rand 0.6.5",
"phf_generator",
"phf_shared",
]
[[package]]
@ -998,8 +989,8 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
dependencies = [
"phf_shared 0.10.0",
"rand 0.8.5",
"phf_shared",
"rand",
]
[[package]]
@ -1008,21 +999,12 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0"
dependencies = [
"phf_generator 0.10.0",
"phf_shared 0.10.0",
"phf_generator",
"phf_shared",
"proc-macro-hack",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "phf_shared"
version = "0.7.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0"
dependencies = [
"siphasher 0.2.3",
"syn 1.0.103",
]
[[package]]
@ -1031,7 +1013,7 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
dependencies = [
"siphasher 0.3.10",
"siphasher",
]
[[package]]
@ -1099,41 +1081,22 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro2"
version = "1.0.47"
version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
dependencies = [
"autocfg 0.1.8",
"libc",
"rand_chacha 0.1.1",
"rand_core 0.4.2",
"rand_hc",
"rand_isaac",
"rand_jitter",
"rand_os",
"rand_pcg",
"rand_xorshift",
"winapi",
]
[[package]]
name = "rand"
version = "0.8.5"
@ -1141,18 +1104,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha 0.3.1",
"rand_core 0.6.4",
]
[[package]]
name = "rand_chacha"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
dependencies = [
"autocfg 0.1.8",
"rand_core 0.3.1",
"rand_chacha",
"rand_core",
]
[[package]]
@ -1162,24 +1115,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core 0.6.4",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
dependencies = [
"rand_core 0.4.2",
]
[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rand_core"
version = "0.6.4"
@ -1190,90 +1128,28 @@ dependencies = [
]
[[package]]
name = "rand_hc"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "rand_isaac"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "rand_jitter"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
dependencies = [
"libc",
"rand_core 0.4.2",
"winapi",
]
[[package]]
name = "rand_os"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
dependencies = [
"cloudabi",
"fuchsia-cprng",
"libc",
"rand_core 0.4.2",
"rdrand",
"winapi",
]
[[package]]
name = "rand_pcg"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
dependencies = [
"autocfg 0.1.8",
"rand_core 0.4.2",
]
[[package]]
name = "rand_xorshift"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "rdrand"
version = "0.4.0"
name = "redox_syscall"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"rand_core 0.3.1",
"bitflags",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
"bitflags",
]
[[package]]
name = "regex"
version = "1.7.0"
version = "1.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f"
dependencies = [
"regex-syntax",
]
@ -1286,9 +1162,9 @@ checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
[[package]]
name = "regex-syntax"
version = "0.6.28"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
[[package]]
name = "remove_dir_all"
@ -1354,6 +1230,12 @@ dependencies = [
"windows-sys 0.36.1",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "scratch"
version = "1.0.2"
@ -1389,17 +1271,6 @@ version = "1.0.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
[[package]]
name = "serde_derive"
version = "1.0.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.87"
@ -1434,12 +1305,6 @@ dependencies = [
"digest",
]
[[package]]
name = "siphasher"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
[[package]]
name = "siphasher"
version = "0.3.10"
@ -1452,7 +1317,7 @@ version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
dependencies = [
"autocfg 1.1.0",
"autocfg",
]
[[package]]
@ -1473,38 +1338,30 @@ dependencies = [
[[package]]
name = "string_cache"
version = "0.7.5"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89c058a82f9fd69b1becf8c274f412281038877c553182f1d02eb027045a2d67"
checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b"
dependencies = [
"lazy_static",
"new_debug_unreachable",
"phf_shared 0.7.24",
"once_cell",
"parking_lot",
"phf_shared",
"precomputed-hash",
"serde",
"string_cache_codegen",
"string_cache_shared",
]
[[package]]
name = "string_cache_codegen"
version = "0.4.4"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0f45ed1b65bf9a4bf2f7b7dc59212d1926e9eaf00fa998988e420fd124467c6"
checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988"
dependencies = [
"phf_generator 0.7.24",
"phf_shared 0.7.24",
"phf_generator",
"phf_shared",
"proc-macro2",
"quote",
"string_cache_shared",
]
[[package]]
name = "string_cache_shared"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc"
[[package]]
name = "strsim"
version = "0.10.0"
@ -1522,6 +1379,17 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tempfile"
version = "3.3.0"
@ -1531,7 +1399,7 @@ dependencies = [
"cfg-if",
"fastrand",
"libc",
"redox_syscall",
"redox_syscall 0.2.16",
"remove_dir_all",
"winapi",
]
@ -1600,7 +1468,7 @@ version = "1.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099"
dependencies = [
"autocfg 1.1.0",
"autocfg",
"bytes",
"libc",
"memchr",
@ -1675,9 +1543,9 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "unicode-bidi"
version = "0.3.8"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
[[package]]
name = "unicode-ident"
@ -1702,9 +1570,9 @@ checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "url"
version = "2.3.1"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb"
dependencies = [
"form_urlencoded",
"idna",
@ -1781,7 +1649,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn",
"syn 1.0.103",
"wasm-bindgen-shared",
]
@ -1815,7 +1683,7 @@ checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.103",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -1886,21 +1754,42 @@ version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_gnullvm 0.42.0",
"windows_aarch64_msvc 0.42.0",
"windows_i686_gnu 0.42.0",
"windows_i686_msvc 0.42.0",
"windows_x86_64_gnu 0.42.0",
"windows_x86_64_gnullvm",
"windows_x86_64_gnullvm 0.42.0",
"windows_x86_64_msvc 0.42.0",
]
[[package]]
name = "windows-targets"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
dependencies = [
"windows_aarch64_gnullvm 0.48.0",
"windows_aarch64_msvc 0.48.0",
"windows_i686_gnu 0.48.0",
"windows_i686_msvc 0.48.0",
"windows_x86_64_gnu 0.48.0",
"windows_x86_64_gnullvm 0.48.0",
"windows_x86_64_msvc 0.48.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.36.1"
@ -1913,6 +1802,12 @@ version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
version = "0.36.1"
@ -1925,6 +1820,12 @@ version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
version = "0.36.1"
@ -1937,6 +1838,12 @@ version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_x86_64_gnu"
version = "0.36.1"
@ -1949,12 +1856,24 @@ version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_msvc"
version = "0.36.1"
@ -1967,6 +1886,12 @@ version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "winreg"
version = "0.10.1"
@ -1975,3 +1900,14 @@ checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
dependencies = [
"winapi",
]
[[package]]
name = "xml5ever"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4034e1d05af98b51ad7214527730626f019682d797ba38b51689212118d8e650"
dependencies = [
"log",
"mac",
"markup5ever",
]

@ -8,7 +8,7 @@ authors = [
"Emi Simpson <emi@alchemi.dev>",
"rhysd <lin90162@yahoo.co.jp>",
]
edition = "2018"
edition = "2021"
description = "CLI tool for saving web pages as a single HTML file"
homepage = "https://github.com/Y2Z/monolith"
repository = "https://github.com/Y2Z/monolith"
@ -26,16 +26,18 @@ atty = "0.2.14" # Used for highlighting network errors
base64 = "0.13.0" # Used for integrity attributes
chrono = "0.4.20" # Used for formatting creation timestamp
clap = "3.2.16"
cssparser = "0.29.6"
encoding_rs = "0.8.31"
html5ever = "0.24.1"
percent-encoding = "2.1.0"
sha2 = "0.10.2" # Used for calculating checksums during integrity checks
url = "2.2.2"
cssparser = "0.31.2"
encoding_rs = "0.8.32"
html5ever = "0.26.0"
markup5ever_rcdom = "0.2.0"
once_cell = "*"
percent-encoding = "2.3.0"
sha2 = "0.10.6" # Used for calculating checksums during integrity checks
url = "2.4.0"
# Used for parsing srcset and NOSCRIPT
[dependencies.regex]
version = "1.6.0"
version = "1.8.4"
default-features = false
features = ["std", "perf-dfa", "unicode-perl"]

@ -1,8 +1,6 @@
use cssparser::{
serialize_identifier, serialize_string, ParseError, Parser, ParserInput, SourcePosition, Token,
};
use reqwest::blocking::Client;
use std::collections::HashMap;
use url::Url;
use crate::opts::Options;
@ -30,29 +28,11 @@ const CSS_PROPS_WITH_IMAGE_URLS: &[&str] = &[
"symbols",
];
pub fn embed_css(
cache: &mut HashMap<String, Vec<u8>>,
client: &Client,
document_url: &Url,
css: &str,
options: &Options,
depth: u32,
) -> String {
let mut input = ParserInput::new(&css);
pub fn embed_css(document_url: &Url, css: &str, options: &Options, depth: u32) -> String {
let mut input = ParserInput::new(css);
let mut parser = Parser::new(&mut input);
process_css(
cache,
client,
document_url,
&mut parser,
options,
depth,
"",
"",
"",
)
.unwrap()
process_css(document_url, &mut parser, options, depth, "", "", "").unwrap()
}
pub fn format_ident(ident: &str) -> String {
@ -71,13 +51,10 @@ pub fn format_quoted_string(string: &str) -> String {
pub fn is_image_url_prop(prop_name: &str) -> bool {
CSS_PROPS_WITH_IMAGE_URLS
.iter()
.find(|p| prop_name.eq_ignore_ascii_case(p))
.is_some()
.any(|p| prop_name.eq_ignore_ascii_case(p))
}
pub fn process_css<'a>(
cache: &mut HashMap<String, Vec<u8>>,
client: &Client,
document_url: &Url,
parser: &mut Parser,
options: &Options,
@ -88,8 +65,8 @@ pub fn process_css<'a>(
) -> Result<String, ParseError<'a, String>> {
let mut result: String = "".to_string();
let mut curr_rule: String = rule_name.clone().to_string();
let mut curr_prop: String = prop_name.clone().to_string();
let mut curr_rule: String = rule_name.to_string();
let mut curr_prop: String = prop_name.to_string();
let mut token: &Token;
let mut token_offset: SourcePosition;
@ -107,9 +84,9 @@ pub fn process_css<'a>(
let token_slice = parser.slice_from(token_offset);
result.push_str(token_slice);
}
Token::Semicolon => result.push_str(";"),
Token::Colon => result.push_str(":"),
Token::Comma => result.push_str(","),
Token::Semicolon => result.push(';'),
Token::Colon => result.push(':'),
Token::Comma => result.push(','),
Token::ParenthesisBlock | Token::SquareBracketBlock | Token::CurlyBracketBlock => {
if options.no_fonts && curr_rule == "font-face" {
continue;
@ -117,21 +94,19 @@ pub fn process_css<'a>(
let closure: &str;
if token == &Token::ParenthesisBlock {
result.push_str("(");
result.push('(');
closure = ")";
} else if token == &Token::SquareBracketBlock {
result.push_str("[");
result.push('[');
closure = "]";
} else {
result.push_str("{");
result.push('{');
closure = "}";
}
let block_css: String = parser
.parse_nested_block(|parser| {
process_css(
cache,
client,
document_url,
parser,
options,
@ -146,9 +121,9 @@ pub fn process_css<'a>(
result.push_str(closure);
}
Token::CloseParenthesis => result.push_str(")"),
Token::CloseSquareBracket => result.push_str("]"),
Token::CloseCurlyBracket => result.push_str("}"),
Token::CloseParenthesis => result.push(')'),
Token::CloseSquareBracket => result.push(']'),
Token::CloseCurlyBracket => result.push('}'),
Token::IncludeMatch => result.push_str("~="),
Token::DashMatch => result.push_str("|="),
Token::PrefixMatch => result.push_str("^="),
@ -156,7 +131,7 @@ pub fn process_css<'a>(
Token::SubstringMatch => result.push_str("*="),
Token::CDO => result.push_str("<!--"),
Token::CDC => result.push_str("-->"),
Token::WhiteSpace(ref value) => {
Token::WhiteSpace(value) => {
result.push_str(value);
}
// div...
@ -171,11 +146,11 @@ pub fn process_css<'a>(
if options.no_fonts && curr_rule == "font-face" {
continue;
}
result.push_str("@");
result.push('@');
result.push_str(value);
}
Token::Hash(ref value) => {
result.push_str("#");
result.push('#');
result.push_str(value);
}
Token::QuotedString(ref value) => {
@ -189,15 +164,8 @@ pub fn process_css<'a>(
continue;
}
let import_full_url: Url = resolve_url(&document_url, value);
match retrieve_asset(
cache,
client,
&document_url,
&import_full_url,
options,
depth + 1,
) {
let import_full_url: Url = resolve_url(document_url, value);
match retrieve_asset(document_url, &import_full_url, options, depth + 1) {
Ok((
import_contents,
import_final_url,
@ -208,8 +176,6 @@ pub fn process_css<'a>(
&import_media_type,
&import_charset,
embed_css(
cache,
client,
&import_final_url,
&String::from_utf8_lossy(&import_contents),
options,
@ -219,9 +185,8 @@ pub fn process_css<'a>(
&import_final_url,
);
import_data_url.set_fragment(import_full_url.fragment());
result.push_str(
format_quoted_string(&import_data_url.to_string()).as_str(),
);
result
.push_str(format_quoted_string(import_data_url.as_ref()).as_str());
}
Err(_) => {
// Keep remote reference if unable to retrieve the asset
@ -229,54 +194,42 @@ pub fn process_css<'a>(
|| import_full_url.scheme() == "https"
{
result.push_str(
format_quoted_string(&import_full_url.to_string()).as_str(),
format_quoted_string(import_full_url.as_ref()).as_str(),
);
}
}
}
} else {
if func_name == "url" {
// Skip empty url()'s
if value.len() == 0 {
continue;
}
} else if func_name == "url" {
// Skip empty url()'s
if value.len() == 0 {
continue;
}
if options.no_images && is_image_url_prop(curr_prop.as_str()) {
result.push_str(format_quoted_string(EMPTY_IMAGE_DATA_URL).as_str());
} else {
let resolved_url: Url = resolve_url(&document_url, value);
match retrieve_asset(
cache,
client,
&document_url,
&resolved_url,
options,
depth + 1,
) {
Ok((data, final_url, media_type, charset)) => {
let mut data_url =
create_data_url(&media_type, &charset, &data, &final_url);
data_url.set_fragment(resolved_url.fragment());
if options.no_images && is_image_url_prop(curr_prop.as_str()) {
result.push_str(format_quoted_string(EMPTY_IMAGE_DATA_URL).as_str());
} else {
let resolved_url: Url = resolve_url(document_url, value);
match retrieve_asset(document_url, &resolved_url, options, depth + 1) {
Ok((data, final_url, media_type, charset)) => {
let mut data_url =
create_data_url(&media_type, &charset, &data, &final_url);
data_url.set_fragment(resolved_url.fragment());
result.push_str(format_quoted_string(data_url.as_ref()).as_str());
}
Err(_) => {
// Keep remote reference if unable to retrieve the asset
if resolved_url.scheme() == "http"
|| resolved_url.scheme() == "https"
{
result.push_str(
format_quoted_string(&data_url.to_string()).as_str(),
format_quoted_string(resolved_url.as_ref()).as_str(),
);
}
Err(_) => {
// Keep remote reference if unable to retrieve the asset
if resolved_url.scheme() == "http"
|| resolved_url.scheme() == "https"
{
result.push_str(
format_quoted_string(&resolved_url.to_string())
.as_str(),
);
}
}
}
}
} else {
result.push_str(format_quoted_string(value).as_str());
}
} else {
result.push_str(format_quoted_string(value).as_str());
}
}
Token::Number {
@ -285,7 +238,7 @@ pub fn process_css<'a>(
..
} => {
if *has_sign && *value >= 0. {
result.push_str("+");
result.push('+');
}
result.push_str(&value.to_string())
}
@ -295,10 +248,10 @@ pub fn process_css<'a>(
..
} => {
if *has_sign && *unit_value >= 0. {
result.push_str("+");
result.push('+');
}
result.push_str(&(unit_value * 100.0).to_string());
result.push_str("%");
result.push('%');
}
Token::Dimension {
ref has_sign,
@ -307,15 +260,15 @@ pub fn process_css<'a>(
..
} => {
if *has_sign && *value >= 0. {
result.push_str("+");
result.push('+');
}
result.push_str(&value.to_string());
result.push_str(&unit.to_string());
result.push_str(unit);
}
// #selector, #id...
Token::IDHash(ref value) => {
curr_rule = "".to_string();
result.push_str("#");
result.push('#');
result.push_str(&format_ident(value));
}
// url()
@ -331,31 +284,22 @@ pub fn process_css<'a>(
if value.len() < 1 {
result.push_str("url()");
continue;
} else if value.starts_with("#") {
} else if value.starts_with('#') {
result.push_str("url(");
result.push_str(value);
result.push_str(")");
result.push(')');
continue;
}
result.push_str("url(");
if is_import {
let full_url: Url = resolve_url(&document_url, value);
match retrieve_asset(
cache,
client,
&document_url,
&full_url,
options,
depth + 1,
) {
let full_url: Url = resolve_url(document_url, value);
match retrieve_asset(document_url, &full_url, options, depth + 1) {
Ok((css, final_url, media_type, charset)) => {
let mut data_url = create_data_url(
&media_type,
&charset,
embed_css(
cache,
client,
&final_url,
&String::from_utf8_lossy(&css),
options,
@ -365,61 +309,46 @@ pub fn process_css<'a>(
&final_url,
);
data_url.set_fragment(full_url.fragment());
result.push_str(format_quoted_string(&data_url.to_string()).as_str());
result.push_str(format_quoted_string(data_url.as_ref()).as_str());
}
Err(_) => {
// Keep remote reference if unable to retrieve the asset
if full_url.scheme() == "http" || full_url.scheme() == "https" {
result
.push_str(format_quoted_string(&full_url.to_string()).as_str());
result.push_str(format_quoted_string(full_url.as_ref()).as_str());
}
}
}
} else if is_image_url_prop(curr_prop.as_str()) && options.no_images {
result.push_str(format_quoted_string(EMPTY_IMAGE_DATA_URL).as_str());
} else {
if is_image_url_prop(curr_prop.as_str()) && options.no_images {
result.push_str(format_quoted_string(EMPTY_IMAGE_DATA_URL).as_str());
} else {
let full_url: Url = resolve_url(&document_url, value);
match retrieve_asset(
cache,
client,
&document_url,
&full_url,
options,
depth + 1,
) {
Ok((data, final_url, media_type, charset)) => {
let mut data_url =
create_data_url(&media_type, &charset, &data, &final_url);
data_url.set_fragment(full_url.fragment());
result
.push_str(format_quoted_string(&data_url.to_string()).as_str());
}
Err(_) => {
// Keep remote reference if unable to retrieve the asset
if full_url.scheme() == "http" || full_url.scheme() == "https" {
result.push_str(
format_quoted_string(&full_url.to_string()).as_str(),
);
}
let full_url: Url = resolve_url(document_url, value);
match retrieve_asset(document_url, &full_url, options, depth + 1) {
Ok((data, final_url, media_type, charset)) => {
let mut data_url =
create_data_url(&media_type, &charset, &data, &final_url);
data_url.set_fragment(full_url.fragment());
result.push_str(format_quoted_string(data_url.as_ref()).as_str());
}
Err(_) => {
// Keep remote reference if unable to retrieve the asset
if full_url.scheme() == "http" || full_url.scheme() == "https" {
result.push_str(format_quoted_string(full_url.as_ref()).as_str());
}
}
}
}
result.push_str(")");
result.push(')');
}
// =
Token::Delim(ref value) => result.push_str(&value.to_string()),
Token::Function(ref name) => {
let function_name: &str = &name.clone();
result.push_str(function_name);
result.push_str("(");
result.push('(');
let block_css: String = parser
.parse_nested_block(|parser| {
process_css(
cache,
client,
document_url,
parser,
options,
@ -432,14 +361,14 @@ pub fn process_css<'a>(
.unwrap();
result.push_str(block_css.as_str());
result.push_str(")");
result.push(')');
}
Token::BadUrl(_) | Token::BadString(_) => {}
}
}
// Ensure empty CSS is really empty
if result.len() > 0 && result.trim().len() == 0 {
if !result.is_empty() && result.trim().is_empty() {
result = result.trim().to_string()
}

@ -3,16 +3,15 @@ use chrono::prelude::*;
use encoding_rs::Encoding;
use html5ever::interface::QualName;
use html5ever::parse_document;
use html5ever::rcdom::{Handle, NodeData, RcDom};
use html5ever::serialize::{serialize, SerializeOpts};
use html5ever::tendril::{format_tendril, TendrilSink};
use html5ever::tree_builder::{Attribute, TreeSink};
use html5ever::{local_name, namespace_url, ns, LocalName};
use markup5ever_rcdom::{Handle, NodeData, RcDom, SerializableHandle};
use regex::Regex;
use reqwest::blocking::Client;
use reqwest::Url;
use sha2::{Digest, Sha256, Sha384, Sha512};
use std::collections::HashMap;
use std::default::Default;
use crate::css::embed_css;
@ -28,12 +27,16 @@ struct SrcSetItem<'a> {
descriptor: &'a str,
}
const ICON_VALUES: &'static [&str] = &["icon", "shortcut icon"];
const ICON_VALUES: &[&str] = &["icon", "shortcut icon"];
pub fn add_favicon(document: &Handle, favicon_data_url: String) -> RcDom {
let mut buf: Vec<u8> = Vec::new();
serialize(&mut buf, document, SerializeOpts::default())
.expect("unable to serialize DOM into buffer");
serialize(
&mut buf,
&SerializableHandle::from(document.clone()),
SerializeOpts::default(),
)
.expect("unable to serialize DOM into buffer");
let mut dom = html_to_dom(&buf, "utf-8".to_string());
let doc = dom.get_document();
@ -54,7 +57,7 @@ pub fn add_favicon(document: &Handle, favicon_data_url: String) -> RcDom {
Default::default(),
);
// Insert favicon LINK tag into HEAD
head.children.borrow_mut().push(favicon_node.clone());
head.children.borrow_mut().push(favicon_node);
}
}
@ -62,18 +65,18 @@ pub fn add_favicon(document: &Handle, favicon_data_url: String) -> RcDom {
}
pub fn check_integrity(data: &[u8], integrity: &str) -> bool {
if integrity.starts_with("sha256-") {
if let Some(stripped) = integrity.strip_prefix("sha256-") {
let mut hasher = Sha256::new();
hasher.update(data);
base64::encode(hasher.finalize()) == integrity[7..]
} else if integrity.starts_with("sha384-") {
base64::encode(hasher.finalize()) == stripped
} else if let Some(stripped) = integrity.strip_prefix("sha384-") {
let mut hasher = Sha384::new();
hasher.update(data);
base64::encode(hasher.finalize()) == integrity[7..]
} else if integrity.starts_with("sha512-") {
base64::encode(hasher.finalize()) == stripped
} else if let Some(stripped) = integrity.strip_prefix("sha512-") {
let mut hasher = Sha512::new();
hasher.update(data);
base64::encode(hasher.finalize()) == integrity[7..]
base64::encode(hasher.finalize()) == stripped
} else {
false
}
@ -125,7 +128,7 @@ pub fn create_metadata_tag(url: &Url) -> String {
format!(
"<!-- Saved from {} at {} using {} v{} -->",
if clean_url.scheme() == "http" || clean_url.scheme() == "https" {
&clean_url.as_str()
clean_url.as_str()
} else {
"local source"
},
@ -155,19 +158,12 @@ pub fn determine_link_node_type(node: &Handle) -> &str {
link_type
}
pub fn embed_srcset(
cache: &mut HashMap<String, Vec<u8>>,
client: &Client,
document_url: &Url,
srcset: &str,
options: &Options,
depth: u32,
) -> String {
pub fn embed_srcset(document_url: &Url, srcset: &str, options: &Options, depth: u32) -> String {
let mut array: Vec<SrcSetItem> = vec![];
let re = Regex::new(r",\s+").unwrap();
for srcset_item in re.split(srcset) {
let parts: Vec<&str> = srcset_item.trim().split_whitespace().collect();
if parts.len() > 0 {
let parts: Vec<&str> = srcset_item.split_whitespace().collect();
if !parts.is_empty() {
let path = parts[0].trim();
let descriptor = if parts.len() > 1 { parts[1].trim() } else { "" };
let srcset_real_item = SrcSetItem { path, descriptor };
@ -181,15 +177,8 @@ pub fn embed_srcset(
if options.no_images {
result.push_str(EMPTY_IMAGE_DATA_URL);
} else {
let image_full_url: Url = resolve_url(&document_url, part.path);
match retrieve_asset(
cache,
client,
&document_url,
&image_full_url,
options,
depth + 1,
) {
let image_full_url: Url = resolve_url(document_url, part.path);
match retrieve_asset(document_url, &image_full_url, options, depth + 1) {
Ok((image_data, image_final_url, image_media_type, image_charset)) => {
let mut image_data_url = create_data_url(
&image_media_type,
@ -214,7 +203,7 @@ pub fn embed_srcset(
}
if !part.descriptor.is_empty() {
result.push_str(" ");
result.push(' ');
result.push_str(part.descriptor);
}
@ -239,11 +228,8 @@ pub fn find_base_node(node: &Handle) -> Option<Handle> {
}
}
NodeData::Element { ref name, .. } => {
match name.local.as_ref() {
"head" => {
return get_child_node_by_name(node, "base");
}
_ => {}
if name.local.as_ref() == "head" {
return get_child_node_by_name(node, "base");
}
// Dig deeper
@ -273,7 +259,7 @@ pub fn find_meta_charset_or_content_type_node(node: &Handle) -> Option<Handle> {
match name.local.as_ref() {
"head" => {
if let Some(meta_node) = get_child_node_by_name(node, "meta") {
if let Some(_) = get_node_attr(&meta_node, "charset") {
if get_node_attr(&meta_node, "charset").is_some() {
return Some(meta_node);
} else if let Some(meta_node_http_equiv_attr_value) =
get_node_attr(&meta_node, "http-equiv")
@ -324,7 +310,7 @@ pub fn get_charset(node: &Handle) -> Option<String> {
}
}
return None;
None
}
pub fn get_child_node_by_name(parent: &Handle, node_name: &str) -> Option<Handle> {
@ -333,10 +319,7 @@ pub fn get_child_node_by_name(parent: &Handle, node_name: &str) -> Option<Handle
NodeData::Element { ref name, .. } => &*name.local == node_name,
_ => false,
});
match matching_children {
Some(node) => Some(node.clone()),
_ => None,
}
matching_children.cloned()
}
pub fn get_node_attr(node: &Handle, attr_name: &str) -> Option<String> {
@ -361,7 +344,7 @@ pub fn get_node_name(node: &Handle) -> Option<&'_ str> {
}
pub fn get_parent_node(child: &Handle) -> Handle {
let parent = child.parent.take().clone();
let parent = child.parent.take();
parent.and_then(|node| node.upgrade()).unwrap()
}
@ -406,14 +389,14 @@ pub fn has_favicon(handle: &Handle) -> bool {
found_favicon
}
pub fn html_to_dom(data: &Vec<u8>, document_encoding: String) -> RcDom {
pub fn html_to_dom(data: &[u8], document_encoding: String) -> RcDom {
let s: String;
if let Some(encoding) = Encoding::for_label(document_encoding.as_bytes()) {
let (string, _, _) = encoding.decode(&data);
let (string, _, _) = encoding.decode(data);
s = string.to_string();
} else {
s = String::from_utf8_lossy(&data).to_string();
s = String::from_utf8_lossy(data).to_string();
}
parse_document(RcDom::default(), Default::default())
@ -428,8 +411,12 @@ pub fn is_icon(attr_value: &str) -> bool {
pub fn set_base_url(document: &Handle, desired_base_href: String) -> RcDom {
let mut buf: Vec<u8> = Vec::new();
serialize(&mut buf, document, SerializeOpts::default())
.expect("unable to serialize DOM into buffer");
serialize(
&mut buf,
&SerializableHandle::from(document.clone()),
SerializeOpts::default(),
)
.expect("unable to serialize DOM into buffer");
let mut dom = html_to_dom(&buf, "utf-8".to_string());
let doc = dom.get_document();
@ -449,7 +436,7 @@ pub fn set_base_url(document: &Handle, desired_base_href: String) -> RcDom {
);
// Insert newly created BASE node into HEAD
head_node.children.borrow_mut().push(base_node.clone());
head_node.children.borrow_mut().push(base_node);
}
}
}
@ -459,9 +446,9 @@ pub fn set_base_url(document: &Handle, desired_base_href: String) -> RcDom {
pub fn set_charset(mut dom: RcDom, desired_charset: String) -> RcDom {
if let Some(meta_charset_node) = find_meta_charset_or_content_type_node(&dom.document) {
if let Some(_) = get_node_attr(&meta_charset_node, "charset") {
if get_node_attr(&meta_charset_node, "charset").is_some() {
set_node_attr(&meta_charset_node, "charset", Some(desired_charset));
} else if let Some(_) = get_node_attr(&meta_charset_node, "content") {
} else if get_node_attr(&meta_charset_node, "content").is_some() {
set_node_attr(
&meta_charset_node,
"content",
@ -481,10 +468,7 @@ pub fn set_charset(mut dom: RcDom, desired_charset: String) -> RcDom {
// Insert newly created META charset node into HEAD
if let Some(html_node) = get_child_node_by_name(&dom.document, "html") {
if let Some(head_node) = get_child_node_by_name(&html_node, "head") {
head_node
.children
.borrow_mut()
.push(meta_charset_node.clone());
head_node.children.borrow_mut().push(meta_charset_node);
}
}
}
@ -505,7 +489,7 @@ pub fn set_node_attr(node: &Handle, attr_name: &str, attr_value: Option<String>)
if let Some(attr_value) = attr_value.clone() {
let _ = &attrs_mut[i].value.clear();
let _ = &attrs_mut[i].value.push_slice(&attr_value.as_str());
let _ = &attrs_mut[i].value.push_slice(attr_value.as_str());
} else {
// Remove attr completely if attr_value is not defined
attrs_mut.remove(i);
@ -518,7 +502,7 @@ pub fn set_node_attr(node: &Handle, attr_name: &str, attr_value: Option<String>)
if !found_existing_attr {
// Add new attribute (since originally the target node didn't have it)
if let Some(attr_value) = attr_value.clone() {
if let Some(attr_value) = attr_value {
let name = LocalName::from(attr_name);
attrs_mut.push(Attribute {
@ -564,20 +548,24 @@ pub fn serialize_document(mut dom: RcDom, document_encoding: String, options: &O
// since there already may be one defined in the original document,
// and browsers don't allow re-defining them (for obvious reasons)
head.children.borrow_mut().reverse();
head.children.borrow_mut().push(meta.clone());
head.children.borrow_mut().push(meta);
head.children.borrow_mut().reverse();
}
}
}
serialize(&mut buf, &doc, SerializeOpts::default())
.expect("Unable to serialize DOM into buffer");
serialize(
&mut buf,
&SerializableHandle::from(doc),
SerializeOpts::default(),
)
.expect("Unable to serialize DOM into buffer");
// Unwrap NOSCRIPT elements
if options.unwrap_noscript {
let s: &str = &String::from_utf8_lossy(&buf);
let noscript_re = Regex::new(r"<(?P<c>/?noscript[^>]*)>").unwrap();
buf = noscript_re.replace_all(&s, "<!--$c-->").as_bytes().to_vec();
buf = noscript_re.replace_all(s, "<!--$c-->").as_bytes().to_vec();
}
if !document_encoding.is_empty() {
@ -592,8 +580,6 @@ pub fn serialize_document(mut dom: RcDom, document_encoding: String, options: &O
}
pub fn retrieve_and_embed_asset(
cache: &mut HashMap<String, Vec<u8>>,
client: &Client,
document_url: &Url,
node: &Handle,
attr_name: &str,
@ -601,18 +587,11 @@ pub fn retrieve_and_embed_asset(
options: &Options,
depth: u32,
) {
let resolved_url: Url = resolve_url(document_url, attr_value.clone());
match retrieve_asset(
cache,
client,
&document_url.clone(),
&resolved_url,
options,
depth + 1,
) {
let resolved_url: Url = resolve_url(document_url, attr_value);
match retrieve_asset(&document_url.clone(), &resolved_url, options, depth + 1) {
Ok((data, final_url, mut media_type, charset)) => {
let node_name: &str = get_node_name(&node).unwrap();
let node_name: &str = get_node_name(node).unwrap();
// Check integrity if it's a LINK or SCRIPT element
let mut ok_to_include: bool = true;
@ -639,28 +618,21 @@ pub fn retrieve_and_embed_asset(
if node_name == "link" && determine_link_node_type(node) == "stylesheet" {
// Stylesheet LINK elements require special treatment
let css: String = embed_css(cache, client, &final_url, &s, options, depth + 1);
let css: String = embed_css(&final_url, &s, options, depth + 1);
// Create and embed data URL
let css_data_url =
create_data_url(&media_type, &charset, css.as_bytes(), &final_url);
set_node_attr(&node, attr_name, Some(css_data_url.to_string()));
set_node_attr(node, attr_name, Some(css_data_url.to_string()));
} else if node_name == "frame" || node_name == "iframe" {
// (I)FRAMEs are also quite different from conventional resources
let frame_dom = html_to_dom(&data, charset.clone());
walk_and_embed_assets(
cache,
client,
&final_url,
&frame_dom.document,
&options,
depth + 1,
);
walk_and_embed_assets(&final_url, &frame_dom.document, options, depth + 1);
let mut frame_data: Vec<u8> = Vec::new();
serialize(
&mut frame_data,
&frame_dom.document,
&SerializableHandle::from(frame_dom.document),
SerializeOpts::default(),
)
.unwrap();
@ -674,14 +646,12 @@ pub fn retrieve_and_embed_asset(
// Every other type of element gets processed here
// Parse media type for SCRIPT elements
if node_name == "script" {
if let Some(_) = get_node_attr(node, "src") {
if let Some(script_node_type_attr_value) = get_node_attr(node, "type") {
media_type = script_node_type_attr_value.to_string();
} else {
// Fallback to default one if it's not specified
media_type = "application/javascript".to_string();
}
if node_name == "script" && get_node_attr(node, "src").is_some() {
if let Some(script_node_type_attr_value) = get_node_attr(node, "type") {
media_type = script_node_type_attr_value;
} else {
// Fallback to default one if it's not specified
media_type = "application/javascript".to_string();
}
}
@ -704,19 +674,12 @@ pub fn retrieve_and_embed_asset(
}
}
pub fn walk_and_embed_assets(
cache: &mut HashMap<String, Vec<u8>>,
client: &Client,
document_url: &Url,
node: &Handle,
options: &Options,
depth: u32,
) {
pub fn walk_and_embed_assets(document_url: &Url, node: &Handle, options: &Options, depth: u32) {
match node.data {
NodeData::Document => {
// Dig deeper
for child in node.children.borrow().iter() {
walk_and_embed_assets(cache, client, &document_url, child, options, depth);
walk_and_embed_assets(document_url, child, options, depth);
}
}
NodeData::Element {
@ -732,7 +695,7 @@ pub fn walk_and_embed_assets(
|| meta_attr_http_equiv_value.eq_ignore_ascii_case("location")
{
// Remove http-equiv attributes from META nodes if they're able to control the page
set_node_attr(&node, "http-equiv", None);
set_node_attr(node, "http-equiv", None);
}
}
}
@ -744,9 +707,7 @@ pub fn walk_and_embed_assets(
if let Some(link_attr_href_value) = get_node_attr(node, "href") {
if !options.no_images && !link_attr_href_value.is_empty() {
retrieve_and_embed_asset(
cache,
client,
&document_url,
document_url,
node,
"href",
&link_attr_href_value,
@ -764,19 +725,15 @@ pub fn walk_and_embed_assets(
set_node_attr(node, "href", None);
// Wipe integrity attribute
set_node_attr(node, "integrity", None);
} else {
if !link_attr_href_value.is_empty() {
retrieve_and_embed_asset(
cache,
client,
&document_url,
node,
"href",
&link_attr_href_value,
options,
depth,
);
}
} else if !link_attr_href_value.is_empty() {
retrieve_and_embed_asset(
document_url,
node,
"href",
&link_attr_href_value,
options,
depth,
);
}
}
} else if link_type == "preload" || link_type == "dns-prefetch" {
@ -786,7 +743,7 @@ pub fn walk_and_embed_assets(
// Make sure that all other LINKs' href attributes are full URLs
if let Some(link_attr_href_value) = get_node_attr(node, "href") {
let href_full_url: Url =
resolve_url(&document_url, &link_attr_href_value);
resolve_url(document_url, &link_attr_href_value);
set_node_attr(node, "href", Some(href_full_url.to_string()));
}
}
@ -809,8 +766,6 @@ pub fn walk_and_embed_assets(
if !options.no_images && !body_attr_background_value.is_empty() {
retrieve_and_embed_asset(
cache,
client,
document_url,
node,
"background",
@ -828,56 +783,46 @@ pub fn walk_and_embed_assets(
if options.no_images {
// Put empty images into src and data-src attributes
if img_attr_src_value != None {
if img_attr_src_value.is_some() {
set_node_attr(node, "src", Some(EMPTY_IMAGE_DATA_URL.to_string()));
}
if img_attr_data_src_value != None {
if img_attr_data_src_value.is_some() {
set_node_attr(node, "data-src", Some(EMPTY_IMAGE_DATA_URL.to_string()));
}
} else if img_attr_src_value.clone().unwrap_or_default().is_empty()
&& img_attr_data_src_value
.clone()
.unwrap_or_default()
.is_empty()
{
// Add empty src attribute
set_node_attr(node, "src", Some("".to_string()));
} else {
if img_attr_src_value.clone().unwrap_or_default().is_empty()
&& img_attr_data_src_value
.clone()
.unwrap_or_default()
.is_empty()
// Add data URL src attribute
let img_full_url: String = if !img_attr_data_src_value
.clone()
.unwrap_or_default()
.is_empty()
{
// Add empty src attribute
set_node_attr(node, "src", Some("".to_string()));
img_attr_data_src_value.unwrap_or_default()
} else {
// Add data URL src attribute
let img_full_url: String = if !img_attr_data_src_value
.clone()
.unwrap_or_default()
.is_empty()
{
img_attr_data_src_value.unwrap_or_default()
} else {
img_attr_src_value.unwrap_or_default()
};
retrieve_and_embed_asset(
cache,
client,
document_url,
node,
"src",
&img_full_url,
options,
depth,
);
}
img_attr_src_value.unwrap_or_default()
};
retrieve_and_embed_asset(
document_url,
node,
"src",
&img_full_url,
options,
depth,
);
}
// Resolve srcset attribute
if let Some(img_srcset) = get_node_attr(node, "srcset") {
if !img_srcset.is_empty() {
let resolved_srcset: String = embed_srcset(
cache,
client,
&document_url,
&img_srcset,
options,
depth,
);
let resolved_srcset: String =
embed_srcset(document_url, &img_srcset, options, depth);
set_node_attr(node, "srcset", Some(resolved_srcset));
}
}
@ -900,8 +845,6 @@ pub fn walk_and_embed_assets(
set_node_attr(node, "src", Some(value.to_string()));
} else {
retrieve_and_embed_asset(
cache,
client,
document_url,
node,
"src",
@ -933,8 +876,6 @@ pub fn walk_and_embed_assets(
if !options.no_images && !image_href.is_empty() {
retrieve_and_embed_asset(
cache,
client,
document_url,
node,
"href",
@ -954,8 +895,6 @@ pub fn walk_and_embed_assets(
set_node_attr(node, "src", None);
} else {
retrieve_and_embed_asset(
cache,
client,
document_url,
node,
"src",
@ -969,8 +908,6 @@ pub fn walk_and_embed_assets(
set_node_attr(node, "src", None);
} else {
retrieve_and_embed_asset(
cache,
client,
document_url,
node,
"src",
@ -983,44 +920,36 @@ pub fn walk_and_embed_assets(
}
if let Some(source_attr_srcset_value) = get_node_attr(node, "srcset") {
if parent_node_name == "picture" {
if !source_attr_srcset_value.is_empty() {
if options.no_images {
set_node_attr(
node,
"srcset",
Some(EMPTY_IMAGE_DATA_URL.to_string()),
);
} else {
let resolved_srcset: String = embed_srcset(
cache,
client,
&document_url,
&source_attr_srcset_value,
options,
depth,
);
set_node_attr(node, "srcset", Some(resolved_srcset));
}
if parent_node_name == "picture" && !source_attr_srcset_value.is_empty() {
if options.no_images {
set_node_attr(
node,
"srcset",
Some(EMPTY_IMAGE_DATA_URL.to_string()),
);
} else {
let resolved_srcset: String = embed_srcset(
document_url,
&source_attr_srcset_value,
options,
depth,
);
set_node_attr(node, "srcset", Some(resolved_srcset));
}
}
}
}
"a" | "area" => {
if let Some(anchor_attr_href_value) = get_node_attr(node, "href") {
if anchor_attr_href_value
.clone()
.trim()
.starts_with("javascript:")
{
if anchor_attr_href_value.trim().starts_with("javascript:") {
if options.no_js {
// Replace with empty JS call to preserve original behavior
set_node_attr(node, "href", Some("javascript:;".to_string()));
}
} else {
// Don't touch mailto: links or hrefs which begin with a hash sign
if !anchor_attr_href_value.clone().starts_with('#')
&& !is_url_and_has_protocol(&anchor_attr_href_value.clone())
if !anchor_attr_href_value.starts_with('#')
&& !is_url_and_has_protocol(&anchor_attr_href_value)
{
let href_full_url: Url =
resolve_url(document_url, &anchor_attr_href_value);
@ -1037,15 +966,13 @@ pub fn walk_and_embed_assets(
// Empty inner content
node.children.borrow_mut().clear();
// Remove src attribute
if script_attr_src != None {
if script_attr_src.is_some() {
set_node_attr(node, "src", None);
// Wipe integrity attribute
set_node_attr(node, "integrity", None);
}
} else if !script_attr_src.clone().unwrap_or_default().is_empty() {
retrieve_and_embed_asset(
cache,
client,
document_url,
node,
"src",
@ -1063,14 +990,8 @@ pub fn walk_and_embed_assets(
for child_node in node.children.borrow_mut().iter_mut() {
if let NodeData::Text { ref contents } = child_node.data {
let mut tendril = contents.borrow_mut();
let replacement = embed_css(
cache,
client,
&document_url,
tendril.as_ref(),
options,
depth,
);
let replacement =
embed_css(document_url, tendril.as_ref(), options, depth);
tendril.clear();
tendril.push_slice(&replacement);
}
@ -1094,9 +1015,7 @@ pub fn walk_and_embed_assets(
// Ignore (i)frames with empty source (they cause infinite loops)
if !frame_attr_src_value.trim().is_empty() {
retrieve_and_embed_asset(
cache,
client,
&document_url,
document_url,
node,
"src",
&frame_attr_src_value,
@ -1114,8 +1033,6 @@ pub fn walk_and_embed_assets(
set_node_attr(node, "src", None);
} else {
retrieve_and_embed_asset(
cache,
client,
document_url,
node,
"src",
@ -1133,8 +1050,6 @@ pub fn walk_and_embed_assets(
set_node_attr(node, "src", None);
} else {
retrieve_and_embed_asset(
cache,
client,
document_url,
node,
"src",
@ -1157,8 +1072,6 @@ pub fn walk_and_embed_assets(
);
} else {
retrieve_and_embed_asset(
cache,
client,
document_url,
node,
"poster",
@ -1183,11 +1096,9 @@ pub fn walk_and_embed_assets(
);
// Embed assets of NOSCRIPT node contents
walk_and_embed_assets(
cache,
client,
&document_url,
document_url,
&noscript_contents_dom.document,
&options,
options,
depth,
);
// Get rid of original contents
@ -1198,8 +1109,12 @@ pub fn walk_and_embed_assets(
{
if let Some(body) = get_child_node_by_name(&html, "body") {
let mut buf: Vec<u8> = Vec::new();
serialize(&mut buf, &body, SerializeOpts::default())
.expect("Unable to serialize DOM into buffer");
serialize(
&mut buf,
&SerializableHandle::from(body),
SerializeOpts::default(),
)
.expect("Unable to serialize DOM into buffer");
let result = String::from_utf8_lossy(&buf);
noscript_contents.push_slice(&result);
}
@ -1219,14 +1134,8 @@ pub fn walk_and_embed_assets(
} else {
// Embed URLs found within the style attribute of this node
if let Some(node_attr_style_value) = get_node_attr(node, "style") {
let embedded_style = embed_css(
cache,
client,
&document_url,
&node_attr_style_value,
options,
depth,
);
let embedded_style =
embed_css(document_url, &node_attr_style_value, options, depth);
set_node_attr(node, "style", Some(embedded_style));
}
}
@ -1249,7 +1158,7 @@ pub fn walk_and_embed_assets(
// Dig deeper
for child in node.children.borrow().iter() {
walk_and_embed_assets(cache, client, &document_url, child, options, depth);
walk_and_embed_assets(document_url, child, options, depth);
}
}
_ => {

@ -1,4 +1,4 @@
const JS_DOM_EVENT_ATTRS: &'static [&str] = &[
const JS_DOM_EVENT_ATTRS: &[&str] = &[
// From WHATWG HTML spec 8.1.5.2 "Event handlers on elements, Document objects, and Window objects":
// https://html.spec.whatwg.org/#event-handlers-on-elements,-document-objects,-and-window-objects
// https://html.spec.whatwg.org/#attributes-3 (table "List of event handler content attributes")
@ -98,6 +98,5 @@ const JS_DOM_EVENT_ATTRS: &'static [&str] = &[
pub fn attr_is_event_handler(attr_name: &str) -> bool {
JS_DOM_EVENT_ATTRS
.iter()
.find(|a| attr_name.eq_ignore_ascii_case(a))
.is_some()
.any(|a| attr_name.eq_ignore_ascii_case(a))
}

@ -1,20 +1,16 @@
use encoding_rs::Encoding;
use html5ever::rcdom::RcDom;
use reqwest::blocking::Client;
use reqwest::header::{HeaderMap, HeaderValue, USER_AGENT};
use std::collections::HashMap;
use markup5ever_rcdom::RcDom;
use std::fs;
use std::io::{self, prelude::*, Error, Write};
use std::path::Path;
use std::process;
use std::time::Duration;
use url::Url;
use monolith::html::{
add_favicon, create_metadata_tag, get_base_url, get_charset, has_favicon, html_to_dom,
serialize_document, set_base_url, set_charset, walk_and_embed_assets,
};
use monolith::opts::Options;
use monolith::opts::OPTIONS;
use monolith::url::{create_data_url, resolve_url};
use monolith::utils::retrieve_asset;
@ -32,13 +28,13 @@ impl Output {
}
}
fn write(&mut self, bytes: &Vec<u8>) -> Result<(), Error> {
fn write(&mut self, bytes: &[u8]) -> Result<(), Error> {
match self {
Output::Stdout(stdout) => {
stdout.write_all(bytes)?;
// Ensure newline at end of output
if bytes.last() != Some(&b"\n"[0]) {
stdout.write(b"\n")?;
stdout.write_all(b"\n")?;
}
stdout.flush()
}
@ -46,7 +42,7 @@ impl Output {
file.write_all(bytes)?;
// Ensure newline at end of output
if bytes.last() != Some(&b"\n"[0]) {
file.write(b"\n")?;
file.write_all(b"\n")?;
}
file.flush()
}
@ -64,19 +60,17 @@ pub fn read_stdin() -> Vec<u8> {
}
fn main() {
let options = Options::from_args();
// Check if target was provided
if options.target.len() == 0 {
if !options.silent {
if OPTIONS.target.is_empty() {
if !OPTIONS.silent {
eprintln!("No target specified");
}
process::exit(1);
}
// Check if custom charset is valid
if let Some(custom_charset) = options.charset.clone() {
if !Encoding::for_label_no_replacement(custom_charset.as_bytes()).is_some() {
if let Some(custom_charset) = &OPTIONS.charset {
if Encoding::for_label_no_replacement(custom_charset.as_bytes()).is_none() {
eprintln!("Unknown encoding: {}", &custom_charset);
process::exit(1);
}
@ -84,18 +78,18 @@ fn main() {
let mut use_stdin: bool = false;
let target_url = match options.target.as_str() {
let target_url = match OPTIONS.target.as_str() {
"-" => {
// Read from pipe (stdin)
use_stdin = true;
// Set default target URL to an empty data URL; the user can set it via --base-url
Url::parse("data:text/html,").unwrap()
}
target => match Url::parse(&target) {
target => match Url::parse(target) {
Ok(url) => match url.scheme() {
"data" | "file" | "http" | "https" => url,
unsupported_scheme => {
if !options.silent {
if !OPTIONS.silent {
eprintln!("Unsupported target URL type: {}", unsupported_scheme);
}
process::exit(1)
@ -107,11 +101,11 @@ fn main() {
match path.exists() {
true => match path.is_file() {
true => {
let canonical_path = fs::canonicalize(&path).unwrap();
let canonical_path = fs::canonicalize(path).unwrap();
match Url::from_file_path(canonical_path) {
Ok(url) => url,
Err(_) => {
if !options.silent {
if !OPTIONS.silent {
eprintln!(
"Could not generate file URL out of given path: {}",
&target
@ -122,7 +116,7 @@ fn main() {
}
}
false => {
if !options.silent {
if !OPTIONS.silent {
eprintln!("Local target is not a file: {}", &target);
}
process::exit(1);
@ -139,26 +133,6 @@ fn main() {
},
};
// Initialize client
let mut cache = HashMap::new();
let mut header_map = HeaderMap::new();
if let Some(user_agent) = &options.user_agent {
header_map.insert(
USER_AGENT,
HeaderValue::from_str(&user_agent).expect("Invalid User-Agent header specified"),
);
}
let client = if options.timeout > 0 {
Client::builder().timeout(Duration::from_secs(options.timeout))
} else {
// No timeout is default
Client::builder()
}
.danger_accept_invalid_certs(options.insecure)
.default_headers(header_map)
.build()
.expect("Failed to initialize HTTP client");
// At first we assume that base URL is the same as target URL
let mut base_url: Url = target_url.clone();
@ -173,7 +147,7 @@ fn main() {
|| (target_url.scheme() == "http" || target_url.scheme() == "https")
|| target_url.scheme() == "data"
{
match retrieve_asset(&mut cache, &client, &target_url, &target_url, &options, 0) {
match retrieve_asset(&target_url, &target_url, &OPTIONS, 0) {
Ok((retrieved_data, final_url, media_type, charset)) => {
// Provide output as text without processing it, the way browsers do
if !media_type.eq_ignore_ascii_case("text/html")
@ -181,7 +155,7 @@ fn main() {
{
// Define output
let mut output =
Output::new(&options.output).expect("Could not prepare output");
Output::new(&OPTIONS.output).expect("Could not prepare output");
// Write retrieved data into STDOUT or file
output
@ -192,7 +166,7 @@ fn main() {
process::exit(0);
}
if options
if OPTIONS
.base_url
.clone()
.unwrap_or("".to_string())
@ -205,7 +179,7 @@ fn main() {
document_encoding = charset;
}
Err(_) => {
if !options.silent {
if !OPTIONS.silent {
eprintln!("Could not retrieve target document");
}
process::exit(1);
@ -233,7 +207,7 @@ fn main() {
}
// Use custom base URL if specified, read and use what's in the DOM otherwise
let custom_base_url: String = options.base_url.clone().unwrap_or("".to_string());
let custom_base_url: String = OPTIONS.base_url.clone().unwrap_or("".to_string());
if custom_base_url.is_empty() {
// No custom base URL is specified
// Try to see if document has BASE element
@ -260,12 +234,12 @@ fn main() {
// Relative paths could work for documents saved from filesystem
let path: &Path = Path::new(&custom_base_url);
if path.exists() {
match Url::from_file_path(fs::canonicalize(&path).unwrap()) {
match Url::from_file_path(fs::canonicalize(path).unwrap()) {
Ok(file_url) => {
base_url = file_url;
}
Err(_) => {
if !options.silent {
if !OPTIONS.silent {
eprintln!(
"Could not map given path to base URL: {}",
custom_base_url
@ -281,57 +255,50 @@ fn main() {
}
// Traverse through the document and embed remote assets
walk_and_embed_assets(&mut cache, &client, &base_url, &dom.document, &options, 0);
walk_and_embed_assets(&base_url, &dom.document, &OPTIONS, 0);
// Update or add new BASE element to reroute network requests and hash-links
if let Some(new_base_url) = options.base_url.clone() {
if let Some(new_base_url) = OPTIONS.base_url.clone() {
dom = set_base_url(&dom.document, new_base_url);
}
// Request and embed /favicon.ico (unless it's already linked in the document)
if !options.no_images
if !OPTIONS.no_images
&& (target_url.scheme() == "http" || target_url.scheme() == "https")
&& !has_favicon(&dom.document)
{
let favicon_ico_url: Url = resolve_url(&base_url, "/favicon.ico");
match retrieve_asset(
&mut cache,
&client,
&target_url,
&favicon_ico_url,
&options,
0,
) {
match retrieve_asset(&target_url, &favicon_ico_url, &OPTIONS, 0) {
Ok((data, final_url, media_type, charset)) => {
let favicon_data_url: Url =
create_data_url(&media_type, &charset, &data, &final_url);
dom = add_favicon(&dom.document, favicon_data_url.to_string());
}
Err(_) => {
// Failed to retrieve /favicon.ico
eprintln!("Failed to retrieve /favicon.ico");
}
}
}
// Save using specified charset, if given
if let Some(custom_charset) = options.charset.clone() {
if let Some(custom_charset) = OPTIONS.charset.clone() {
document_encoding = custom_charset;
dom = set_charset(dom, document_encoding.clone());
}
// Serialize DOM tree
let mut result: Vec<u8> = serialize_document(dom, document_encoding, &options);
let mut result: Vec<u8> = serialize_document(dom, document_encoding, &OPTIONS);
// Prepend metadata comment tag
if !options.no_metadata {
if !OPTIONS.no_metadata {
let mut metadata_comment: String = create_metadata_tag(&target_url);
metadata_comment += "\n";
result.splice(0..0, metadata_comment.as_bytes().to_vec());
}
// Define output
let mut output = Output::new(&options.output).expect("Could not prepare output");
let mut output = Output::new(&OPTIONS.output).expect("Could not prepare output");
// Write result into STDOUT or file
output.write(&result).expect("Could not write output");

@ -1,6 +1,9 @@
use clap::{App, Arg, ArgAction};
use once_cell::sync::Lazy;
use std::env;
pub static OPTIONS: Lazy<Options> = Lazy::new(Options::from_args);
#[derive(Default)]
pub struct Options {
pub no_audio: bool,
@ -27,7 +30,7 @@ pub struct Options {
pub unwrap_noscript: bool,
}
const ASCII: &'static str = " \
const ASCII: &str = " \
_____ ______________ __________ ___________________ ___
| \\ / \\ | | | | | |
| \\_/ __ \\_| __ | | ___ ___ |__| |
@ -37,13 +40,13 @@ const ASCII: &'static str = " \
|___| |__________| \\_____________________| |___| |___| |___|
";
const DEFAULT_NETWORK_TIMEOUT: u64 = 120;
const DEFAULT_USER_AGENT: &'static str =
const DEFAULT_USER_AGENT: &str =
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0";
const ENV_VAR_NO_COLOR: &str = "NO_COLOR";
const ENV_VAR_TERM: &str = "TERM";
impl Options {
pub fn from_args() -> Options {
fn from_args() -> Options {
let app = App::new(env!("CARGO_PKG_NAME"))
.version(env!("CARGO_PKG_VERSION"))
.author(format!("\n{}\n\n", env!("CARGO_PKG_AUTHORS").replace(':', "\n")).as_str())
@ -107,7 +110,7 @@ impl Options {
options.charset = Some(charset.to_string());
}
if let Some(domains) = app.get_many::<String>("domains") {
let list_of_domains: Vec<String> = domains.map(|v| v.clone()).collect::<Vec<_>>();
let list_of_domains: Vec<String> = domains.cloned().collect::<Vec<_>>();
options.domains = Some(list_of_domains);
}
options.ignore_errors = app.is_present("ignore-errors");

@ -4,11 +4,11 @@ use url::Url;
use crate::utils::{detect_media_type, parse_content_type};
pub const EMPTY_IMAGE_DATA_URL: &'static str = "data:image/png;base64,\
pub const EMPTY_IMAGE_DATA_URL: &str = "data:image/png;base64,\
iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAQAAADY4iz3AAAAEUlEQVR42mNkwAkYR6UolgIACvgADsuK6xYAAAAASUVORK5CYII=";
pub fn clean_url(url: Url) -> Url {
let mut url = url.clone();
let mut url = url;
// Clear fragment (if any)
url.set_fragment(None);
@ -19,7 +19,7 @@ pub fn clean_url(url: Url) -> Url {
pub fn create_data_url(media_type: &str, charset: &str, data: &[u8], final_asset_url: &Url) -> Url {
// TODO: move this block out of this function
let media_type: String = if media_type.is_empty() {
detect_media_type(data, &final_asset_url)
detect_media_type(data, final_asset_url)
} else {
media_type.to_string()
};
@ -39,13 +39,11 @@ pub fn create_data_url(media_type: &str, charset: &str, data: &[u8], final_asset
}
pub fn is_url_and_has_protocol(input: &str) -> bool {
match Url::parse(&input) {
match Url::parse(input) {
Ok(parsed_url) => {
return parsed_url.scheme().len() > 0;
}
Err(_) => {
return false;
return !parsed_url.scheme().is_empty();
}
Err(_) => false,
}
}
@ -72,7 +70,7 @@ pub fn parse_data_url(url: &Url) -> (String, String, Vec<u8>) {
}
pub fn resolve_url(from: &Url, to: &str) -> Url {
match Url::parse(&to) {
match Url::parse(to) {
Ok(parsed_url) => parsed_url,
Err(_) => match from.join(to) {
Ok(joined) => joined,

@ -1,15 +1,18 @@
use once_cell::sync::Lazy;
use reqwest::blocking::Client;
use reqwest::header::CONTENT_TYPE;
use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE, USER_AGENT};
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::Mutex;
use std::time::Duration;
use url::Url;
use crate::opts::Options;
use crate::opts::{Options, OPTIONS};
use crate::url::{clean_url, parse_data_url};
const ANSI_COLOR_RED: &'static str = "\x1b[31m";
const ANSI_COLOR_RESET: &'static str = "\x1b[0m";
const ANSI_COLOR_RED: &str = "\x1b[31m";
const ANSI_COLOR_RESET: &str = "\x1b[0m";
const MAGIC: [[&[u8]; 2]; 18] = [
// Image
[b"GIF87a", b"image/gif"],
@ -39,6 +42,31 @@ const PLAINTEXT_MEDIA_TYPES: &[&str] = &[
"image/svg+xml",
];
static CLIENT: Lazy<Client> = Lazy::new(|| {
let mut header_map = HeaderMap::new();
if let Some(user_agent) = &OPTIONS.user_agent {
header_map.insert(
USER_AGENT,
HeaderValue::from_str(user_agent).expect("Invalid User-Agent header specified"),
);
}
if OPTIONS.timeout > 0 {
Client::builder().timeout(Duration::from_secs(OPTIONS.timeout))
} else {
Client::builder()
}
.danger_accept_invalid_certs(OPTIONS.insecure)
.default_headers(header_map)
.build()
.expect("Failed to initialize HTTP client")
});
static CACHE: Lazy<Mutex<HashMap<String, Vec<u8>>>> = Lazy::new(|| {
let mut m = HashMap::new();
Mutex::new(m)
});
pub fn detect_media_type(data: &[u8], url: &Url) -> String {
// At first attempt to read file's header
for magic_item in MAGIC.iter() {
@ -93,7 +121,7 @@ pub fn detect_media_type_by_file_name(filename: &str) -> String {
}
pub fn domain_is_within_domain(domain: &str, domain_to_match_against: &str) -> bool {
if domain_to_match_against.len() == 0 {
if domain_to_match_against.is_empty() {
return false;
}
@ -101,12 +129,12 @@ pub fn domain_is_within_domain(domain: &str, domain_to_match_against: &str) -> b
return true;
}
let domain_partials: Vec<&str> = domain.trim_end_matches(".").rsplit(".").collect();
let domain_partials: Vec<&str> = domain.trim_end_matches('.').rsplit('.').collect();
let domain_to_match_against_partials: Vec<&str> = domain_to_match_against
.trim_end_matches(".")
.rsplit(".")
.trim_end_matches('.')
.rsplit('.')
.collect();
let domain_to_match_against_starts_with_a_dot = domain_to_match_against.starts_with(".");
let domain_to_match_against_starts_with_a_dot = domain_to_match_against.starts_with('.');
let mut i: usize = 0;
let l: usize = std::cmp::max(
@ -137,7 +165,7 @@ pub fn domain_is_within_domain(domain: &str, domain_to_match_against: &str) -> b
let parts_match = domain_to_match_against_partial.eq_ignore_ascii_case(domain_partial);
if !parts_match && domain_to_match_against_partial.len() != 0 {
if !parts_match && !domain_to_match_against_partial.is_empty() {
ok = false;
break;
}
@ -172,29 +200,22 @@ pub fn parse_content_type(content_type: &str) -> (String, String, bool) {
// Parse meta data
let content_type_items: Vec<&str> = content_type.split(';').collect();
let mut i: i8 = 0;
for item in &content_type_items {
for (i, item) in (0_i8..).zip(content_type_items.iter()) {
if i == 0 {
if item.trim().len() > 0 {
if !item.trim().is_empty() {
media_type = item.trim().to_string();
}
} else {
if item.trim().eq_ignore_ascii_case("base64") {
is_base64 = true;
} else if item.trim().starts_with("charset=") {
charset = item.trim().chars().skip(8).collect();
}
} else if item.trim().eq_ignore_ascii_case("base64") {
is_base64 = true;
} else if item.trim().starts_with("charset=") {
charset = item.trim().chars().skip(8).collect();
}
i += 1;
}
(media_type, charset, is_base64)
}
pub fn retrieve_asset(
cache: &mut HashMap<String, Vec<u8>>,
client: &Client,
parent_url: &Url,
url: &Url,
options: &Options,
@ -208,11 +229,10 @@ pub fn retrieve_asset(
if parent_url.scheme() != "file" {
if !options.silent {
eprintln!(
"{}{}{} ({}){}",
"{}{}{} (Security Error){}",
indent(depth).as_str(),
if options.no_color { "" } else { ANSI_COLOR_RED },
&url,
"Security Error",
if options.no_color {
""
} else {
@ -221,10 +241,10 @@ pub fn retrieve_asset(
);
}
// Provoke error
client.get("").send()?;
CLIENT.get("").send()?;
}
let path_buf: PathBuf = url.to_file_path().unwrap().clone();
let path_buf: PathBuf = url.to_file_path().unwrap();
let path: &Path = path_buf.as_path();
if path.exists() {
if path.is_dir() {
@ -243,20 +263,16 @@ pub fn retrieve_asset(
}
// Provoke error
Err(client.get("").send().unwrap_err())
Err(CLIENT.get("").send().unwrap_err())
} else {
if !options.silent {
eprintln!("{}{}", indent(depth).as_str(), &url);
}
let file_blob: Vec<u8> = fs::read(&path).expect("Unable to read file");
let file_blob: Vec<u8> = fs::read(path).expect("Unable to read file");
let file_type = detect_media_type(&file_blob, url);
Ok((
file_blob.clone(),
url.clone(),
detect_media_type(&file_blob, url),
"".to_string(),
))
Ok((file_blob, url.clone(), file_type, "".to_string()))
}
} else {
if !options.silent {
@ -274,19 +290,19 @@ pub fn retrieve_asset(
}
// Provoke error
Err(client.get("").send().unwrap_err())
Err(CLIENT.get("").send().unwrap_err())
}
} else {
let cache_key: String = clean_url(url.clone()).as_str().to_string();
if cache.contains_key(&cache_key) {
if CACHE.lock().unwrap().contains_key(&cache_key) {
// URL is in cache, we get and return it
if !options.silent {
eprintln!("{}{} (from cache)", indent(depth).as_str(), &url);
}
Ok((
cache.get(&cache_key).unwrap().to_vec(),
CACHE.lock().unwrap().get(&cache_key).unwrap().to_vec(),
url.clone(),
"".to_string(),
"".to_string(),
@ -295,16 +311,16 @@ pub fn retrieve_asset(
if let Some(domains) = &options.domains {
let domain_matches = domains
.iter()
.any(|d| domain_is_within_domain(url.host_str().unwrap(), &d.trim()));
.any(|d| domain_is_within_domain(url.host_str().unwrap(), d.trim()));
if (options.blacklist_domains && domain_matches)
|| (!options.blacklist_domains && !domain_matches)
{
return Err(client.get("").send().unwrap_err());
return Err(CLIENT.get("").send().unwrap_err());
}
}
// URL not in cache, we retrieve the file
match client.get(url.as_str()).send() {
match CLIENT.get(url.as_str()).send() {
Ok(response) => {
if !options.ignore_errors && response.status() != reqwest::StatusCode::OK {
if !options.silent {
@ -322,7 +338,7 @@ pub fn retrieve_asset(
);
}
// Provoke error
return Err(client.get("").send().unwrap_err());
return Err(CLIENT.get("").send().unwrap_err());
}
let response_url: Url = response.url().clone();
@ -344,7 +360,7 @@ pub fn retrieve_asset(
.and_then(|header| header.to_str().ok())
.unwrap_or("");
let (media_type, charset, _is_base64) = parse_content_type(&content_type);
let (media_type, charset, _is_base64) = parse_content_type(content_type);
// Convert response into a byte array
let mut data: Vec<u8> = vec![];
@ -370,7 +386,7 @@ pub fn retrieve_asset(
}
// Add retrieved resource to cache
cache.insert(new_cache_key, data.clone());
CACHE.lock().unwrap().insert(new_cache_key, data.clone());
// Return
Ok((data, response_url, media_type, charset))
@ -391,7 +407,7 @@ pub fn retrieve_asset(
);
}
Err(client.get("").send().unwrap_err())
Err(CLIENT.get("").send().unwrap_err())
}
}
}

@ -95,8 +95,8 @@ mod passing {
{file_url_css}\n \
{file_url_css}\n\
",
file_url_html = Url::from_file_path(fs::canonicalize(&path_html).unwrap()).unwrap(),
file_url_css = Url::from_file_path(fs::canonicalize(&path_css).unwrap()).unwrap(),
file_url_html = Url::from_file_path(fs::canonicalize(path_html).unwrap()).unwrap(),
file_url_css = Url::from_file_path(fs::canonicalize(path_css).unwrap()).unwrap(),
)
);

@ -23,7 +23,7 @@ mod passing {
.unwrap()
.to_str()
.unwrap()
.replace("\\", "/");
.replace('\\', "/");
let out = cmd
.arg("-M")
.arg(format!(
@ -88,7 +88,7 @@ mod passing {
String::from_utf8_lossy(&out.stderr),
format!(
"{file_url_html}\n",
file_url_html = Url::from_file_path(fs::canonicalize(&path_html).unwrap()).unwrap(),
file_url_html = Url::from_file_path(fs::canonicalize(path_html).unwrap()).unwrap(),
)
);
@ -124,7 +124,7 @@ mod passing {
.unwrap()
.to_str()
.unwrap()
.replace("\\", "/");
.replace('\\', "/");
let file_url_protocol: &str = if cfg!(windows) { "file:///" } else { "file://" };
let out = cmd
.arg("-M")
@ -188,8 +188,8 @@ mod passing {
{file_url_html}\n \
{file_url_svg}\n\
",
file_url_html = Url::from_file_path(fs::canonicalize(&path_html).unwrap()).unwrap(),
file_url_svg = Url::from_file_path(fs::canonicalize(&path_svg).unwrap()).unwrap(),
file_url_html = Url::from_file_path(fs::canonicalize(path_html).unwrap()).unwrap(),
file_url_svg = Url::from_file_path(fs::canonicalize(path_svg).unwrap()).unwrap(),
)
);
@ -210,24 +210,16 @@ mod passing {
.unwrap()
.to_str()
.unwrap()
.replace("\\", "/");
.replace('\\', "/");
let file_url_protocol: &str = if cfg!(windows) { "file:///" } else { "file://" };
let out = cmd
.arg("-M")
.arg("-i")
.arg(if cfg!(windows) {
format!(
"{file}{cwd}/tests/_data_/integrity/index.html",
file = file_url_protocol,
cwd = cwd_normalized,
)
} else {
format!(
"{file}{cwd}/tests/_data_/integrity/index.html",
file = file_url_protocol,
cwd = cwd_normalized,
)
})
.arg(format!(
"{file}{cwd}/tests/_data_/integrity/index.html",
file = file_url_protocol,
cwd = cwd_normalized,
))
.output()
.unwrap();

@ -30,8 +30,8 @@ mod passing {
{file_url_html}\n \
{file_url_svg}\n\
",
file_url_html = Url::from_file_path(fs::canonicalize(&path_html).unwrap()).unwrap(),
file_url_svg = Url::from_file_path(fs::canonicalize(&path_svg).unwrap()).unwrap(),
file_url_html = Url::from_file_path(fs::canonicalize(path_html).unwrap()).unwrap(),
file_url_svg = Url::from_file_path(fs::canonicalize(path_svg).unwrap()).unwrap(),
)
);
@ -61,8 +61,8 @@ mod passing {
{file_url_html}\n \
{file_url_svg}\n\
",
file_url_html = Url::from_file_path(fs::canonicalize(&path_html).unwrap()).unwrap(),
file_url_svg = Url::from_file_path(fs::canonicalize(&path_svg).unwrap()).unwrap(),
file_url_html = Url::from_file_path(fs::canonicalize(path_html).unwrap()).unwrap(),
file_url_svg = Url::from_file_path(fs::canonicalize(path_svg).unwrap()).unwrap(),
)
);
@ -92,8 +92,8 @@ mod passing {
{file_url_html}\n \
{file_url_svg}\n\
",
file_url_html = Url::from_file_path(fs::canonicalize(&path_html).unwrap()).unwrap(),
file_url_svg = Url::from_file_path(fs::canonicalize(&path_svg).unwrap()).unwrap(),
file_url_html = Url::from_file_path(fs::canonicalize(path_html).unwrap()).unwrap(),
file_url_svg = Url::from_file_path(fs::canonicalize(path_svg).unwrap()).unwrap(),
)
);
@ -123,8 +123,8 @@ mod passing {
{file_url_html}\n \
{file_url_svg}\n\
",
file_url_html = Url::from_file_path(fs::canonicalize(&path_html).unwrap()).unwrap(),
file_url_svg = Url::from_file_path(fs::canonicalize(&path_svg).unwrap()).unwrap(),
file_url_html = Url::from_file_path(fs::canonicalize(path_html).unwrap()).unwrap(),
file_url_svg = Url::from_file_path(fs::canonicalize(path_svg).unwrap()).unwrap(),
)
);

@ -16,7 +16,7 @@ mod passing {
#[test]
fn properly_save_document_with_gb2312() {
let cwd = env::current_dir().unwrap();
let cwd_normalized: String = cwd.to_str().unwrap().replace("\\", "/");
let cwd_normalized: String = cwd.to_str().unwrap().replace('\\', "/");
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();
let out = cmd
.arg("-M")
@ -111,7 +111,7 @@ mod passing {
#[test]
fn properly_save_document_with_gb2312_custom_charset() {
let cwd = env::current_dir().unwrap();
let cwd_normalized: String = cwd.to_str().unwrap().replace("\\", "/");
let cwd_normalized: String = cwd.to_str().unwrap().replace('\\', "/");
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();
let out = cmd
.arg("-M")
@ -198,7 +198,7 @@ mod failing {
#[test]
fn change_iso88591_to_utf8_to_properly_display_html_entities() {
let cwd = env::current_dir().unwrap();
let cwd_normalized: String = cwd.to_str().unwrap().replace("\\", "/");
let cwd_normalized: String = cwd.to_str().unwrap().replace('\\', "/");
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();
let out = cmd
.arg("-M")

@ -7,9 +7,7 @@
#[cfg(test)]
mod passing {
use reqwest::blocking::Client;
use reqwest::Url;
use std::collections::HashMap;
use monolith::css;
use monolith::opts::Options;
@ -17,34 +15,25 @@ mod passing {
#[test]
fn empty_input() {
let cache = &mut HashMap::new();
let client = Client::new();
let document_url: Url = Url::parse("data:,").unwrap();
let options = Options::default();
assert_eq!(
css::embed_css(cache, &client, &document_url, "", &options, 0),
""
);
assert_eq!(css::embed_css(&document_url, "", &options, 0), "");
}
#[test]
fn trim_if_empty() {
let cache = &mut HashMap::new();
let client = Client::new();
let document_url: Url = Url::parse("https://doesntmatter.local/").unwrap();
let options = Options::default();
assert_eq!(
css::embed_css(cache, &client, &document_url, "\t \t ", &options, 0,),
css::embed_css(&document_url, "\t \t ", &options, 0,),
""
);
}
#[test]
fn style_exclude_unquoted_images() {
let cache = &mut HashMap::new();
let client = Client::new();
let document_url: Url = Url::parse("https://doesntmatter.local/").unwrap();
let mut options = Options::default();
options.no_images = true;
@ -59,7 +48,7 @@ mod passing {
height: calc(100vh - 10pt)";
assert_eq!(
css::embed_css(cache, &client, &document_url, &STYLE, &options, 0,),
css::embed_css(&document_url, STYLE, &options, 0,),
format!(
"/* border: none;*/\
background-image: url(\"{empty_image}\"); \
@ -75,8 +64,6 @@ mod passing {
#[test]
fn style_exclude_single_quoted_images() {
let cache = &mut HashMap::new();
let client = Client::new();
let document_url: Url = Url::parse("data:,").unwrap();
let mut options = Options::default();
options.no_images = true;
@ -91,7 +78,7 @@ mod passing {
height: calc(100vh - 10pt)";
assert_eq!(
css::embed_css(cache, &client, &document_url, &STYLE, &options, 0),
css::embed_css(&document_url, STYLE, &options, 0),
format!(
"/* border: none;*/\
background-image: url(\"{empty_image}\"); \
@ -107,8 +94,6 @@ mod passing {
#[test]
fn style_block() {
let cache = &mut HashMap::new();
let client = Client::new();
let document_url: Url = Url::parse("file:///").unwrap();
let mut options = Options::default();
options.silent = true;
@ -121,16 +106,11 @@ mod passing {
\n\
html > body {}";
assert_eq!(
css::embed_css(cache, &client, &document_url, &CSS, &options, 0),
CSS
);
assert_eq!(css::embed_css(&document_url, CSS, &options, 0), CSS);
}
#[test]
fn attribute_selectors() {
let cache = &mut HashMap::new();
let client = Client::new();
let document_url: Url = Url::parse("https://doesntmatter.local/").unwrap();
let mut options = Options::default();
options.silent = true;
@ -165,16 +145,11 @@ mod passing {
}
";
assert_eq!(
css::embed_css(cache, &client, &document_url, &CSS, &options, 0),
CSS
);
assert_eq!(css::embed_css(&document_url, CSS, &options, 0), CSS);
}
#[test]
fn import_string() {
let cache = &mut HashMap::new();
let client = Client::new();
let document_url: Url = Url::parse("https://doesntmatter.local/").unwrap();
let mut options = Options::default();
options.silent = true;
@ -188,7 +163,7 @@ mod passing {
";
assert_eq!(
css::embed_css(cache, &client, &document_url, &CSS, &options, 0,),
css::embed_css(&document_url, CSS, &options, 0,),
"\
@charset \"UTF-8\";\n\
\n\
@ -201,8 +176,6 @@ mod passing {
#[test]
fn hash_urls() {
let cache = &mut HashMap::new();
let client = Client::new();
let document_url: Url = Url::parse("https://doesntmatter.local/").unwrap();
let mut options = Options::default();
options.silent = true;
@ -217,16 +190,11 @@ mod passing {
}\n\
";
assert_eq!(
css::embed_css(cache, &client, &document_url, &CSS, &options, 0,),
CSS
);
assert_eq!(css::embed_css(&document_url, CSS, &options, 0,), CSS);
}
#[test]
fn transform_percentages_and_degrees() {
let cache = &mut HashMap::new();
let client = Client::new();
let document_url: Url = Url::parse("https://doesntmatter.local/").unwrap();
let mut options = Options::default();
options.silent = true;
@ -239,16 +207,11 @@ mod passing {
}\n\
";
assert_eq!(
css::embed_css(cache, &client, &document_url, &CSS, &options, 0,),
CSS
);
assert_eq!(css::embed_css(&document_url, CSS, &options, 0,), CSS);
}
#[test]
fn unusual_indents() {
let cache = &mut HashMap::new();
let client = Client::new();
let document_url: Url = Url::parse("https://doesntmatter.local/").unwrap();
let mut options = Options::default();
options.silent = true;
@ -263,16 +226,11 @@ mod passing {
}\n\
";
assert_eq!(
css::embed_css(cache, &client, &document_url, &CSS, &options, 0,),
CSS
);
assert_eq!(css::embed_css(&document_url, CSS, &options, 0,), CSS);
}
#[test]
fn exclude_fonts() {
let cache = &mut HashMap::new();
let client = Client::new();
let document_url: Url = Url::parse("https://doesntmatter.local/").unwrap();
let mut options = Options::default();
options.no_fonts = true;
@ -311,16 +269,11 @@ mod passing {
}\n\
";
assert_eq!(
css::embed_css(cache, &client, &document_url, &CSS, &options, 0,),
CSS_OUT
);
assert_eq!(css::embed_css(&document_url, CSS, &options, 0,), CSS_OUT);
}
#[test]
fn content() {
let cache = &mut HashMap::new();
let client = Client::new();
let document_url: Url = Url::parse("data:,").unwrap();
let mut options = Options::default();
options.silent = true;
@ -336,16 +289,11 @@ mod passing {
white-space: pre }\n\
";
assert_eq!(
css::embed_css(cache, &client, &document_url, &CSS, &options, 0,),
CSS_OUT
);
assert_eq!(css::embed_css(&document_url, CSS, &options, 0,), CSS_OUT);
}
#[test]
fn ie_css_hack() {
let cache = &mut HashMap::new();
let client = Client::new();
let document_url: Url = Url::parse("data:,").unwrap();
let mut options = Options::default();
options.silent = true;
@ -363,9 +311,6 @@ mod passing {
}\n\
";
assert_eq!(
css::embed_css(cache, &client, &document_url, &CSS, &options, 0,),
CSS_OUT
);
assert_eq!(css::embed_css(&document_url, CSS, &options, 0,), CSS_OUT);
}
}

@ -9,17 +9,23 @@
mod passing {
use html5ever::serialize::{serialize, SerializeOpts};
use markup5ever_rcdom::SerializableHandle;
use monolith::html;
#[test]
fn basic() {
let html = "<div>text</div>";
let mut dom = html::html_to_dom(&html.as_bytes().to_vec(), "".to_string());
let mut dom = html::html_to_dom(html.as_bytes(), "".to_string());
dom = html::add_favicon(&dom.document, "I_AM_A_FAVICON_DATA_URL".to_string());
let mut buf: Vec<u8> = Vec::new();
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
serialize(
&mut buf,
&SerializableHandle::from(dom.document),
SerializeOpts::default(),
)
.unwrap();
assert_eq!(
buf.iter().map(|&c| c as char).collect::<String>(),

@ -7,9 +7,7 @@
#[cfg(test)]
mod passing {
use reqwest::blocking::Client;
use reqwest::Url;
use std::collections::HashMap;
use monolith::html;
use monolith::opts::Options;
@ -17,20 +15,12 @@ mod passing {
#[test]
fn small_medium_large() {
let cache = &mut HashMap::new();
let client = Client::new();
let srcset_value = "small.png 1x, medium.png 1.5x, large.png 2x";
let mut options = Options::default();
options.no_images = true;
options.silent = true;
let embedded_css = html::embed_srcset(
cache,
&client,
&Url::parse("data:,").unwrap(),
&srcset_value,
&options,
0,
);
let embedded_css =
html::embed_srcset(&Url::parse("data:,").unwrap(), srcset_value, &options, 0);
assert_eq!(
embedded_css,
@ -43,20 +33,12 @@ mod passing {
#[test]
fn small_medium_only_medium_has_scale() {
let cache = &mut HashMap::new();
let client = Client::new();
let srcset_value = "small.png, medium.png 1.5x";
let mut options = Options::default();
options.no_images = true;
options.silent = true;
let embedded_css = html::embed_srcset(
cache,
&client,
&Url::parse("data:,").unwrap(),
&srcset_value,
&options,
0,
);
let embedded_css =
html::embed_srcset(&Url::parse("data:,").unwrap(), srcset_value, &options, 0);
assert_eq!(
embedded_css,
@ -66,20 +48,12 @@ mod passing {
#[test]
fn commas_within_file_names() {
let cache = &mut HashMap::new();
let client = Client::new();
let srcset_value = "small,s.png 1x, large,l.png 2x";
let mut options = Options::default();
options.no_images = true;
options.silent = true;
let embedded_css = html::embed_srcset(
cache,
&client,
&Url::parse("data:,").unwrap(),
&srcset_value,
&options,
0,
);
let embedded_css =
html::embed_srcset(&Url::parse("data:,").unwrap(), srcset_value, &options, 0);
assert_eq!(
embedded_css,
@ -89,20 +63,12 @@ mod passing {
#[test]
fn tabs_and_newlines_after_commas() {
let cache = &mut HashMap::new();
let client = Client::new();
let srcset_value = "small,s.png 1x,\nmedium,m.png 2x,\nlarge,l.png 3x";
let mut options = Options::default();
options.no_images = true;
options.silent = true;
let embedded_css = html::embed_srcset(
cache,
&client,
&Url::parse("data:,").unwrap(),
&srcset_value,
&options,
0,
);
let embedded_css =
html::embed_srcset(&Url::parse("data:,").unwrap(), srcset_value, &options, 0);
assert_eq!(
embedded_css,
@ -123,9 +89,7 @@ mod passing {
#[cfg(test)]
mod failing {
use reqwest::blocking::Client;
use reqwest::Url;
use std::collections::HashMap;
use monolith::html;
use monolith::opts::Options;
@ -133,20 +97,12 @@ mod failing {
#[test]
fn trailing_comma() {
let cache = &mut HashMap::new();
let client = Client::new();
let srcset_value = "small.png 1x, large.png 2x,";
let mut options = Options::default();
options.no_images = true;
options.silent = true;
let embedded_css = html::embed_srcset(
cache,
&client,
&Url::parse("data:,").unwrap(),
&srcset_value,
&options,
0,
);
let embedded_css =
html::embed_srcset(&Url::parse("data:,").unwrap(), srcset_value, &options, 0);
assert_eq!(
embedded_css,

@ -7,14 +7,14 @@
#[cfg(test)]
mod passing {
use html5ever::rcdom::{Handle, NodeData};
use markup5ever_rcdom::{Handle, NodeData};
use monolith::html;
#[test]
fn div_two_style_attributes() {
let html = "<!doctype html><html><head></head><body><DIV STYLE=\"color: blue;\" style=\"display: none;\"></div></body></html>";
let dom = html::html_to_dom(&html.as_bytes().to_vec(), "".to_string());
let dom = html::html_to_dom(&html.as_bytes(), "".to_string());
let mut count = 0;
fn test_walk(node: &Handle, i: &mut i8) {

@ -7,7 +7,7 @@
#[cfg(test)]
mod passing {
use html5ever::rcdom::{Handle, NodeData};
use markup5ever_rcdom::{Handle, NodeData};
use monolith::html;

@ -7,7 +7,7 @@
#[cfg(test)]
mod passing {
use html5ever::rcdom::{Handle, NodeData};
use markup5ever_rcdom::{Handle, NodeData};
use monolith::html;

@ -8,8 +8,7 @@
#[cfg(test)]
mod passing {
use html5ever::serialize::{serialize, SerializeOpts};
use reqwest::blocking::Client;
use std::collections::HashMap;
use markup5ever_rcdom::SerializableHandle;
use url::Url;
use monolith::html;
@ -18,8 +17,6 @@ mod passing {
#[test]
fn basic() {
let cache = &mut HashMap::new();
let html: &str = "<div><P></P></div>";
let dom = html::html_to_dom(&html.as_bytes().to_vec(), "".to_string());
let url: Url = Url::parse("http://localhost").unwrap();
@ -27,12 +24,15 @@ mod passing {
let mut options = Options::default();
options.silent = true;
let client = Client::new();
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options, 0);
html::walk_and_embed_assets(&url, &dom.document, &options, 0);
let mut buf: Vec<u8> = Vec::new();
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
serialize(
&mut buf,
&SerializableHandle::from(dom.document),
SerializeOpts::default(),
)
.unwrap();
assert_eq!(
buf.iter().map(|&c| c as char).collect::<String>(),
@ -45,17 +45,19 @@ mod passing {
let html = "<div><P></P><iframe src=\"\"></iframe></div>";
let dom = html::html_to_dom(&html.as_bytes().to_vec(), "".to_string());
let url: Url = Url::parse("http://localhost").unwrap();
let cache = &mut HashMap::new();
let mut options = Options::default();
options.silent = true;
let client = Client::new();
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options, 0);
html::walk_and_embed_assets(&url, &dom.document, &options, 0);
let mut buf: Vec<u8> = Vec::new();
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
serialize(
&mut buf,
&SerializableHandle::from(dom.document),
SerializeOpts::default(),
)
.unwrap();
assert_eq!(
buf.iter().map(|&c| c as char).collect::<String>(),
@ -68,17 +70,19 @@ mod passing {
let html = "<frameset><frame src=\"\"></frameset>";
let dom = html::html_to_dom(&html.as_bytes().to_vec(), "".to_string());
let url: Url = Url::parse("http://localhost").unwrap();
let cache = &mut HashMap::new();
let mut options = Options::default();
options.silent = true;
let client = Client::new();
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options, 0);
html::walk_and_embed_assets(&url, &dom.document, &options, 0);
let mut buf: Vec<u8> = Vec::new();
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
serialize(
&mut buf,
&SerializableHandle::from(dom.document),
SerializeOpts::default(),
)
.unwrap();
assert_eq!(
buf.iter().map(|&c| c as char).collect::<String>(),
@ -96,18 +100,20 @@ mod passing {
";
let dom = html::html_to_dom(&html.as_bytes().to_vec(), "".to_string());
let url: Url = Url::parse("http://localhost").unwrap();
let cache = &mut HashMap::new();
let mut options = Options::default();
options.no_css = true;
options.silent = true;
let client = Client::new();
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options, 0);
html::walk_and_embed_assets(&url, &dom.document, &options, 0);
let mut buf: Vec<u8> = Vec::new();
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
serialize(
&mut buf,
&SerializableHandle::from(dom.document),
SerializeOpts::default(),
)
.unwrap();
assert_eq!(
buf.iter().map(|&c| c as char).collect::<String>(),
@ -132,18 +138,20 @@ mod passing {
<div><img src=\"http://localhost/assets/mono_lisa.png\" /></div>";
let dom = html::html_to_dom(&html.as_bytes().to_vec(), "".to_string());
let url: Url = Url::parse("http://localhost").unwrap();
let cache = &mut HashMap::new();
let mut options = Options::default();
options.no_images = true;
options.silent = true;
let client = Client::new();
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options, 0);
html::walk_and_embed_assets(&url, &dom.document, &options, 0);
let mut buf: Vec<u8> = Vec::new();
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
serialize(
&mut buf,
&SerializableHandle::from(dom.document),
SerializeOpts::default(),
)
.unwrap();
assert_eq!(
buf.iter().map(|&c| c as char).collect::<String>(),
@ -169,18 +177,20 @@ mod passing {
"<body background=\"no/such/image.png\" background=\"no/such/image2.png\"></body>";
let dom = html::html_to_dom(&html.as_bytes().to_vec(), "".to_string());
let url: Url = Url::parse("http://localhost").unwrap();
let cache = &mut HashMap::new();
let mut options = Options::default();
options.no_images = true;
options.silent = true;
let client = Client::new();
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options, 0);
html::walk_and_embed_assets(&url, &dom.document, &options, 0);
let mut buf: Vec<u8> = Vec::new();
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
serialize(
&mut buf,
&SerializableHandle::from(dom.document),
SerializeOpts::default(),
)
.unwrap();
assert_eq!(
buf.iter().map(|&c| c as char).collect::<String>(),
@ -193,18 +203,20 @@ mod passing {
let html = "<frameset><frame src=\"http://trackbook.com\"></frameset>";
let dom = html::html_to_dom(&html.as_bytes().to_vec(), "".to_string());
let url: Url = Url::parse("http://localhost").unwrap();
let cache = &mut HashMap::new();
let mut options = Options::default();
options.no_frames = true;
options.silent = true;
let client = Client::new();
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options, 0);
html::walk_and_embed_assets(&url, &dom.document, &options, 0);
let mut buf: Vec<u8> = Vec::new();
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
serialize(
&mut buf,
&SerializableHandle::from(dom.document),
SerializeOpts::default(),
)
.unwrap();
assert_eq!(
buf.iter().map(|&c| c as char).collect::<String>(),
@ -225,18 +237,20 @@ mod passing {
let html = "<iframe src=\"http://trackbook.com\"></iframe>";
let dom = html::html_to_dom(&html.as_bytes().to_vec(), "".to_string());
let url: Url = Url::parse("http://localhost").unwrap();
let cache = &mut HashMap::new();
let mut options = Options::default();
options.no_frames = true;
options.silent = true;
let client = Client::new();
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options, 0);
html::walk_and_embed_assets(&url, &dom.document, &options, 0);
let mut buf: Vec<u8> = Vec::new();
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
serialize(
&mut buf,
&SerializableHandle::from(dom.document),
SerializeOpts::default(),
)
.unwrap();
assert_eq!(
buf.iter().map(|&c| c as char).collect::<String>(),
@ -261,18 +275,20 @@ mod passing {
";
let dom = html::html_to_dom(&html.as_bytes().to_vec(), "".to_string());
let url: Url = Url::parse("http://localhost").unwrap();
let cache = &mut HashMap::new();
let mut options = Options::default();
options.no_js = true;
options.silent = true;
let client = Client::new();
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options, 0);
html::walk_and_embed_assets(&url, &dom.document, &options, 0);
let mut buf: Vec<u8> = Vec::new();
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
serialize(
&mut buf,
&SerializableHandle::from(dom.document),
SerializeOpts::default(),
)
.unwrap();
assert_eq!(
buf.iter().map(|&c| c as char).collect::<String>(),
@ -296,17 +312,19 @@ mod passing {
<link integrity=\"sha384-12345\" rel=\"something\" href=\"https://some-site.com/some-file.ext\" />";
let dom = html::html_to_dom(&html.as_bytes().to_vec(), "".to_string());
let url: Url = Url::parse("http://localhost").unwrap();
let cache = &mut HashMap::new();
let mut options = Options::default();
options.silent = true;
let client = Client::new();
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options, 0);
html::walk_and_embed_assets(&url, &dom.document, &options, 0);
let mut buf: Vec<u8> = Vec::new();
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
serialize(
&mut buf,
&SerializableHandle::from(dom.document),
SerializeOpts::default(),
)
.unwrap();
assert_eq!(
buf.iter().map(|&c| c as char).collect::<String>(),
@ -331,19 +349,21 @@ mod passing {
";
let dom = html::html_to_dom(&html.as_bytes().to_vec(), "".to_string());
let url: Url = Url::parse("http://localhost").unwrap();
let cache = &mut HashMap::new();
let mut options = Options::default();
options.no_css = true;
options.no_js = true;
options.silent = true;
let client = Client::new();
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options, 0);
html::walk_and_embed_assets(&url, &dom.document, &options, 0);
let mut buf: Vec<u8> = Vec::new();
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
serialize(
&mut buf,
&SerializableHandle::from(dom.document),
SerializeOpts::default(),
)
.unwrap();
assert_eq!(
buf.iter().map(|&c| c as char).collect::<String>(),
@ -369,19 +389,21 @@ mod passing {
";
let dom = html::html_to_dom(&html.as_bytes().to_vec(), "".to_string());
let url: Url = Url::parse("http://localhost").unwrap();
let cache = &mut HashMap::new();
let mut options = Options::default();
options.no_css = true;
options.no_js = true;
options.silent = true;
let client = Client::new();
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options, 0);
html::walk_and_embed_assets(&url, &dom.document, &options, 0);
let mut buf: Vec<u8> = Vec::new();
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
serialize(
&mut buf,
&SerializableHandle::from(dom.document),
SerializeOpts::default(),
)
.unwrap();
assert_eq!(
buf.iter().map(|&c| c as char).collect::<String>(),
@ -413,7 +435,6 @@ mod passing {
";
let dom = html::html_to_dom(&html.as_bytes().to_vec(), "".to_string());
let url: Url = Url::parse("http://localhost").unwrap();
let cache = &mut HashMap::new();
let mut options = Options::default();
options.no_css = true;
@ -422,12 +443,15 @@ mod passing {
options.no_images = true;
options.silent = true;
let client = Client::new();
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options, 0);
html::walk_and_embed_assets(&url, &dom.document, &options, 0);
let mut buf: Vec<u8> = Vec::new();
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
serialize(
&mut buf,
&SerializableHandle::from(dom.document),
SerializeOpts::default(),
)
.unwrap();
assert_eq!(
buf.iter().map(|&c| c as char).collect::<String>(),
@ -455,18 +479,20 @@ mod passing {
</html>";
let dom = html::html_to_dom(&html.as_bytes().to_vec(), "".to_string());
let url: Url = Url::parse("http://localhost").unwrap();
let cache = &mut HashMap::new();
let mut options = Options::default();
options.no_images = true;
options.silent = true;
let client = Client::new();
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options, 0);
html::walk_and_embed_assets(&url, &dom.document, &options, 0);
let mut buf: Vec<u8> = Vec::new();
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
serialize(
&mut buf,
&SerializableHandle::from(dom.document),
SerializeOpts::default(),
)
.unwrap();
assert_eq!(
buf.iter().map(|&c| c as char).collect::<String>(),
@ -491,17 +517,19 @@ mod passing {
let html = "<script id=\"data\" type=\"application/json\">{\"mono\":\"lith\"}</script>";
let dom = html::html_to_dom(&html.as_bytes().to_vec(), "".to_string());
let url: Url = Url::parse("http://localhost").unwrap();
let cache = &mut HashMap::new();
let mut options = Options::default();
options.silent = true;
let client = Client::new();
html::walk_and_embed_assets(cache, &client, &url, &dom.document, &options, 0);
html::walk_and_embed_assets(&url, &dom.document, &options, 0);
let mut buf: Vec<u8> = Vec::new();
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
serialize(
&mut buf,
&SerializableHandle::from(dom.document),
SerializeOpts::default(),
)
.unwrap();
assert_eq!(
buf.iter().map(|&c| c as char).collect::<String>(),

@ -13,22 +13,22 @@ mod passing {
fn defaults() {
let options: Options = Options::default();
assert_eq!(options.no_audio, false);
assert!(!options.no_audio);
assert_eq!(options.base_url, None);
assert_eq!(options.no_css, false);
assert!(!options.no_css);
assert_eq!(options.charset, None);
assert_eq!(options.no_frames, false);
assert_eq!(options.no_fonts, false);
assert_eq!(options.no_images, false);
assert_eq!(options.isolate, false);
assert_eq!(options.no_js, false);
assert_eq!(options.insecure, false);
assert_eq!(options.no_metadata, false);
assert!(!options.no_frames);
assert!(!options.no_fonts);
assert!(!options.no_images);
assert!(!options.isolate);
assert!(!options.no_js);
assert!(!options.insecure);
assert!(!options.no_metadata);
assert_eq!(options.output, "".to_string());
assert_eq!(options.silent, false);
assert!(!options.silent);
assert_eq!(options.timeout, 0);
assert_eq!(options.user_agent, None);
assert_eq!(options.no_video, false);
assert!(!options.no_video);
assert_eq!(options.target, "".to_string());
}

@ -84,27 +84,25 @@ mod failing {
#[test]
fn url_with_no_protocol() {
assert_eq!(
url::is_url_and_has_protocol("//some-hostname.com/some-file.html"),
false
assert!(
!url::is_url_and_has_protocol("//some-hostname.com/some-file.html")
);
}
#[test]
fn relative_path() {
assert_eq!(
url::is_url_and_has_protocol("some-hostname.com/some-file.html"),
false
assert!(
!url::is_url_and_has_protocol("some-hostname.com/some-file.html")
);
}
#[test]
fn relative_to_root_path() {
assert_eq!(url::is_url_and_has_protocol("/some-file.html"), false);
assert!(!url::is_url_and_has_protocol("/some-file.html"));
}
#[test]
fn empty_string() {
assert_eq!(url::is_url_and_has_protocol(""), false);
assert!(!url::is_url_and_has_protocol(""));
}
}

@ -7,9 +7,7 @@
#[cfg(test)]
mod passing {
use reqwest::blocking::Client;
use reqwest::Url;
use std::collections::HashMap;
use std::env;
use monolith::opts::Options;
@ -18,17 +16,12 @@ mod passing {
#[test]
fn read_data_url() {
let cache = &mut HashMap::new();
let client = Client::new();
let mut options = Options::default();
options.silent = true;
// If both source and target are data URLs,
// ensure the result contains target data URL
let (data, final_url, media_type, charset) = utils::retrieve_asset(
cache,
&client,
&Url::parse("data:text/html;base64,c291cmNl").unwrap(),
&Url::parse("data:text/html;base64,dGFyZ2V0").unwrap(),
&options,
@ -49,9 +42,6 @@ mod passing {
#[test]
fn read_local_file_with_file_url_parent() {
let cache = &mut HashMap::new();
let client = Client::new();
let mut options = Options::default();
options.silent = true;
@ -60,8 +50,6 @@ mod passing {
// Inclusion of local assets from local sources should be allowed
let cwd = env::current_dir().unwrap();
let (data, final_url, media_type, charset) = utils::retrieve_asset(
cache,
&client,
&Url::parse(&format!(
"{file}{cwd}/tests/_data_/basic/local-file.html",
file = file_url_protocol,
@ -102,25 +90,18 @@ mod passing {
#[cfg(test)]
mod failing {
use reqwest::blocking::Client;
use reqwest::Url;
use std::collections::HashMap;
use monolith::opts::Options;
use monolith::utils;
#[test]
fn read_local_file_with_data_url_parent() {
let cache = &mut HashMap::new();
let client = Client::new();
let mut options = Options::default();
options.silent = true;
// Inclusion of local assets from data URL sources should not be allowed
match utils::retrieve_asset(
cache,
&client,
&Url::parse("data:text/html;base64,SoUrCe").unwrap(),
&Url::parse("file:///etc/passwd").unwrap(),
&options,
@ -137,16 +118,11 @@ mod failing {
#[test]
fn read_local_file_with_https_parent() {
let cache = &mut HashMap::new();
let client = Client::new();
let mut options = Options::default();
options.silent = true;
// Inclusion of local assets from remote sources should not be allowed
match utils::retrieve_asset(
cache,
&client,
&Url::parse("https://kernel.org/").unwrap(),
&Url::parse("file:///etc/passwd").unwrap(),
&options,

Loading…
Cancel
Save