diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml new file mode 100644 index 0000000..abcbbff --- /dev/null +++ b/.github/workflows/audit.yml @@ -0,0 +1,16 @@ +name: Security audit +on: + push: + paths: + - '**/Cargo.toml' + - '**/Cargo.lock' + schedule: + - cron: '0 0 * * *' +jobs: + security_audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/audit-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/general.yml b/.github/workflows/general.yml new file mode 100644 index 0000000..5ac5f64 --- /dev/null +++ b/.github/workflows/general.yml @@ -0,0 +1,55 @@ +name: Rust + +on: [pull_request, push] + +env: + CARGO_TERM_COLOR: always + +jobs: + test: + name: Test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + - run: cargo test + + fmt: + name: Rustfmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + - run: cargo fmt --all -- --check + + clippy: + name: Clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: + components: clippy + - run: cargo clippy -- -D warnings + + coverage: + name: Code coverage + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install stable toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Install cargo-tarpaulin + uses: taiki-e/install-action@cargo-tarpaulin + + - name: Run cargo-tarpaulin + run: cargo tarpaulin --out Xml --ignore-tests diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..925711e --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,34 @@ +name: Release + +on: + release: + types: [published] + workflow_dispatch: null + push: null + +jobs: + release: + name: release ${{ matrix.target }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-unknown-linux-musl + archive: tar.gz + + # TODO: Enable more platforms in the future + # - target: x86_64-apple-darwin + # archive: zip + # - target: x86_64-pc-windows-gnu + # archive: zip + steps: + - uses: actions/checkout@master + - name: Compile and release + uses: rust-build/rust-build.action@v1.4.5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RUSTTARGET: ${{ matrix.target }} + TOOLCHAIN_VERSION: 1.83.0 + EXTRA_FILES: "README.md" + ARCHIVE_TYPES: ${{ matrix.archive }} diff --git a/.gitignore b/.gitignore index 8b00285..defe2ea 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ # Example server example/server/node_modules + +# Editor temp files +.vscode diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9eda9d2..0000000 --- a/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -language: rust -cache: cargo -dist: trusty -os: - - linux - - osx - -rust: - # FIXME: Enable again when rustfmt is available on nightly - # - nightly - - stable - -before_script: - - rustup component add rustfmt - - cargo install --force cargo-audit - - cargo generate-lockfile - -script: - - cargo fmt --all -- --check - - cargo build - - cargo test - - cargo audit diff --git a/Cargo.lock b/Cargo.lock index ae4ac5e..883e93e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,148 +1,140 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" -version = "0.15.2" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a2e47a1fbe209ee101dd6d61285226744c6c8d3c21c8dc878ba6cb9f467f3a" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aho-corasick" -version = "0.7.18" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "ansi_term" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ - "winapi 0.3.9", + "winapi", ] [[package]] name = "async-trait" -version = "0.1.50" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b98e84bbb4cbcdd97da190ba0c58a1bb0de2c1fdf67d159e192ed766aeca722" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", - "winapi 0.3.9", + "winapi", ] [[package]] name = "autocfg" -version = "1.0.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" -version = "0.3.60" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7815ea54e4d821e791162e078acbebfd6d8c8939cd559c9335dceb1c8ca7282" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", - "cfg-if 1.0.0", + "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] -name = "base-x" -version = "0.2.8" +name = "base64" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" -version = "0.13.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "bstr" -version = "0.2.16" +name = "bitflags" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279" -dependencies = [ - "lazy_static", - "memchr", - "regex-automata", - "serde", -] +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bumpalo" -version = "3.7.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "0.5.6" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.0.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "cc" -version = "1.0.68" +version = "1.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" +dependencies = [ + "find-msvc-tools", + "shlex", +] [[package]] name = "cfg-if" @@ -152,13 +144,13 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "2.33.3" +version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "ansi_term", "atty", - "bitflags", + "bitflags 1.3.2", "strsim", "textwrap", "unicode-width", @@ -167,26 +159,19 @@ dependencies = [ [[package]] name = "colored" -version = "1.9.3" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" dependencies = [ - "atty", "lazy_static", - "winapi 0.3.9", + "windows-sys 0.48.0", ] -[[package]] -name = "const_fn" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92cfa0fd5690b3cf8c1ef2cabbd9b7ef22fa53cf5e1f92b05103f6d5d1cf6e7" - [[package]] name = "cookie" -version = "0.14.4" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a5d7b21829bc7b4bf4754a978a241ae54ea55a40f92bb20216e54096f4b951" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" dependencies = [ "percent-encoding", "time", @@ -195,15 +180,17 @@ dependencies = [ [[package]] name = "cookie_store" -version = "0.12.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3818dfca4b0cb5211a659bbcbb94225b7127407b2b135e650d717bfb78ab10d3" +checksum = "3fc4bff745c9b4c7fb1e97b25d13153da2bc7796260141df62378998d070207f" dependencies = [ "cookie", + "document-features", "idna", "log", "publicsuffix", "serde", + "serde_derive", "serde_json", "time", "url", @@ -211,9 +198,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.9.1" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -221,46 +208,40 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.2" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "crc32fast" -version = "1.2.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] name = "crossbeam-channel" -version = "0.5.1" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ - "cfg-if 1.0.0", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.8" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" -dependencies = [ - "cfg-if 1.0.0", - "lazy_static", -] +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "csv" -version = "1.1.6" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" dependencies = [ - "bstr", "csv-core", "itoa", "ryu", @@ -269,22 +250,45 @@ dependencies = [ [[package]] name = "csv-core" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" dependencies = [ "memchr", ] [[package]] -name = "discard" -version = "1.0.4" +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "document-features" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] [[package]] name = "drill" -version = "0.7.2" +version = "0.9.0" dependencies = [ "async-trait", "clap", @@ -292,50 +296,66 @@ dependencies = [ "csv", "futures", "hdrhistogram", + "hex", "lazy_static", "linked-hash-map", "num_cpus", "openssl-sys", - "rand 0.7.3", + "rand", "regex", "reqwest", "serde", "serde_json", + "serde_yaml", + "tempfile", "tokio", "url", - "yaml-rust", ] [[package]] name = "encoding_rs" -version = "0.8.28" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] -name = "enum-as-inner" -version = "0.3.3" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c5f0096a91d210159eceb2ff5e1c4da18388a170e1e3ce948aac9c8fdbbf595" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", + "libc", + "windows-sys 0.52.0", ] +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "find-msvc-tools" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" + [[package]] name = "flate2" -version = "1.0.22" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ - "cfg-if 1.0.0", "crc32fast", - "libc", "miniz_oxide", ] @@ -362,35 +382,18 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ - "matches", "percent-encoding", ] -[[package]] -name = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -dependencies = [ - "bitflags", - "fuchsia-zircon-sys", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" - [[package]] name = "futures" -version = "0.3.15" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7e43a803dae2fa37c1f6a8fe121e1f7bf9548b4dfc0522a42f34145dadfc27" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -403,9 +406,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.15" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e682a68b29a882df0545c143dc3646daefe80ba479bcdede94d5a703de2871e2" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -413,15 +416,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.15" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0402f765d8a89a26043b889b26ce3c4679d268fa6bb22cd7c6aad98340e179d1" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.15" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "badaa6a909fac9e7236d0620a2f57f7664640c56575b71a7552fbd68deafab79" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -430,18 +433,16 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.15" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acc499defb3b348f8d8f3f66415835a9131856ff7714bf10dadfc4ec4bdb29a1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.15" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c40298486cdf52cc00cd6d6987892ba502c7656a16a4192a9992b1ccedd121" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ - "autocfg", - "proc-macro-hack", "proc-macro2", "quote", "syn", @@ -449,23 +450,22 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.15" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a57bead0ceff0d6dde8f465ecd96c9338121bb7717d3e7b108059531870c4282" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.15" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a16bef9fc1a4dddb5bee51c989e3fbba26569cbb0e31f5b303c184e3dd33dae" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.15" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ - "autocfg", "futures-channel", "futures-core", "futures-io", @@ -473,74 +473,60 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.6", + "pin-project-lite", "pin-utils", - "proc-macro-hack", - "proc-macro-nested", "slab", ] [[package]] name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.3" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi", ] [[package]] name = "gimli" -version = "0.24.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "h2" -version = "0.2.7" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ - "bytes 0.5.6", + "atomic-waker", + "bytes", "fnv", "futures-core", "futures-sink", - "futures-util", "http", "indexmap", "slab", "tokio", "tokio-util", "tracing", - "tracing-futures", ] [[package]] name = "hashbrown" -version = "0.9.1" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" [[package]] name = "hdrhistogram" -version = "7.4.0" +version = "7.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6490be71f07a5f62b564bc58e36953f675833df11c7e4a0647bee7a07ca1ec5e" +checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" dependencies = [ - "base64", + "base64 0.21.7", "byteorder", "crossbeam-channel", "flate2", @@ -549,312 +535,376 @@ dependencies = [ ] [[package]] -name = "heck" -version = "0.3.3" +name = "hermit-abi" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ - "unicode-segmentation", + "libc", ] [[package]] name = "hermit-abi" -version = "0.1.18" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" -dependencies = [ - "libc", -] +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] -name = "hostname" -version = "0.3.1" +name = "hex" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -dependencies = [ - "libc", - "match_cfg", - "winapi 0.3.9", -] +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "http" -version = "0.2.4" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ - "bytes 1.0.1", - "fnv", + "bytes", "itoa", ] [[package]] name = "http-body" -version = "0.3.1" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ - "bytes 0.5.6", + "bytes", "http", ] [[package]] -name = "httparse" -version = "1.4.1" +name = "http-body-util" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a87b616e37e93c22fb19bcd386f02f3af5ea98a25670ad0fce773de23c5e68" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] [[package]] -name = "httpdate" -version = "0.3.2" +name = "httparse" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "hyper" -version = "0.13.10" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a6f157065790a3ed2f88679250419b5cdd96e714a0d65f7797fd337186e96bb" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ - "bytes 0.5.6", + "atomic-waker", + "bytes", "futures-channel", "futures-core", - "futures-util", "h2", "http", "http-body", "httparse", - "httpdate", "itoa", - "pin-project", - "socket2", + "pin-project-lite", + "pin-utils", + "smallvec", "tokio", - "tower-service", - "tracing", "want", ] +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "hyper-tls" -version = "0.4.3" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d979acc56dcb5b8dddba3917601745e877576475aa046df3226eabdecef78eed" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ - "bytes 0.5.6", + "bytes", + "http-body-util", "hyper", + "hyper-util", "native-tls", "tokio", - "tokio-tls", + "tokio-native-tls", + "tower-service", ] [[package]] -name = "idna" -version = "0.2.3" +name = "hyper-util" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2 0.6.1", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", ] [[package]] -name = "indexmap" -version = "1.6.2" +name = "icu_collections" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ - "autocfg", - "hashbrown", + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", ] [[package]] -name = "iovec" -version = "0.1.4" +name = "icu_locale_core" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ - "libc", + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", ] [[package]] -name = "ipconfig" -version = "0.2.2" +name = "icu_normalizer" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "socket2", - "widestring", - "winapi 0.3.9", - "winreg 0.6.2", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", ] [[package]] -name = "ipnet" -version = "2.3.1" +name = "icu_normalizer_data" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] -name = "itoa" -version = "0.4.7" +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] -name = "js-sys" -version = "0.3.51" +name = "icu_provider" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ - "wasm-bindgen", + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", ] [[package]] -name = "kernel32-sys" -version = "0.2.2" +name = "idna" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ - "winapi 0.2.8", - "winapi-build", + "idna_adapter", + "smallvec", + "utf8_iter", ] [[package]] -name = "lazy_static" -version = "1.4.0" +name = "idna_adapter" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] [[package]] -name = "libc" -version = "0.2.97" +name = "indexmap" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown", +] [[package]] -name = "linked-hash-map" -version = "0.5.4" +name = "ipnet" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" [[package]] -name = "log" -version = "0.4.14" +name = "iri-string" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" dependencies = [ - "cfg-if 1.0.0", + "memchr", + "serde", ] [[package]] -name = "lru-cache" -version = "0.1.2" +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ - "linked-hash-map", + "once_cell", + "wasm-bindgen", ] [[package]] -name = "match_cfg" -version = "0.1.0" +name = "lazy_static" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] -name = "matches" -version = "0.1.8" +name = "libc" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] -name = "memchr" -version = "2.4.0" +name = "linked-hash-map" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] -name = "mime" -version = "0.3.16" +name = "linux-raw-sys" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] -name = "mime_guess" -version = "2.0.3" +name = "litemap" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" -dependencies = [ - "mime", - "unicase", -] +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] -name = "minimal-lexical" -version = "0.1.4" +name = "litrs" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c64630dcdd71f1a64c435f54885086a0de5d6a12d104d69b165fb7d5286d677" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] -name = "miniz_oxide" -version = "0.4.4" +name = "log" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg", -] +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] -name = "mio" -version = "0.6.23" +name = "memchr" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" -dependencies = [ - "cfg-if 0.1.10", - "fuchsia-zircon", - "fuchsia-zircon-sys", - "iovec", - "kernel32-sys", - "libc", - "log", - "miow", - "net2", - "slab", - "winapi 0.2.8", -] +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] -name = "mio-uds" -version = "0.6.8" +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "iovec", - "libc", - "mio", + "adler2", ] [[package]] -name = "miow" -version = "0.2.2" +name = "mio" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ - "kernel32-sys", - "net2", - "winapi 0.2.8", - "ws2_32-sys", + "hermit-abi 0.3.9", + "libc", + "wasi", + "windows-sys 0.52.0", ] [[package]] name = "native-tls" -version = "0.2.7" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -866,98 +916,103 @@ dependencies = [ "tempfile", ] -[[package]] -name = "net2" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" -dependencies = [ - "cfg-if 0.1.10", - "libc", - "winapi 0.3.9", -] - [[package]] name = "nom" -version = "7.0.0" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffd9d26838a953b4af82cbeb9f1592c6798916983959be223a7124e992742c1" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", - "version_check", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.13.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", ] [[package]] name = "object" -version = "0.25.3" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a38f2be3697a57b4060074ff41b44c16870d916ad7877c17696e063257482bc7" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.8.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "openssl" -version = "0.10.35" +version = "0.10.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "549430950c79ae24e6d02e0b7404534ecf311d94cc9f861e9e4020187d13d885" +checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" dependencies = [ - "bitflags", - "cfg-if 1.0.0", + "bitflags 2.6.0", + "cfg-if", "foreign-types", "libc", "once_cell", + "openssl-macros", "openssl-sys", ] +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "openssl-probe" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "111.20.0+1.1.1o" +version = "300.3.2+3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92892c4f87d56e376e469ace79f1128fdaded07646ddf73aa0be4706ff712dec" +checksum = "a211a18d945ef7e648cc6e0058f4c548ee46aab922ea203e0d30e966ea23647b" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.66" +version = "0.9.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1996d2d305e561b70d1ee0c53f1542833f4e1ac6ce9a6708b6ff2738ca67dc82" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ - "autocfg", "cc", "libc", "openssl-src", @@ -967,41 +1022,15 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - -[[package]] -name = "pin-project" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7509cc106041c40a4518d2af7a61530e1eed0e6285296a3d8c5472806ccc4a4" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c950132583b500556b1efd71d45b319029f2b71518d979fcc208e16b42426f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pin-project-lite" -version = "0.1.12" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project-lite" -version = "0.2.6" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -1011,95 +1040,77 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" - -[[package]] -name = "ppv-lite86" -version = "0.2.10" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] -name = "proc-macro-hack" -version = "0.5.19" +name = "potential_utf" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] [[package]] -name = "proc-macro-nested" -version = "0.1.7" +name = "powerfmt" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] -name = "proc-macro2" -version = "1.0.27" +name = "ppv-lite86" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "unicode-xid", + "zerocopy", ] [[package]] -name = "publicsuffix" -version = "1.5.6" +name = "proc-macro2" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b4ce31ff0a27d93c8de1849cf58162283752f065a90d508f1105fa6c9a213f" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" dependencies = [ - "idna", - "url", + "unicode-ident", ] [[package]] -name = "quick-error" -version = "1.2.3" +name = "psl-types" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" [[package]] -name = "quote" -version = "1.0.9" +name = "publicsuffix" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" dependencies = [ - "proc-macro2", + "idna", + "psl-types", ] [[package]] -name = "rand" -version = "0.7.3" +name = "quote" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc 0.2.0", + "proc-macro2", ] [[package]] name = "rand" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.3", - "rand_hc 0.3.1", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "rand_chacha", + "rand_core", ] [[package]] @@ -1109,173 +1120,183 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.3", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", + "rand_core", ] [[package]] name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom 0.2.3", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_hc" -version = "0.3.1" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "rand_core 0.6.3", -] - -[[package]] -name = "redox_syscall" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" -dependencies = [ - "bitflags", + "getrandom", ] [[package]] name = "regex" -version = "1.5.5" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", + "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.1.10" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi 0.3.9", -] +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.10.10" +version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0718f81a8e14c4dbb3b34cf23dc6aaf9ab8a0dfec160c534b3dbca1aaa21f47c" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ - "base64", - "bytes 0.5.6", + "base64 0.22.1", + "bytes", "cookie", "cookie_store", "encoding_rs", "futures-core", - "futures-util", + "h2", "http", "http-body", + "http-body-util", "hyper", + "hyper-rustls", "hyper-tls", - "ipnet", + "hyper-util", "js-sys", - "lazy_static", "log", "mime", - "mime_guess", "native-tls", "percent-encoding", - "pin-project-lite 0.2.6", + "pin-project-lite", + "rustls-pki-types", "serde", + "serde_json", "serde_urlencoded", - "time", + "sync_wrapper", "tokio", - "tokio-tls", - "trust-dns-resolver", + "tokio-native-tls", + "tower", + "tower-http", + "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg 0.7.0", ] [[package]] -name = "resolv-conf" -version = "0.7.0" +name = "ring" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ - "hostname", - "quick-error", + "cc", + "cfg-if", + "getrandom", + "libc", + "untrusted", + "windows-sys 0.52.0", ] [[package]] name = "rustc-demangle" -version = "0.1.20" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dead70b0b5e03e9c814bcb6b01e03e68f7c57a80aa48c72ec92152ab3e818d49" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] -name = "rustc_version" -version = "0.2.3" +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.23.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ - "semver", + "ring", + "rustls-pki-types", + "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "schannel" -version = "0.1.19" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" dependencies = [ - "lazy_static", - "winapi 0.3.9", + "windows-sys 0.59.0", ] [[package]] name = "security-framework" -version = "2.3.1" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -1284,43 +1305,28 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.3.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4effb91b4b8b6fb7732e670b6cee160278ff8e6bf485c7805d9e319d76e284" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" dependencies = [ "core-foundation-sys", "libc", ] -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "serde" -version = "1.0.126" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.126" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -1329,20 +1335,21 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.64" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] name = "serde_urlencoded" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", "itoa", @@ -1351,389 +1358,351 @@ dependencies = [ ] [[package]] -name = "sha1" -version = "0.6.0" +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "slab" -version = "0.4.3" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] [[package]] name = "smallvec" -version = "1.6.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.3.19" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ - "cfg-if 1.0.0", "libc", - "winapi 0.3.9", + "windows-sys 0.52.0", ] [[package]] -name = "standback" -version = "0.2.17" +name = "socket2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ - "version_check", + "libc", + "windows-sys 0.60.2", ] [[package]] -name = "stdweb" -version = "0.4.20" +name = "stable_deref_trait" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] -name = "stdweb-derive" -version = "0.5.3" +name = "strsim" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_derive", - "syn", -] +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] -name = "stdweb-internal-macros" -version = "0.2.9" +name = "syn" +version = "2.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" +checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021" dependencies = [ - "base-x", "proc-macro2", "quote", - "serde", - "serde_derive", - "serde_json", - "sha1", - "syn", + "unicode-ident", ] [[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" - -[[package]] -name = "strsim" -version = "0.8.0" +name = "sync_wrapper" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] [[package]] -name = "syn" -version = "1.0.73" +name = "synstructure" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "syn", ] [[package]] -name = "tempfile" -version = "3.2.0" +name = "system-configuration" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ - "cfg-if 1.0.0", - "libc", - "rand 0.8.4", - "redox_syscall", - "remove_dir_all", - "winapi 0.3.9", + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", ] [[package]] -name = "textwrap" -version = "0.11.0" +name = "system-configuration-sys" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" dependencies = [ - "unicode-width", + "core-foundation-sys", + "libc", ] [[package]] -name = "thiserror" -version = "1.0.25" +name = "tempfile" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ - "thiserror-impl", + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", ] [[package]] -name = "thiserror-impl" -version = "1.0.25" +name = "textwrap" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ - "proc-macro2", - "quote", - "syn", + "unicode-width", ] [[package]] name = "time" -version = "0.2.27" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ - "const_fn", - "libc", - "standback", - "stdweb", + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", "time-macros", - "version_check", - "winapi 0.3.9", ] [[package]] -name = "time-macros" -version = "0.1.1" +name = "time-core" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" -dependencies = [ - "proc-macro-hack", - "time-macros-impl", -] +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] -name = "time-macros-impl" -version = "0.1.2" +name = "time-macros" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "standback", - "syn", + "num-conv", + "time-core", ] [[package]] -name = "tinyvec" -version = "1.2.0" +name = "tinystr" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ - "tinyvec_macros", + "displaydoc", + "zerovec", ] -[[package]] -name = "tinyvec_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" - [[package]] name = "tokio" -version = "0.2.25" +version = "1.43.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092" +checksum = "492a604e2fd7f814268a378409e6c92b5525d747d10db9a229723f55a417958c" dependencies = [ - "bytes 0.5.6", - "fnv", - "futures-core", - "iovec", - "lazy_static", + "backtrace", + "bytes", "libc", - "memchr", "mio", - "mio-uds", - "num_cpus", - "pin-project-lite 0.1.12", - "slab", + "pin-project-lite", + "socket2 0.5.7", + "windows-sys 0.52.0", ] [[package]] -name = "tokio-tls" +name = "tokio-native-tls" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-util" -version = "0.3.1" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ - "bytes 0.5.6", + "bytes", "futures-core", "futures-sink", - "log", - "pin-project-lite 0.1.12", + "pin-project-lite", "tokio", ] [[package]] -name = "tower-service" -version = "0.3.1" +name = "tower" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" - -[[package]] -name = "tracing" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ - "cfg-if 1.0.0", - "log", - "pin-project-lite 0.2.6", - "tracing-core", + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", ] [[package]] -name = "tracing-core" -version = "0.1.18" +name = "tower-http" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "lazy_static", + "bitflags 2.6.0", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", ] [[package]] -name = "tracing-futures" -version = "0.2.5" +name = "tower-layer" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] -name = "trust-dns-proto" -version = "0.19.7" +name = "tower-service" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cad71a0c0d68ab9941d2fb6e82f8fb2e86d9945b94e1661dd0aaea2b88215a9" -dependencies = [ - "async-trait", - "backtrace", - "cfg-if 1.0.0", - "enum-as-inner", - "futures", - "idna", - "lazy_static", - "log", - "rand 0.7.3", - "smallvec", - "thiserror", - "tokio", - "url", -] +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] -name = "trust-dns-resolver" -version = "0.19.7" +name = "tracing" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "710f593b371175db53a26d0b38ed2978fafb9e9e8d3868b1acd753ea18df0ceb" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if 0.1.10", - "futures", - "ipconfig", - "lazy_static", - "log", - "lru-cache", - "resolv-conf", - "smallvec", - "thiserror", - "tokio", - "trust-dns-proto", + "pin-project-lite", + "tracing-core", ] [[package]] -name = "try-lock" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" - -[[package]] -name = "unicase" -version = "2.6.0" +name = "tracing-core" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ - "version_check", + "once_cell", ] [[package]] -name = "unicode-bidi" -version = "0.3.5" +name = "try-lock" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" -dependencies = [ - "matches", -] +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] -name = "unicode-normalization" -version = "0.1.19" +name = "unicode-ident" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" -dependencies = [ - "tinyvec", -] +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] -name = "unicode-segmentation" -version = "1.7.1" +name = "unicode-width" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] -name = "unicode-width" -version = "0.1.8" +name = "unsafe-libyaml" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] -name = "unicode-xid" -version = "0.2.2" +name = "untrusted" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.2.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", - "matches", "percent-encoding", ] +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "vcpkg" version = "0.2.15" @@ -1748,66 +1717,45 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] [[package]] name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.74" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ - "cfg-if 1.0.0", - "serde", - "serde_json", + "cfg-if", + "once_cell", + "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" -dependencies = [ - "bumpalo", - "lazy_static", - "log", - "proc-macro2", - "quote", - "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.24" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fba7978c679d53ce2d0ac80c8c175840feb849a161664365d1287b41f2e67f1" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "wasm-bindgen", "web-sys", @@ -1815,9 +1763,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.74" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1825,45 +1773,36 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.74" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.74" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +dependencies = [ + "unicode-ident", +] [[package]] name = "web-sys" -version = "0.3.51" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" dependencies = [ "js-sys", "wasm-bindgen", ] -[[package]] -name = "widestring" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c" - -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" - [[package]] name = "winapi" version = "0.3.9" @@ -1874,12 +1813,6 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" - [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -1893,38 +1826,368 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "winreg" +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "writeable" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "winapi 0.3.9", + "stable_deref_trait", + "yoke-derive", + "zerofrom", ] [[package]] -name = "winreg" -version = "0.7.0" +name = "yoke-derive" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ - "winapi 0.3.9", + "proc-macro2", + "quote", + "syn", + "synstructure", ] [[package]] -name = "ws2_32-sys" -version = "0.2.1" +name = "zerocopy" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "winapi 0.2.8", - "winapi-build", + "byteorder", + "zerocopy-derive", ] [[package]] -name = "yaml-rust" -version = "0.4.5" +name = "zerocopy-derive" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ - "linked-hash-map", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] diff --git a/Cargo.toml b/Cargo.toml index e3b6310..794dee7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,31 +1,32 @@ [package] name = "drill" -version = "0.7.2" +version = "0.9.0" authors = ["Ferran Basora "] description = "Drill is a HTTP load testing application written in Rust inspired by Ansible syntax" repository = "https://github.com/fcsonline/drill" keywords = ["performance", "http", "ansible", "jmeter"] license = "GPL-3.0" -edition = "2018" +edition = "2021" [dependencies] clap = "2.32.0" -colored = "1.7.0" +colored = "2.0.0" csv = "1.0.5" regex = "1.5.5" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.39" -yaml-rust = "0.4.3" +serde_yaml = "0.9" url = "2.1.1" linked-hash-map = "0.5.3" -tokio = { version = "0.2.20", features = ["rt-core", "rt-threaded", "time", "net", "io-driver"] } -reqwest = { version = "0.10.4", features = ["cookies", "trust-dns"] } +tokio = { version = "1.43.1", features = ["time", "net"] } +reqwest = { version = "0.12.28", features = ["cookies", "trust-dns"] } async-trait = "0.1.30" futures = "0.3.5" lazy_static = "1.4.0" num_cpus = "1.13.0" -rand = "0.7.3" +rand = "0.8.5" hdrhistogram = "7.4.0" +hex = "0.4.3" # Add openssl-sys as a direct dependency so it can be cross compiled to # x86_64-unknown-linux-musl using the "vendored" feature below @@ -35,3 +36,6 @@ openssl-sys = "0.9.66" # Force openssl-sys to statically link in the openssl library. Necessary when # cross compiling to x86_64-unknown-linux-musl. vendored = ["openssl-sys/vendored"] + +[dev-dependencies] +tempfile = "3.8" diff --git a/README.md b/README.md index f5d9ff2..d6d9f42 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,11 @@ plan: request: url: /api/users/{{ foo.body.manager_id }} + - name: Assert request response code + assert: + key: foo.status + value: 200 + - name: Assign values assign: key: bar @@ -66,6 +71,12 @@ plan: - 73 - 75 + - name: Tagged user request + request: + url: /api/users/70 + tags: + - tag_user + - name: Fetch some users by hash request: url: /api/users/{{ item.id }} @@ -97,7 +108,7 @@ plan: Content-Type: 'application/json' with_items_from_csv: file_name: ./fixtures/transactions.csv - quote_char: "\'" + quote_char: "\\'" - name: Fetch no relative url request: @@ -113,6 +124,20 @@ plan: method: POST body: foo=bar&arg={{ bar }} + - name: Support for hex-encoded request body + request: + url: /api/blob + method: POST + body: + hex: 65 78 61 6D 70 6C 65 + + - name: Support request body loaded from a file + request: + url: /api/blob + method: POST + body: + file: ./image.png + - name: Login user request: url: /login?user=example&password=3x4mpl3 @@ -148,6 +173,25 @@ plan: Authorization: Basic aHR0cHdhdGNoOmY= X-Foo: Bar X-Bar: Bar {{ memory.headers.token }} + + - name: One request with a random item + request: + url: /api/users/{{ item }} + with_items: + - 70 + - 73 + - 75 + shuffle: true + pick: 1 + + - name: Three requests with random items from a range + request: + url: /api/users/{{ item }} + with_items_range: + start: 1 + stop: 1000 + shuffle: true + pick: 3 ``` As you can see, you can play with interpolations in different ways. This @@ -158,7 +202,13 @@ If you want to know more about the benchmark file syntax, [read this](./SYNTAX.m ## Install -The easiest way right now is to install with [cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html): +Right now, the easiest way to get `drill` is to go to the +[latest release](https://github.com/fcsonline/drill/releases/latest) +page and download the binary file for your platform. + + +Another way to install `drill`, if you have [Rust](https://rustup.rs/) available in +your system, is with [cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html): ``` cargo install drill @@ -221,6 +271,7 @@ This is the list of all features supported by the current version of `drill`: - **Cookie support:** create benchmarks with sessions because cookies are propagates between requests. - **Stats:** get nice statistics about all the requests. Example: [cookies.yml](./example/cookies.yml) - **Thresholds:** compare the current benchmark performance against a stored one session and fail if a threshold is exceeded. +- **Tags:** specify test plan items by tags. ## Test it @@ -235,7 +286,7 @@ production environments. Full list of cli options, which is available under `drill --help` ``` -drill 0.7.1 +drill 0.9.0 HTTP load testing application written in Rust inspired by Ansible syntax USAGE: @@ -243,6 +294,8 @@ USAGE: FLAGS: -h, --help Prints help information + --list-tags List all benchmark tags + --list-tasks List benchmark tasks (executes --tags/--skip-tags filter) -n, --nanosec Shows statistics in nanoseconds --no-check-certificate Disables SSL certification check. (Not recommended) -q, --quiet Disables output @@ -255,6 +308,8 @@ OPTIONS: -b, --benchmark Sets the benchmark file -c, --compare Sets a compare file -r, --report Sets a report file + --skip-tags Tags to exclude + --tags Tags to include -t, --threshold Sets a threshold value in ms amongst the compared file -o, --timeout Set timeout in seconds for all requests ``` @@ -264,6 +319,12 @@ OPTIONS: - Complete and improve the interpolation engine - Add writing to a file support +## Donations + +If you appreciate all the job done in this project, a small donation is always welcome: + +[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/fcsonline) + ## Contribute This project started as a side project to learn Rust, so I'm sure that is full diff --git a/SYNTAX.md b/SYNTAX.md index 361068a..398092a 100644 --- a/SYNTAX.md +++ b/SYNTAX.md @@ -46,9 +46,12 @@ All those three items can be combined with `name` property to be show in logs. - `method`: HTTP method in the requests. Valid methods are GET, POST, PUT, PATCH, HEAD or DELETE. (default: GET) - `body`: Request body for methods like POST, PUT or PATCH. - `with_items`: List of items to be interpolated in the given request url. -- `with_items_range`: Generates items from an iterator from start, step, stop. +- `with_items_range`: Generates items from an iterator from start, step (optional, default: 1), stop. - `with_items_from_csv`: Read the given CSV values and go through all of them as items. -- `assign`: save the response in the context to be interpolated later. +- `shuffle`: Shuffle given items randomly (default: false). +- `pick`: Number of items to pick and perform requests with. +- `assign`: Save the response in the context to be interpolated later. +- `tags`: List of tags for that item. #### with_items_from_csv item properties @@ -58,3 +61,28 @@ Second, it can be a hash with the following properties: - `file_name`: csv file containing the records to be used as items - `quote_char`: character to use as quote in csv parsing. Defaults to `"\""`, but can be set to `"\'"`. If your csv file has quoted strings that contain commas and that causes parse errors, make sure this value is set correctly. + +#### body item properties + +The `body` property can be specified in different ways depending on the type of data you want to send in the request. Here are three variants: + +1. `body: "string with {{ templates }}"` + - This variant allows you to use a string with templates that can be interpolated with values from the context. + +2. `body: { hex: 65 78 61 6D 70 6C 65 }` + - This variant allows you to send a raw byte string value in the request body. + +3. `body: { file: path/to/file.txt }` + - This variant allows you to specify a file path, and the content of the file will be used as the request body. + +#### tags item properties + +[Ansible](https://docs.ansible.com/ansible/latest/user_guide/playbooks_tags.html#special-tags-always-and-never)-like tags. + +If you assign list of tags, e.g `[tag1, tag2]`, this item will be executed if `tag1` **OR** `tag2` is passed. + +Special tags: `always` and `never`. + +If you assign the `always` tag, `drill` will always run that item, unless you specifically skip it (`--skip-tags always`). + +If you assign the `never` tag to item, `drill` will skip that item unless you specifically request it (`--tags never`). diff --git a/example/benchmark.yml b/example/benchmark.yml index d5abce4..f9d5aa8 100644 --- a/example/benchmark.yml +++ b/example/benchmark.yml @@ -54,7 +54,14 @@ plan: - 73 - 75 + - name: Tagged user request + request: + url: /api/users/70 + tags: + - tag_user + - name: Fetch some users by hash + assign: fetchuser request: url: /api/users/{{ item.id }} shuffle: true @@ -63,6 +70,11 @@ plan: - { id: 73 } - { id: 75 } + - name: Assert request response code + assert: + key: fetchuser.status + value: 200 + - name: Assert values assert: key: bar @@ -97,7 +109,7 @@ plan: url: /api/users/contacts/{{ item.id }} with_items_from_csv: file_name: ./fixtures/users.csv - quote_char: "\'" + quote_char: "\\'" - name: POST some crafted JSONs stored in CSV request: @@ -108,7 +120,7 @@ plan: Content-Type: 'application/json' with_items_from_csv: file_name: ./fixtures/transactions.csv - quote_char: "\'" + quote_char: "\\'" - name: Fetch no relative url request: @@ -119,3 +131,23 @@ plan: url: /api/users method: POST body: foo=bar&arg={{ bar }} + + - name: One request with a random item + request: + url: /api/users/{{ item }} + with_items: + - 70 + - 73 + - 75 + shuffle: true + pick: 1 + + - name: Complex access + request: + url: /api/users.json + assign: complex + + - name: Assert request response code + assert: + key: complex.body[1].phones[1] + value: '+44 2345678' diff --git a/example/server/Dockerfile-example-server b/example/server/Dockerfile-example-server new file mode 100644 index 0000000..bfeefe6 --- /dev/null +++ b/example/server/Dockerfile-example-server @@ -0,0 +1,6 @@ +FROM node +WORKDIR /app +COPY package.json ./ +RUN npm install +COPY . . +CMD node server.js diff --git a/example/server/docker-compose.yml b/example/server/docker-compose.yml new file mode 100644 index 0000000..b0a6041 --- /dev/null +++ b/example/server/docker-compose.yml @@ -0,0 +1,14 @@ +version: "3.9" +services: + drill-server: + ports: + - "9000:9000" + image: drill-example-server:latest + build: + context: ./ + dockerfile: Dockerfile-example-server + #args: + # buildno: 1 + environment: + OUTPUT: 0 + DELAY_MS: 100 diff --git a/example/server/package-lock.json b/example/server/package-lock.json index 77cb44a..2e56901 100644 --- a/example/server/package-lock.json +++ b/example/server/package-lock.json @@ -1,410 +1,880 @@ { "name": "server", "version": "1.0.0", - "lockfileVersion": 1, + "lockfileVersion": 3, "requires": true, - "dependencies": { - "accepts": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", - "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", - "requires": { - "mime-types": "~2.1.16", - "negotiator": "0.6.1" + "packages": { + "": { + "name": "server", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "cookie-parser": "^1.4.3", + "express": "^4.16.2", + "express-session": "^1.15.6" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" } }, - "array-flatten": { + "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, - "body-parser": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", - "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", - "requires": { - "bytes": "3.0.0", - "content-type": "~1.0.4", + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", "debug": "2.6.9", - "depd": "~1.1.1", - "http-errors": "~1.6.2", - "iconv-lite": "0.4.19", - "on-finished": "~2.3.0", - "qs": "6.5.1", - "raw-body": "2.3.2", - "type-is": "~1.6.15" + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } }, - "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } }, - "content-type": { + "node_modules/call-bound": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=" - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" - }, - "cookie-parser": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.3.tgz", - "integrity": "sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU=", - "requires": { - "cookie": "0.3.1", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" } }, - "cookie-signature": { + "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, - "crc": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.4.4.tgz", - "integrity": "sha1-naHpgOO9RPxck79as9ozeNheRms=" - }, - "debug": { + "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", - "requires": { + "dependencies": { "ms": "2.0.0" } }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } }, - "ee-first": { + "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } }, - "escape-html": { + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" }, - "etag": { + "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "express": { - "version": "4.16.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", - "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=", - "requires": { - "accepts": "~1.3.4", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.18.2", - "content-disposition": "0.5.2", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", "content-type": "~1.0.4", - "cookie": "0.3.1", - "cookie-signature": "1.0.6", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", "debug": "2.6.9", - "depd": "~1.1.1", - "encodeurl": "~1.0.1", + "depd": "2.0.0", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.1.0", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.2", - "qs": "6.5.1", - "range-parser": "~1.2.0", - "safe-buffer": "5.1.1", - "send": "0.16.1", - "serve-static": "1.13.1", - "setprototypeof": "1.1.0", - "statuses": "~1.3.1", - "type-is": "~1.6.15", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "express-session": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.15.6.tgz", - "integrity": "sha1-R7QWDIj0KrcP6KUI4xy/92dXqwo=", - "requires": { - "cookie": "0.3.1", - "cookie-signature": "1.0.6", - "crc": "3.4.4", + "node_modules/express-session": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.2.tgz", + "integrity": "sha512-SZjssGQC7TzTs9rpPDuUrR23GNZ9+2+IkA/+IJWmvQilTr5OSliEHGF+D9scbIpdC6yGtTI0/VhaHoVes2AN/A==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.7", "debug": "2.6.9", - "depd": "~1.1.1", - "on-headers": "~1.0.1", - "parseurl": "~1.3.2", - "uid-safe": "~2.1.5", - "utils-merge": "1.0.1" + "depd": "~2.0.0", + "on-headers": "~1.1.0", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "engines": { + "node": ">= 0.8.0" } }, - "finalhandler": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", - "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", - "requires": { + "node_modules/express-session/node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.1", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.3.1", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } }, - "fresh": { + "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "http-errors": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", - "requires": { - "depd": "1.1.1", - "inherits": "2.0.3", - "setprototypeof": "1.0.3", - "statuses": ">= 1.3.1 < 2" + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", "dependencies": { - "depd": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" - }, - "setprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", - "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" - } + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } }, - "ipaddr.js": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", - "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=" + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } }, - "media-typer": { + "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "methods": { + "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } }, - "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha1-Eh+evEnjdm8xGnbh+hyAA8SwOqY=" + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } }, - "mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } }, - "mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", - "requires": { - "mime-db": "~1.33.0" + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" } }, - "ms": { + "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" } }, - "on-headers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", - "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=" + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } }, - "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" }, - "proxy-addr": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", - "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.6.0" + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" } }, - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "random-bytes": { + "node_modules/random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", - "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=", + "engines": { + "node": ">= 0.8" + } }, - "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" - }, - "raw-body": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", - "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.2", - "iconv-lite": "0.4.19", - "unpipe": "1.0.0" - } - }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" - }, - "send": { - "version": "0.16.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", - "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==", - "requires": { + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { "debug": "2.6.9", - "depd": "~1.1.1", - "destroy": "~1.0.4", - "encodeurl": "~1.0.1", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.3.1" - } - }, - "serve-static": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", - "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==", - "requires": { - "encodeurl": "~1.0.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", - "parseurl": "~1.3.2", - "send": "0.16.1" + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "setprototypeof": { + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha1-0L2FU2iHtv58DYGMuWLZ2RxU5lY=" - }, - "statuses": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" - }, - "type-is": { - "version": "1.6.16", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha1-+JzjQVQcZysl7nrjxz3uOyvlAZQ=", - "requires": { + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { "media-typer": "0.3.0", - "mime-types": "~2.1.18" + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" } }, - "uid-safe": { + "node_modules/uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", "integrity": "sha1-Kz1cckDo/C5Y+Komnl7knAhXvTo=", - "requires": { + "dependencies": { "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "unpipe": { + "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } }, - "utils-merge": { + "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "engines": { + "node": ">= 0.4.0" + } }, - "vary": { + "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } } } } diff --git a/example/server/server.js b/example/server/server.js index e58c5d3..a1be21e 100644 --- a/example/server/server.js +++ b/example/server/server.js @@ -50,7 +50,7 @@ const randomFailedHandler = function(req, res) { } }; -const transactionsHander = function(req, res) { +const transactionsHandler = function(req, res) { const body = req.body if (body.a + body.b === '123') { @@ -72,7 +72,7 @@ app.get('/api/users/at/:floor/:room', logger_handler('R')); app.get('/api/account', logger_handler('A')); app.post('/api/users', randomFailedHandler); -app.post('/api/transactions', transactionsHander); +app.post('/api/transactions', transactionsHandler); // Sessions test plan app.get('/login', function(req, res){ diff --git a/example/subtags.yml b/example/subtags.yml new file mode 100644 index 0000000..64685aa --- /dev/null +++ b/example/subtags.yml @@ -0,0 +1,9 @@ +# Example of a included file + +--- +- name: Fetch comments + request: + url: /api/comments.json + tags: + - tag_user + diff --git a/example/tags.yml b/example/tags.yml new file mode 100644 index 0000000..ca5b714 --- /dev/null +++ b/example/tags.yml @@ -0,0 +1,14 @@ +--- +base: 'http://localhost:9000' +iterations: 1 +concurrency: 1 + +plan: + - name: Include comments + include: subtags.yml + + - name: Tagged user request + request: + url: /api/users/70 + tags: + - tag_user diff --git a/src/actions/assert.rs b/src/actions/assert.rs index 57d8062..7cb3032 100644 --- a/src/actions/assert.rs +++ b/src/actions/assert.rs @@ -1,7 +1,7 @@ use async_trait::async_trait; use colored::*; use serde_json::json; -use yaml_rust::Yaml; +use serde_yaml::Value; use crate::actions::extract; use crate::actions::Runnable; @@ -17,19 +17,20 @@ pub struct Assert { } impl Assert { - pub fn is_that_you(item: &Yaml) -> bool { - item["assert"].as_hash().is_some() + pub fn is_that_you(item: &Value) -> bool { + item.get("assert").and_then(|v| v.as_mapping()).is_some() } - pub fn new(item: &Yaml, _with_item: Option) -> Assert { + pub fn new(item: &Value, _with_item: Option) -> Assert { let name = extract(item, "name"); - let key = extract(&item["assert"], "key"); - let value = extract(&item["assert"], "value"); + let assert_val = item.get("assert").expect("assert field is required"); + let key = extract(assert_val, "key"); + let value = extract(assert_val, "value"); Assert { - name: name.to_string(), - key: key.to_string(), - value: value.to_string(), + name, + key, + value, } } } @@ -47,7 +48,7 @@ impl Runnable for Assert { let assertion = json!(self.value.to_owned()); if !stored.eq(&assertion) { - panic!("Assertion mismatched: {} != {}", stored, assertion); + panic!("Assertion mismatched: {stored} != {assertion}"); } } } diff --git a/src/actions/assign.rs b/src/actions/assign.rs index 2486684..ea7c458 100644 --- a/src/actions/assign.rs +++ b/src/actions/assign.rs @@ -1,7 +1,7 @@ use async_trait::async_trait; use colored::*; use serde_json::json; -use yaml_rust::Yaml; +use serde_yaml::Value; use crate::actions::extract; use crate::actions::Runnable; @@ -16,19 +16,20 @@ pub struct Assign { } impl Assign { - pub fn is_that_you(item: &Yaml) -> bool { - item["assign"].as_hash().is_some() + pub fn is_that_you(item: &Value) -> bool { + item.get("assign").and_then(|v| v.as_mapping()).is_some() } - pub fn new(item: &Yaml, _with_item: Option) -> Assign { + pub fn new(item: &Value, _with_item: Option) -> Assign { let name = extract(item, "name"); - let key = extract(&item["assign"], "key"); - let value = extract(&item["assign"], "value"); + let assign_val = item.get("assign").expect("assign field is required"); + let key = extract(assign_val, "key"); + let value = extract(assign_val, "value"); Assign { - name: name.to_string(), - key: key.to_string(), - value: value.to_string(), + name, + key, + value, } } } diff --git a/src/actions/delay.rs b/src/actions/delay.rs index d53e99b..aa2d9c9 100644 --- a/src/actions/delay.rs +++ b/src/actions/delay.rs @@ -1,7 +1,7 @@ use async_trait::async_trait; use colored::*; -use tokio::time::delay_for; -use yaml_rust::Yaml; +use serde_yaml::Value; +use tokio::time::sleep; use crate::actions::extract; use crate::actions::Runnable; @@ -18,16 +18,17 @@ pub struct Delay { } impl Delay { - pub fn is_that_you(item: &Yaml) -> bool { - item["delay"].as_hash().is_some() + pub fn is_that_you(item: &Value) -> bool { + item.get("delay").and_then(|v| v.as_mapping()).is_some() } - pub fn new(item: &Yaml, _with_item: Option) -> Delay { + pub fn new(item: &Value, _with_item: Option) -> Delay { let name = extract(item, "name"); - let seconds = u64::try_from(item["delay"]["seconds"].as_i64().unwrap()).expect("Invalid number of seconds"); + let delay_val = item.get("delay").expect("delay field is required"); + let seconds = u64::try_from(delay_val.get("seconds").and_then(|v| v.as_i64()).expect("Invalid number of seconds")).expect("Invalid number of seconds"); Delay { - name: name.to_string(), + name, seconds, } } @@ -36,7 +37,7 @@ impl Delay { #[async_trait] impl Runnable for Delay { async fn execute(&self, _context: &mut Context, _reports: &mut Reports, _pool: &Pool, config: &Config) { - delay_for(Duration::from_secs(self.seconds as u64)).await; + sleep(Duration::from_secs(self.seconds)).await; if !config.quiet { println!("{:width$} {}{}", self.name.green(), self.seconds.to_string().cyan().bold(), "s".magenta(), width = 25); diff --git a/src/actions/exec.rs b/src/actions/exec.rs index c4df3b2..c3f25bf 100644 --- a/src/actions/exec.rs +++ b/src/actions/exec.rs @@ -1,8 +1,8 @@ use async_trait::async_trait; use colored::*; use serde_json::json; +use serde_yaml::Value; use std::process::Command; -use yaml_rust::Yaml; use crate::actions::Runnable; use crate::actions::{extract, extract_optional}; @@ -18,19 +18,20 @@ pub struct Exec { } impl Exec { - pub fn is_that_you(item: &Yaml) -> bool { - item["exec"].as_hash().is_some() + pub fn is_that_you(item: &Value) -> bool { + item.get("exec").and_then(|v| v.as_mapping()).is_some() } - pub fn new(item: &Yaml, _with_item: Option) -> Exec { + pub fn new(item: &Value, _with_item: Option) -> Exec { let name = extract(item, "name"); - let command = extract(&item["exec"], "command"); + let exec_val = item.get("exec").expect("exec field is required"); + let command = extract(exec_val, "command"); let assign = extract_optional(item, "assign"); Exec { - name: name.to_string(), - command: command.to_string(), - assign: assign.map(str::to_string), + name, + command, + assign, } } } @@ -44,12 +45,12 @@ impl Runnable for Exec { let final_command = interpolator::Interpolator::new(context).resolve(&self.command, !config.relaxed_interpolations); - let args = vec!["bash", "-c", "--", final_command.as_str()]; + let args = ["bash", "-c", "--", final_command.as_str()]; let execution = Command::new(args[0]).args(&args[1..]).output().expect("Couldn't run it"); let output: String = String::from_utf8_lossy(&execution.stdout).into(); - let output = output.trim_end().to_string().to_owned(); + let output = output.trim_end().to_string(); if let Some(ref key) = self.assign { context.insert(key.to_owned(), json!(output)); diff --git a/src/actions/mod.rs b/src/actions/mod.rs index 8c7a50e..e3c142c 100644 --- a/src/actions/mod.rs +++ b/src/actions/mod.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use yaml_rust::Yaml; +use serde_yaml::Value; mod assert; mod assign; @@ -42,26 +42,24 @@ impl fmt::Display for Report { } } -pub fn extract_optional<'a>(item: &'a Yaml, attr: &'a str) -> Option<&'a str> { - if let Some(s) = item[attr].as_str() { - Some(s) +pub fn extract_optional<'a>(item: &'a Value, attr: &'a str) -> Option { + if let Some(s) = item.get(attr).and_then(|v| v.as_str()) { + Some(s.to_string()) + } else if item.get(attr).and_then(|v| v.as_mapping()).is_some() { + panic!("`{attr}` needs to be a string. Try adding quotes"); } else { - if item[attr].as_hash().is_some() { - panic!("`{}` needs to be a string. Try adding quotes", attr); - } else { - None - } + None } } -pub fn extract<'a>(item: &'a Yaml, attr: &'a str) -> &'a str { - if let Some(s) = item[attr].as_str() { - s +pub fn extract<'a>(item: &'a Value, attr: &'a str) -> String { + if let Some(s) = item.get(attr).and_then(|v| v.as_i64()) { + s.to_string() + } else if let Some(s) = item.get(attr).and_then(|v| v.as_str()) { + s.to_string() + } else if item.get(attr).and_then(|v| v.as_mapping()).is_some() { + panic!("`{attr}` is required needs to be a string. Try adding quotes"); } else { - if item[attr].as_hash().is_some() { - panic!("`{}` is required needs to be a string. Try adding quotes", attr); - } else { - panic!("Unknown node `{}` => {:?}", attr, item[attr]); - } + panic!("Unknown node `{}` => {:?}", attr, item.get(attr)); } } diff --git a/src/actions/request.rs b/src/actions/request.rs index 8151fda..7200990 100644 --- a/src/actions/request.rs +++ b/src/actions/request.rs @@ -7,9 +7,11 @@ use reqwest::{ header::{self, HeaderMap, HeaderName, HeaderValue}, ClientBuilder, Method, Response, }; +use serde_yaml::Value as YamlValue; use std::fmt::Write; +use std::fs::File; +use std::io::Read; use url::Url; -use yaml_rust::Yaml; use serde::{Deserialize, Serialize}; use serde_json::{json, Map, Value}; @@ -24,53 +26,77 @@ use crate::actions::{Report, Runnable}; static USER_AGENT: &str = "drill"; #[derive(Clone)] +pub enum Body { + Template(String), + Binary(Vec), +} + +#[derive(Clone)] +#[allow(dead_code)] pub struct Request { name: String, url: String, time: f64, method: String, headers: HashMap, - pub body: Option, - pub with_item: Option, + pub body: Option, + pub with_item: Option, pub index: Option, pub assign: Option, } #[derive(Serialize, Deserialize)] struct AssignedRequest { + status: u16, body: Value, headers: Map, } impl Request { - pub fn is_that_you(item: &Yaml) -> bool { - item["request"].as_hash().is_some() + pub fn is_that_you(item: &YamlValue) -> bool { + item.get("request").and_then(|v| v.as_mapping()).is_some() } - pub fn new(item: &Yaml, with_item: Option, index: Option) -> Request { + pub fn new(item: &YamlValue, with_item: Option, index: Option) -> Request { let name = extract(item, "name"); - let url = extract(&item["request"], "url"); + let request_val = item.get("request").expect("request field is required"); + let url = extract(request_val, "url"); let assign = extract_optional(item, "assign"); - let method = if let Some(v) = extract_optional(&item["request"], "method") { - v.to_string().to_uppercase() + let method = if let Some(v) = extract_optional(request_val, "method") { + v.to_uppercase() } else { "GET".to_string() }; - let body_verbs = vec!["POST", "PATCH", "PUT"]; + let body_verbs = ["POST", "PATCH", "PUT"]; let body = if body_verbs.contains(&method.as_str()) { - Some(extract(&item["request"], "body")) + if let Some(body) = request_val.get("body").and_then(|v| v.as_str()) { + Some(Body::Template(body.to_string())) + } else if let Some(file_path) = request_val.get("body").and_then(|v| v.get("file")).and_then(|v| v.as_str()) { + let mut file = File::open(file_path).expect("Unable to open file"); + let mut buffer = Vec::new(); + file.read_to_end(&mut buffer).expect("Unable to read file"); + Some(Body::Binary(buffer)) + } else if let Some(hex_str) = request_val.get("body").and_then(|v| v.get("hex")).and_then(|v| v.as_str()) { + Some(Body::Binary(hex::decode(hex_str).expect("Invalid hex string"))) + } else { + panic!("{} Body must be string, file or hex!!", "WARNING!".yellow().bold()); + } } else { None }; let mut headers = HashMap::new(); - if let Some(hash) = item["request"]["headers"].as_hash() { - for (key, val) in hash.iter() { + if let Some(mapping) = request_val.get("headers").and_then(|v| v.as_mapping()) { + for (key, val) in mapping.iter() { if let Some(vs) = val.as_str() { - headers.insert(key.as_str().unwrap().to_string(), vs.to_string()); + if let Some(key_str) = key.as_str() { + headers.insert(key_str.to_string(), vs.to_string()); + } else { + panic!("{} Header keys must be strings!!", "WARNING!".yellow().bold()); + } } else { panic!("{} Headers must be strings!!", "WARNING!".yellow().bold()); } @@ -78,15 +104,15 @@ impl Request { } Request { - name: name.to_string(), - url: url.to_string(), + name, + url, time: 0.0, method, headers, - body: body.map(str::to_string), + body, with_item, index, - assign: assign.map(str::to_string), + assign, } } @@ -120,7 +146,7 @@ impl Request { match context.get("base") { Some(value) => { if let Some(vs) = value.as_str() { - format!("{}{}", vs.to_string(), interpolated_url) + format!("{vs}{interpolated_url}") } else { panic!("{} Wrong type 'base' variable!", "WARNING!".yellow().bold()); } @@ -154,12 +180,13 @@ impl Request { let mut pool2 = pool.lock().unwrap(); let client = pool2.entry(domain).or_insert_with(|| ClientBuilder::default().danger_accept_invalid_certs(config.no_check_certificate).build().unwrap()); - let request = if let Some(body) = self.body.as_ref() { - interpolated_body = uninterpolator.get_or_insert(interpolator::Interpolator::new(context)).resolve(body, !config.relaxed_interpolations); - - client.request(method, interpolated_base_url.as_str()).body(interpolated_body) - } else { - client.request(method, interpolated_base_url.as_str()) + let request = match self.body.as_ref() { + Some(Body::Template(template_body)) => { + interpolated_body = uninterpolator.get_or_insert(interpolator::Interpolator::new(context)).resolve(template_body, !config.relaxed_interpolations); + client.request(method, interpolated_base_url.as_str()).body(interpolated_body) + } + Some(Body::Binary(binary_body)) => client.request(method, interpolated_base_url.as_str()).body(binary_body.clone()), + None => client.request(method, interpolated_base_url.as_str()), }; (client.clone(), request) @@ -171,7 +198,7 @@ impl Request { if let Some(cookies) = context.get("cookies") { let cookies: Map = serde_json::from_value(cookies.clone()).unwrap(); - let cookie = cookies.iter().map(|(key, value)| format!("{}={}", key, value)).collect::>().join(";"); + let cookie = cookies.iter().map(|(key, value)| format!("{key}={value}")).collect::>().join(";"); headers.insert(header::COOKIE, HeaderValue::from_str(&cookie).unwrap()); } @@ -220,31 +247,38 @@ impl Request { } } -fn yaml_to_json(data: Yaml) -> Value { - if let Some(b) = data.as_bool() { - json!(b) - } else if let Some(i) = data.as_i64() { - json!(i) - } else if let Some(s) = data.as_str() { - json!(s) - } else if let Some(h) = data.as_hash() { - let mut map = Map::new(); - - for (key, value) in h.iter() { - map.entry(key.as_str().unwrap()).or_insert(yaml_to_json(value.clone())); +fn yaml_to_json(data: YamlValue) -> Value { + match data { + YamlValue::Bool(b) => json!(b), + YamlValue::Number(n) => { + if let Some(i) = n.as_i64() { + json!(i) + } else if let Some(f) = n.as_f64() { + json!(f) + } else { + // Fallback: convert to string representation + json!(n.to_string()) + } } - - json!(map) - } else if let Some(v) = data.as_vec() { - let mut array = Vec::new(); - - for value in v.iter() { - array.push(yaml_to_json(value.clone())); + YamlValue::String(s) => json!(s), + YamlValue::Mapping(m) => { + let mut map = Map::new(); + for (key, value) in m.iter() { + if let Some(key_str) = key.as_str() { + map.insert(key_str.to_string(), yaml_to_json(value.clone())); + } + } + json!(map) } - - json!(array) - } else { - panic!("Unknown Yaml node") + YamlValue::Sequence(v) => { + let mut array = Vec::new(); + for value in v.iter() { + array.push(yaml_to_json(value.clone())); + } + json!(array) + } + YamlValue::Null => json!(null), + _ => panic!("Unknown Yaml node"), } } @@ -256,7 +290,7 @@ impl Runnable for Request { } if self.index.is_some() { - context.insert("index".to_string(), json!(self.index.clone().unwrap())); + context.insert("index".to_string(), json!(self.index.unwrap())); } let (res, duration_ms) = self.send_request(context, pool, config).await; @@ -274,20 +308,17 @@ impl Runnable for Request { status: 520u16, }), Some(response) => { + let status = response.status().as_u16(); + reports.push(Report { name: self.name.to_owned(), duration: duration_ms, - status: response.status().as_u16(), + status, }); - if response.cookies().count() > 0 { - let mut cookies = Map::new(); - - for cookie in response.cookies() { - cookies.insert(cookie.name().to_string(), json!(cookie.value().to_string())); - } - - context.insert("cookies".to_string(), json!(cookies)); + for cookie in response.cookies() { + let cookies = context.entry("cookies").or_insert_with(|| json!({})).as_object_mut().unwrap(); + cookies.insert(cookie.name().to_string(), json!(cookie.value().to_string())); } let data = if let Some(ref key) = self.assign { @@ -302,6 +333,7 @@ impl Runnable for Request { let body: Value = serde_json::from_str(&data).unwrap_or(serde_json::Value::Null); let assigned = AssignedRequest { + status, body, headers, }; @@ -315,7 +347,9 @@ impl Runnable for Request { None }; - log_message_response.map(|msg| log_response(msg, &data)); + if let Some(msg) = log_message_response { + log_response(msg, &data) + } } } } @@ -324,19 +358,19 @@ impl Runnable for Request { fn log_request(request: &reqwest::Request) { let mut message = String::new(); write!(message, "{}", ">>>".bold().green()).unwrap(); - write!(message, " {} {},", "URL:".bold(), request.url().to_string()).unwrap(); - write!(message, " {} {},", "METHOD:".bold(), request.method().to_string()).unwrap(); - write!(message, " {} {}", "HEADERS:".bold(), format!("{:?}", request.headers())).unwrap(); - println!("{}", message); + write!(message, " {} {},", "URL:".bold(), request.url()).unwrap(); + write!(message, " {} {},", "METHOD:".bold(), request.method()).unwrap(); + write!(message, " {} {:?}", "HEADERS:".bold(), request.headers()).unwrap(); + println!("{message}"); } fn log_message_response(response: &Option, duration_ms: f64) -> String { let mut message = String::new(); match response { Some(response) => { - write!(message, " {} {},", "URL:".bold(), response.url().to_string()).unwrap(); + write!(message, " {} {},", "URL:".bold(), response.url()).unwrap(); write!(message, " {} {},", "STATUS:".bold(), response.status()).unwrap(); - write!(message, " {} {}", "HEADERS:".bold(), format!("{:?}", response.headers())).unwrap(); + write!(message, " {} {:?}", "HEADERS:".bold(), response.headers()).unwrap(); write!(message, " {} {:.4} ms,", "DURATION:".bold(), duration_ms).unwrap(); } None => { @@ -349,6 +383,321 @@ fn log_message_response(response: &Option, duration_ms: f64) fn log_response(log_message_response: String, body: &Option) { let mut message = String::new(); write!(message, "{}{}", "<<<".bold().green(), log_message_response).unwrap(); - body.as_ref().map(|body| write!(message, " {} {:?}", "BODY:".bold(), body).unwrap()); - println!("{}", message); + if let Some(body) = body.as_ref() { + write!(message, " {} {:?}", "BODY:".bold(), body).unwrap() + } + println!("{message}"); +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_yaml::Value as YamlValue; + use std::io::Write; + use tempfile::NamedTempFile; + + fn create_yaml_request_with_string_body(body_content: &str) -> YamlValue { + let yaml_str = format!( + r#" +name: test_request +request: + url: http://example.com + method: POST + body: "{}" +"#, + body_content + ); + serde_yaml::from_str(&yaml_str).unwrap() + } + + fn create_yaml_request_with_hex_body(hex_content: &str) -> YamlValue { + let yaml_str = format!( + r#" +name: test_request +request: + url: http://example.com + method: POST + body: + hex: "{}" +"#, + hex_content + ); + serde_yaml::from_str(&yaml_str).unwrap() + } + + fn create_yaml_request_with_file_body(file_path: &str) -> YamlValue { + let yaml_str = format!( + r#" +name: test_request +request: + url: http://example.com + method: POST + body: + file: "{}" +"#, + file_path + ); + serde_yaml::from_str(&yaml_str).unwrap() + } + + #[test] + fn test_body_template_string() { + let yaml = create_yaml_request_with_string_body("Hello, World!"); + let request = Request::new(&yaml, None, None); + + match request.body { + Some(Body::Template(content)) => { + assert_eq!(content, "Hello, World!"); + } + _ => panic!("Expected Body::Template"), + } + } + + #[test] + fn test_body_hex() { + // "Hello" in hex is "48656c6c6f" + let yaml = create_yaml_request_with_hex_body("48656c6c6f"); + let request = Request::new(&yaml, None, None); + + match request.body { + Some(Body::Binary(data)) => { + assert_eq!(data, b"Hello"); + } + _ => panic!("Expected Body::Binary"), + } + } + + #[test] + fn test_body_hex_empty() { + let yaml = create_yaml_request_with_hex_body(""); + let request = Request::new(&yaml, None, None); + + match request.body { + Some(Body::Binary(data)) => { + assert_eq!(data, b""); + } + _ => panic!("Expected Body::Binary with empty data"), + } + } + + #[test] + fn test_body_hex_complex() { + // "Hello, World!" in hex + let yaml = create_yaml_request_with_hex_body("48656c6c6f2c20576f726c6421"); + let request = Request::new(&yaml, None, None); + + match request.body { + Some(Body::Binary(data)) => { + assert_eq!(data, b"Hello, World!"); + } + _ => panic!("Expected Body::Binary"), + } + } + + #[test] + fn test_body_file() { + // Create a temporary file with test content + let mut temp_file = NamedTempFile::new().unwrap(); + let test_content = b"Test file content"; + temp_file.write_all(test_content).unwrap(); + temp_file.flush().unwrap(); + + let file_path = temp_file.path().to_str().unwrap(); + let yaml = create_yaml_request_with_file_body(file_path); + + let request = Request::new(&yaml, None, None); + + match request.body { + Some(Body::Binary(data)) => { + assert_eq!(data, test_content); + } + _ => panic!("Expected Body::Binary"), + } + } + + #[test] + fn test_body_file_empty() { + // Create an empty temporary file + let temp_file = NamedTempFile::new().unwrap(); + let file_path = temp_file.path().to_str().unwrap(); + + let yaml = create_yaml_request_with_file_body(file_path); + + let request = Request::new(&yaml, None, None); + + match request.body { + Some(Body::Binary(data)) => { + assert_eq!(data, b""); + } + _ => panic!("Expected Body::Binary with empty data"), + } + } + + #[test] + fn test_body_file_binary_data() { + // Create a file with binary data (not UTF-8) + let mut temp_file = NamedTempFile::new().unwrap(); + let binary_content = vec![0x00, 0x01, 0x02, 0xFF, 0xFE, 0xFD]; + temp_file.write_all(&binary_content).unwrap(); + temp_file.flush().unwrap(); + + let file_path = temp_file.path().to_str().unwrap(); + let yaml = create_yaml_request_with_file_body(file_path); + + let request = Request::new(&yaml, None, None); + + match request.body { + Some(Body::Binary(data)) => { + assert_eq!(data, binary_content); + } + _ => panic!("Expected Body::Binary"), + } + } + + #[test] + fn test_body_file_large_content() { + // Create a file with larger content + let mut temp_file = NamedTempFile::new().unwrap(); + let large_content: Vec = (0..10000).map(|i| (i % 256) as u8).collect(); + temp_file.write_all(&large_content).unwrap(); + temp_file.flush().unwrap(); + + let file_path = temp_file.path().to_str().unwrap(); + let yaml = create_yaml_request_with_file_body(file_path); + + let request = Request::new(&yaml, None, None); + + match request.body { + Some(Body::Binary(data)) => { + assert_eq!(data.len(), 10000); + assert_eq!(data, large_content); + } + _ => panic!("Expected Body::Binary"), + } + } + + #[test] + fn test_body_none_for_get() { + let yaml_str = r#" +name: test_request +request: + url: http://example.com + method: GET +"#; + let yaml: YamlValue = serde_yaml::from_str(yaml_str).unwrap(); + let request = Request::new(&yaml, None, None); + + assert!(request.body.is_none()); + } + + #[test] + fn test_body_none_for_delete() { + let yaml_str = r#" +name: test_request +request: + url: http://example.com + method: DELETE +"#; + let yaml: YamlValue = serde_yaml::from_str(yaml_str).unwrap(); + let request = Request::new(&yaml, None, None); + + assert!(request.body.is_none()); + } + + #[test] + fn test_body_hex_uppercase() { + // Test that hex decoding works with uppercase letters + let yaml = create_yaml_request_with_hex_body("48656C6C6F"); + let request = Request::new(&yaml, None, None); + + match request.body { + Some(Body::Binary(data)) => { + assert_eq!(data, b"Hello"); + } + _ => panic!("Expected Body::Binary"), + } + } + + #[test] + fn test_body_hex_mixed_case() { + // Test that hex decoding works with mixed case + let yaml = create_yaml_request_with_hex_body("48656c6C6F"); + let request = Request::new(&yaml, None, None); + + match request.body { + Some(Body::Binary(data)) => { + assert_eq!(data, b"Hello"); + } + _ => panic!("Expected Body::Binary"), + } + } + + #[test] + #[should_panic(expected = "Invalid hex string")] + fn test_body_hex_invalid() { + let yaml = create_yaml_request_with_hex_body("InvalidHexString!"); + Request::new(&yaml, None, None); + } + + #[test] + #[should_panic(expected = "Unable to open file")] + fn test_body_file_not_found() { + let yaml = create_yaml_request_with_file_body("/nonexistent/path/to/file.txt"); + Request::new(&yaml, None, None); + } + + #[test] + fn test_body_priority_string_over_hex() { + // When body is a string, it should be treated as Template, not hex + let yaml = create_yaml_request_with_string_body("48656c6c6f"); + let request = Request::new(&yaml, None, None); + + match request.body { + Some(Body::Template(content)) => { + assert_eq!(content, "48656c6c6f"); + } + _ => panic!("Expected Body::Template when body is a string"), + } + } + + #[test] + fn test_body_put_method() { + let yaml_str = r#" +name: test_request +request: + url: http://example.com + method: PUT + body: "PUT body content" +"#; + let yaml: YamlValue = serde_yaml::from_str(yaml_str).unwrap(); + let request = Request::new(&yaml, None, None); + + match request.body { + Some(Body::Template(content)) => { + assert_eq!(content, "PUT body content"); + } + _ => panic!("Expected Body::Template"), + } + } + + #[test] + fn test_body_patch_method() { + let yaml_str = r#" +name: test_request +request: + url: http://example.com + method: PATCH + body: + hex: "5061746368" +"#; + let yaml: YamlValue = serde_yaml::from_str(yaml_str).unwrap(); + let request = Request::new(&yaml, None, None); + + match request.body { + Some(Body::Binary(data)) => { + assert_eq!(data, b"Patch"); + } + _ => panic!("Expected Body::Binary"), + } + } } diff --git a/src/benchmark.rs b/src/benchmark.rs index 94f6fee..368f4fa 100644 --- a/src/benchmark.rs +++ b/src/benchmark.rs @@ -4,20 +4,21 @@ use std::time::{Duration, Instant}; use futures::stream::{self, StreamExt}; -use serde_json::{json, Value}; -use tokio::{runtime, time::delay_for}; +use serde_json::{json, Map, Value}; +use tokio::{runtime, time::sleep}; use crate::actions::{Report, Runnable}; use crate::config::Config; use crate::expandable::include; +use crate::tags::Tags; use crate::writer; use reqwest::Client; use colored::*; -pub type Benchmark = Vec>; -pub type Context = HashMap; +pub type Benchmark = Vec>; +pub type Context = Map; pub type Reports = Vec; pub type PoolStore = HashMap; pub type Pool = Arc>; @@ -30,7 +31,7 @@ pub struct BenchmarkResult { async fn run_iteration(benchmark: Arc, pool: Pool, config: Arc, iteration: i64) -> Vec { if config.rampup > 0 { let delay = config.rampup / config.iterations; - delay_for(Duration::new((delay * iteration) as u64, 0)).await; + sleep(Duration::new((delay * iteration) as u64, 0)).await; } let mut context: Context = Context::new(); @@ -53,7 +54,8 @@ fn join(l: Vec, sep: &str) -> String { ) } -pub fn execute(benchmark_path: &str, report_path_option: Option<&str>, relaxed_interpolations: bool, no_check_certificate: bool, quiet: bool, nanosec: bool, timeout: Option<&str>, verbose: bool) -> BenchmarkResult { +#[allow(clippy::too_many_arguments)] +pub fn execute(benchmark_path: &str, report_path_option: Option<&str>, relaxed_interpolations: bool, no_check_certificate: bool, quiet: bool, nanosec: bool, timeout: Option<&str>, verbose: bool, tags: &Tags) -> BenchmarkResult { let config = Arc::new(Config::new(benchmark_path, relaxed_interpolations, no_check_certificate, quiet, nanosec, timeout.map_or(10, |t| t.parse().unwrap_or(10)), verbose)); if report_path_option.is_some() { @@ -68,12 +70,18 @@ pub fn execute(benchmark_path: &str, report_path_option: Option<&str>, relaxed_i println!(); let threads = std::cmp::min(num_cpus::get(), config.concurrency as usize); - let mut rt = runtime::Builder::new().threaded_scheduler().enable_all().core_threads(threads).max_threads(threads).build().unwrap(); + let rt = runtime::Builder::new_current_thread().enable_all().worker_threads(threads).build().unwrap(); + rt.block_on(async { let mut benchmark: Benchmark = Benchmark::new(); let pool_store: PoolStore = PoolStore::new(); - include::expand_from_filepath(benchmark_path, &mut benchmark, Some("plan")); + include::expand_from_filepath(benchmark_path, &mut benchmark, Some("plan"), tags); + + if benchmark.is_empty() { + eprintln!("Empty benchmark. Exiting."); + std::process::exit(1); + } let benchmark = Arc::new(benchmark); let pool = Arc::new(Mutex::new(pool_store)); diff --git a/src/checker.rs b/src/checker.rs index 9ee3009..af6136f 100644 --- a/src/checker.rs +++ b/src/checker.rs @@ -1,11 +1,7 @@ -use std::fs::File; -use std::io::prelude::*; -use std::path::Path; - use colored::*; -use yaml_rust::YamlLoader; use crate::actions::Report; +use crate::reader; pub fn compare(list_reports: &[Vec], filepath: &str, threshold: &str) -> Result<(), i32> { let threshold_value = match threshold.parse::() { @@ -13,32 +9,16 @@ pub fn compare(list_reports: &[Vec], filepath: &str, threshold: &str) -> _ => panic!("arrrgh"), }; - // Create a path to the desired file - let path = Path::new(filepath); - let display = path.display(); - - // Open the path in read-only mode, returns `io::Result` - let mut file = match File::open(&path) { - Err(why) => panic!("couldn't open {}: {}", display, why), - Ok(file) => file, - }; - - // Read the file contents into a string, returns `io::Result` - let mut content = String::new(); - if let Err(why) = file.read_to_string(&mut content) { - panic!("couldn't read {}: {}", display, why); - } - - let docs = YamlLoader::load_from_str(content.as_str()).unwrap(); + let docs = reader::read_file_as_yml(filepath); let doc = &docs[0]; - let items = doc.as_vec().unwrap(); + let items = doc.as_sequence().unwrap(); let mut slow_counter = 0; println!(); for report in list_reports { for (i, report_item) in report.iter().enumerate() { - let recorded_duration = items[i]["duration"].as_f64().unwrap(); + let recorded_duration = items[i].get("duration").and_then(|v| v.as_f64()).unwrap(); let delta_ms = report_item.duration - recorded_duration; if delta_ms > threshold_value { diff --git a/src/config.rs b/src/config.rs index 958548a..38cce2f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,4 @@ -use yaml_rust::{Yaml, YamlLoader}; +use serde_yaml::Value; use crate::benchmark::Context; use crate::interpolator; @@ -22,9 +22,7 @@ pub struct Config { impl Config { pub fn new(path: &str, relaxed_interpolations: bool, no_check_certificate: bool, quiet: bool, nanosec: bool, timeout: u64, verbose: bool) -> Config { - let config_file = reader::read_file(path); - - let config_docs = YamlLoader::load_from_str(config_file.as_str()).unwrap(); + let config_docs = reader::read_file_as_yml(path); let config_doc = &config_docs[0]; let context: Context = Context::new(); @@ -54,18 +52,18 @@ impl Config { } } -fn read_str_configuration(config_doc: &Yaml, interpolator: &interpolator::Interpolator, name: &str, default: &str) -> String { - match config_doc[name].as_str() { +fn read_str_configuration(config_doc: &Value, interpolator: &interpolator::Interpolator, name: &str, default: &str) -> String { + match config_doc.get(name).and_then(|v| v.as_str()) { Some(value) => { if value.contains('{') { - interpolator.resolve(&value, true).to_owned() + interpolator.resolve(value, true) } else { value.to_owned() } } None => { - if config_doc[name].as_str().is_some() { - println!("Invalid {} value!", name); + if config_doc.get(name).and_then(|v| v.as_str()).is_some() { + println!("Invalid {name} value!"); } default.to_owned() @@ -73,11 +71,11 @@ fn read_str_configuration(config_doc: &Yaml, interpolator: &interpolator::Interp } } -fn read_i64_configuration(config_doc: &Yaml, interpolator: &interpolator::Interpolator, name: &str, default: i64) -> i64 { - let value = if let Some(value) = config_doc[name].as_i64() { +fn read_i64_configuration(config_doc: &Value, interpolator: &interpolator::Interpolator, name: &str, default: i64) -> i64 { + let value = if let Some(value) = config_doc.get(name).and_then(|v| v.as_i64()) { Some(value) - } else if let Some(key) = config_doc[name].as_str() { - interpolator.resolve(&key, false).parse::().ok() + } else if let Some(key) = config_doc.get(name).and_then(|v| v.as_str()) { + interpolator.resolve(key, false).parse::().ok() } else { None }; @@ -85,7 +83,7 @@ fn read_i64_configuration(config_doc: &Yaml, interpolator: &interpolator::Interp match value { Some(value) => { if value < 0 { - println!("Invalid negative {} value!", name); + println!("Invalid negative {name} value!"); default } else { @@ -93,8 +91,8 @@ fn read_i64_configuration(config_doc: &Yaml, interpolator: &interpolator::Interp } } None => { - if config_doc[name].as_str().is_some() { - println!("Invalid {} value!", name); + if config_doc.get(name).and_then(|v| v.as_str()).is_some() { + println!("Invalid {name} value!"); } default diff --git a/src/expandable/include.rs b/src/expandable/include.rs index e2f5232..91576fb 100644 --- a/src/expandable/include.rs +++ b/src/expandable/include.rs @@ -1,63 +1,55 @@ +use serde_yaml::Value; use std::path::Path; -use std::process; -use yaml_rust::{Yaml, YamlEmitter, YamlLoader}; use crate::interpolator::INTERPOLATION_REGEX; use crate::actions; use crate::benchmark::Benchmark; use crate::expandable::{include, multi_csv_request, multi_file_request, multi_iter_request, multi_request}; +use crate::tags::Tags; use crate::reader; -pub fn is_that_you(item: &Yaml) -> bool { - item["include"].as_str().is_some() +pub fn is_that_you(item: &Value) -> bool { + item.get("include").and_then(|v| v.as_str()).is_some() } -pub fn expand(parent_path: &str, item: &Yaml, mut benchmark: &mut Benchmark) { - let include_path = item["include"].as_str().unwrap(); +pub fn expand(parent_path: &str, item: &Value, benchmark: &mut Benchmark, tags: &Tags) { + let include_path = item.get("include").and_then(|v| v.as_str()).unwrap(); - if INTERPOLATION_REGEX.is_match(&include_path) { + if INTERPOLATION_REGEX.is_match(include_path) { panic!("Interpolations not supported in 'include' property!"); } let include_filepath = Path::new(parent_path).with_file_name(include_path); let final_path = include_filepath.to_str().unwrap(); - expand_from_filepath(final_path, &mut benchmark, None); + expand_from_filepath(final_path, benchmark, None, tags); } -pub fn expand_from_filepath(parent_path: &str, mut benchmark: &mut Benchmark, accessor: Option<&str>) { - let benchmark_file = reader::read_file(parent_path); - - let docs = YamlLoader::load_from_str(benchmark_file.as_str()).unwrap(); - let doc = &docs[0]; - let items; - - if let Some(accessor_id) = accessor { - items = match doc[accessor_id].as_vec() { - Some(items) => items, - None => { - println!("Node missing on config: {}", accessor_id); - println!("Exiting."); - process::exit(1) - } - } - } else { - items = doc.as_vec().unwrap(); - } +pub fn expand_from_filepath(parent_path: &str, benchmark: &mut Benchmark, accessor: Option<&str>, tags: &Tags) { + let docs = reader::read_file_as_yml(parent_path); + let items = reader::read_yaml_doc_accessor(&docs[0], accessor); for item in items { + if include::is_that_you(item) { + include::expand(parent_path, item, benchmark, tags); + + continue; + } + + if tags.should_skip_item(item) { + continue; + } + if multi_request::is_that_you(item) { - multi_request::expand(item, &mut benchmark); + multi_request::expand(item, benchmark); } else if multi_iter_request::is_that_you(item) { - multi_iter_request::expand(item, &mut benchmark); + multi_iter_request::expand(item, benchmark); } else if multi_csv_request::is_that_you(item) { - multi_csv_request::expand(parent_path, item, &mut benchmark); + multi_csv_request::expand(parent_path, item, benchmark); } else if multi_file_request::is_that_you(item) { - multi_file_request::expand(parent_path, item, &mut benchmark); - } else if include::is_that_you(item) { - include::expand(parent_path, item, &mut benchmark); + multi_file_request::expand(parent_path, item, benchmark); } else if actions::Delay::is_that_you(item) { benchmark.push(Box::new(actions::Delay::new(item, None))); } else if actions::Exec::is_that_you(item) { @@ -69,27 +61,28 @@ pub fn expand_from_filepath(parent_path: &str, mut benchmark: &mut Benchmark, ac } else if actions::Request::is_that_you(item) { benchmark.push(Box::new(actions::Request::new(item, None, None))); } else { - let mut out_str = String::new(); - let mut emitter = YamlEmitter::new(&mut out_str); - emitter.dump(item).unwrap(); - panic!("Unknown node:\n\n{}\n\n", out_str); + let out_str = serde_yaml::to_string(item).unwrap(); + panic!("Unknown node:\n\n{out_str}\n\n"); } } } +#[cfg(test)] mod tests { - use super::*; + use crate::benchmark::Benchmark; + use crate::expandable::include::{expand, is_that_you}; + use crate::tags::Tags; #[test] fn expand_include() { let text = "---\nname: Include comment\ninclude: comments.yml"; - let docs = yaml_rust::YamlLoader::load_from_str(text).unwrap(); + let docs = crate::reader::read_file_as_yml_from_str(text); let doc = &docs[0]; let mut benchmark: Benchmark = Benchmark::new(); - expand("example/benchmark.yml", &doc, &mut benchmark); + expand("example/benchmark.yml", doc, &mut benchmark, &Tags::new(None, None)); - assert_eq!(is_that_you(&doc), true); + assert!(is_that_you(doc)); assert_eq!(benchmark.len(), 2); } @@ -97,10 +90,10 @@ mod tests { #[should_panic] fn invalid_expand() { let text = "---\nname: Include comment\ninclude: {{ memory }}.yml"; - let docs = yaml_rust::YamlLoader::load_from_str(text).unwrap(); + let docs = crate::reader::read_file_as_yml_from_str(text); let doc = &docs[0]; let mut benchmark: Benchmark = Benchmark::new(); - expand("example/benchmark.yml", &doc, &mut benchmark); + expand("example/benchmark.yml", doc, &mut benchmark, &Tags::new(None, None)); } } diff --git a/src/expandable/mod.rs b/src/expandable/mod.rs index 9674966..1b044b4 100644 --- a/src/expandable/mod.rs +++ b/src/expandable/mod.rs @@ -4,3 +4,63 @@ mod multi_csv_request; mod multi_file_request; mod multi_iter_request; mod multi_request; + +use serde_yaml::Value; + +pub fn pick(item: &Value, with_items: &[Value]) -> usize { + match item.get("pick").and_then(|v| v.as_i64()) { + Some(value) => { + if value.is_negative() { + panic!("pick option should not be negative, but was {value}"); + } else if value as usize > with_items.len() { + panic!("pick option should not be greater than the provided items, but was {value}"); + } else { + value as usize + } + } + None => with_items.len(), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + mod pick { + use super::*; + + #[test] + fn should_return_the_configured_value() { + let text = "---\nname: foobar\nrequest:\n url: /api/{{ item }}\npick: 2\nwith_items:\n - 1\n - 2\n - 3"; + let item = &crate::reader::read_file_as_yml_from_str(text)[0]; + let pick = pick(item, item.get("with_items").and_then(|v| v.as_sequence()).unwrap()); + + assert_eq!(pick, 2); + } + + #[test] + fn should_return_the_with_items_length_if_unset() { + let text = "---\nname: foobar\nrequest:\n url: /api/{{ item }}\nwith_items:\n - 1\n - 2\n - 3"; + let item = &crate::reader::read_file_as_yml_from_str(text)[0]; + let pick = pick(item, item.get("with_items").and_then(|v| v.as_sequence()).unwrap()); + + assert_eq!(pick, 3); + } + + #[test] + #[should_panic(expected = "pick option should not be negative, but was -1")] + fn should_panic_for_negative_values() { + let text = "---\nname: foobar\nrequest:\n url: /api/{{ item }}\npick: -1\nwith_items:\n - 1\n - 2\n - 3"; + let item = &crate::reader::read_file_as_yml_from_str(text)[0]; + pick(item, item.get("with_items").and_then(|v| v.as_sequence()).unwrap()); + } + + #[test] + #[should_panic(expected = "pick option should not be greater than the provided items, but was 4")] + fn should_panic_for_values_greater_than_the_items_list() { + let text = "---\nname: foobar\nrequest:\n url: /api/{{ item }}\npick: 4\nwith_items:\n - 1\n - 2\n - 3"; + let item = &crate::reader::read_file_as_yml_from_str(text)[0]; + pick(item, item.get("with_items").and_then(|v| v.as_sequence()).unwrap()); + } + } +} diff --git a/src/expandable/multi_csv_request.rs b/src/expandable/multi_csv_request.rs index 4e86c0f..35bb550 100644 --- a/src/expandable/multi_csv_request.rs +++ b/src/expandable/multi_csv_request.rs @@ -1,31 +1,32 @@ use rand::seq::SliceRandom; use rand::thread_rng; +use serde_yaml::Value; use std::path::Path; -use yaml_rust::Yaml; - -use crate::interpolator::INTERPOLATION_REGEX; +use super::pick; use crate::actions::Request; use crate::benchmark::Benchmark; +use crate::interpolator::INTERPOLATION_REGEX; use crate::reader; -pub fn is_that_you(item: &Yaml) -> bool { - item["request"].as_hash().is_some() && (item["with_items_from_csv"].as_str().is_some() || item["with_items_from_csv"].as_hash().is_some()) +pub fn is_that_you(item: &Value) -> bool { + item.get("request").and_then(|v| v.as_mapping()).is_some() && (item.get("with_items_from_csv").and_then(|v| v.as_str()).is_some() || item.get("with_items_from_csv").and_then(|v| v.as_mapping()).is_some()) } -pub fn expand(parent_path: &str, item: &Yaml, benchmark: &mut Benchmark) { - let (with_items_path, quote_char) = if let Some(with_items_path) = item["with_items_from_csv"].as_str() { +pub fn expand(parent_path: &str, item: &Value, benchmark: &mut Benchmark) { + let (with_items_path, quote_char) = if let Some(with_items_path) = item.get("with_items_from_csv").and_then(|v| v.as_str()) { (with_items_path, b'\"') - } else if let Some(_with_items_hash) = item["with_items_from_csv"].as_hash() { - let with_items_path = item["with_items_from_csv"]["file_name"].as_str().expect("Expected a file_name"); - let quote_char = item["with_items_from_csv"]["quote_char"].as_str().unwrap_or("\"").bytes().next().unwrap(); + } else if let Some(_with_items_hash) = item.get("with_items_from_csv").and_then(|v| v.as_mapping()) { + let csv_val = item.get("with_items_from_csv").unwrap(); + let with_items_path = csv_val.get("file_name").and_then(|v| v.as_str()).expect("Expected a file_name"); + let quote_char = csv_val.get("quote_char").and_then(|v| v.as_str()).unwrap_or("\"").bytes().next().unwrap(); (with_items_path, quote_char) } else { unreachable!(); }; - if INTERPOLATION_REGEX.is_match(&with_items_path) { + if INTERPOLATION_REGEX.is_match(with_items_path) { panic!("Interpolations not supported in 'with_items_from_csv' property!"); } @@ -34,14 +35,15 @@ pub fn expand(parent_path: &str, item: &Yaml, benchmark: &mut Benchmark) { let mut with_items_file = reader::read_csv_file_as_yml(final_path, quote_char); - if let Some(shuffle) = item["shuffle"].as_bool() { + if let Some(shuffle) = item.get("shuffle").and_then(|v| v.as_bool()) { if shuffle { let mut rng = thread_rng(); with_items_file.shuffle(&mut rng); } } - for (index, with_item) in with_items_file.iter().enumerate() { + let pick = pick(item, &with_items_file); + for (index, with_item) in with_items_file.iter().take(pick).enumerate() { let index = index as u32; benchmark.push(Box::new(Request::new(item, Some(with_item.clone()), Some(index)))); @@ -55,24 +57,50 @@ mod tests { #[test] fn expand_multi() { let text = "---\nname: foobar\nrequest:\n url: /api/{{ item.id }}\nwith_items_from_csv: ./fixtures/users.csv"; - let docs = yaml_rust::YamlLoader::load_from_str(text).unwrap(); + let docs = crate::reader::read_file_as_yml_from_str(text); + let doc = &docs[0]; + let mut benchmark: Benchmark = Benchmark::new(); + + expand("example/benchmark.yml", doc, &mut benchmark); + + assert!(is_that_you(doc)); + assert_eq!(benchmark.len(), 2); + } + + #[test] + fn expand_multi_should_limit_requests_using_the_pick_option() { + let text = "---\nname: foobar\nrequest:\n url: /api/{{ item }}\npick: 2\nwith_items_from_csv: ./fixtures/users.csv"; + let docs = crate::reader::read_file_as_yml_from_str(text); let doc = &docs[0]; let mut benchmark: Benchmark = Benchmark::new(); - expand("example/benchmark.yml", &doc, &mut benchmark); + expand("example/benchmark.yml", doc, &mut benchmark); - assert_eq!(is_that_you(&doc), true); + assert!(is_that_you(doc)); assert_eq!(benchmark.len(), 2); } + #[test] + fn expand_multi_should_work_with_pick_and_shuffle() { + let text = "---\nname: foobar\nrequest:\n url: /api/{{ item }}\npick: 1\nshuffle: true\nwith_items_from_csv: ./fixtures/users.csv"; + let docs = crate::reader::read_file_as_yml_from_str(text); + let doc = &docs[0]; + let mut benchmark: Benchmark = Benchmark::new(); + + expand("example/benchmark.yml", doc, &mut benchmark); + + assert!(is_that_you(doc)); + assert_eq!(benchmark.len(), 1); + } + #[test] #[should_panic] fn runtime_expand() { let text = "---\nname: foobar\nrequest:\n url: /api/{{ item.id }}\nwith_items_from_csv: ./fixtures/{{ memory }}.csv"; - let docs = yaml_rust::YamlLoader::load_from_str(text).unwrap(); + let docs = crate::reader::read_file_as_yml_from_str(text); let doc = &docs[0]; let mut benchmark: Benchmark = Benchmark::new(); - expand("example/benchmark.yml", &doc, &mut benchmark); + expand("example/benchmark.yml", doc, &mut benchmark); } } diff --git a/src/expandable/multi_file_request.rs b/src/expandable/multi_file_request.rs index 420bf7d..86c8093 100644 --- a/src/expandable/multi_file_request.rs +++ b/src/expandable/multi_file_request.rs @@ -1,41 +1,42 @@ +use super::pick; +use crate::actions::Request; use crate::benchmark::Benchmark; use crate::interpolator::INTERPOLATION_REGEX; +use crate::reader; use rand::seq::SliceRandom; use rand::thread_rng; +use serde_yaml::Value; use std::path::Path; -use yaml_rust::Yaml; - -use crate::actions::Request; -use crate::reader; -pub fn is_that_you(item: &Yaml) -> bool { - item["request"].as_hash().is_some() && (item["with_items_from_file"].as_str().is_some() || item["with_items_from_file"].as_hash().is_some()) +pub fn is_that_you(item: &Value) -> bool { + item.get("request").and_then(|v| v.as_mapping()).is_some() && (item.get("with_items_from_file").and_then(|v| v.as_str()).is_some() || item.get("with_items_from_file").and_then(|v| v.as_mapping()).is_some()) } -pub fn expand(parent_path: &str, item: &Yaml, benchmark: &mut Benchmark) { - let with_items_path = if let Some(with_items_path) = item["with_items_from_file"].as_str() { +pub fn expand(parent_path: &str, item: &Value, benchmark: &mut Benchmark) { + let with_items_path = if let Some(with_items_path) = item.get("with_items_from_file").and_then(|v| v.as_str()) { with_items_path } else { unreachable!(); }; - if INTERPOLATION_REGEX.is_match(&with_items_path) { + if INTERPOLATION_REGEX.is_match(with_items_path) { panic!("Interpolation not supported in 'with_items_from_file' property!"); } let with_items_filepath = Path::new(parent_path).with_file_name(with_items_path); let final_path = with_items_filepath.to_str().unwrap(); - let mut with_items_file = reader::read_file_as_yml(final_path); + let mut with_items_file = reader::read_file_as_yml_array(final_path); - if let Some(shuffle) = item["shuffle"].as_bool() { + if let Some(shuffle) = item.get("shuffle").and_then(|v| v.as_bool()) { if shuffle { let mut rng = thread_rng(); with_items_file.shuffle(&mut rng); } } - for (index, with_item) in with_items_file.iter().enumerate() { + let pick = pick(item, &with_items_file); + for (index, with_item) in with_items_file.iter().take(pick).enumerate() { let index = index as u32; benchmark.push(Box::new(Request::new(item, Some(with_item.clone()), Some(index)))); @@ -49,13 +50,39 @@ mod test { #[test] fn expand_multi() { let text = "---\nname: foobar\nrequest:\n url: /api/{{ item.id }}\nwith_items_from_file: ./fixtures/texts.txt"; - let docs = yaml_rust::YamlLoader::load_from_str(text).unwrap(); + let docs = crate::reader::read_file_as_yml_from_str(text); let doc = &docs[0]; let mut benchmark: Benchmark = Benchmark::new(); - expand("example/benchmark.yml", &doc, &mut benchmark); + expand("example/benchmark.yml", doc, &mut benchmark); - assert_eq!(is_that_you(&doc), true); + assert!(is_that_you(doc)); assert_eq!(benchmark.len(), 3); } + + #[test] + fn expand_multi_should_limit_requests_using_the_pick_option() { + let text = "---\nname: foobar\nrequest:\n url: /api/{{ item }}\npick: 2\nwith_items_from_file: ./fixtures/texts.txt"; + let docs = crate::reader::read_file_as_yml_from_str(text); + let doc = &docs[0]; + let mut benchmark: Benchmark = Benchmark::new(); + + expand("example/benchmark.yml", doc, &mut benchmark); + + assert!(is_that_you(doc)); + assert_eq!(benchmark.len(), 2); + } + + #[test] + fn expand_multi_should_work_with_pick_and_shuffle() { + let text = "---\nname: foobar\nrequest:\n url: /api/{{ item }}\npick: 1\nshuffle: true\nwith_items_from_file: ./fixtures/texts.txt"; + let docs = crate::reader::read_file_as_yml_from_str(text); + let doc = &docs[0]; + let mut benchmark: Benchmark = Benchmark::new(); + + expand("example/benchmark.yml", doc, &mut benchmark); + + assert!(is_that_you(doc)); + assert_eq!(benchmark.len(), 1); + } } diff --git a/src/expandable/multi_iter_request.rs b/src/expandable/multi_iter_request.rs index 376848a..6a4aa18 100644 --- a/src/expandable/multi_iter_request.rs +++ b/src/expandable/multi_iter_request.rs @@ -1,40 +1,42 @@ +use std::convert::TryInto; + use rand::seq::SliceRandom; use rand::thread_rng; -use yaml_rust::Yaml; +use serde_yaml::{Number, Value}; use crate::interpolator::INTERPOLATION_REGEX; use crate::actions::Request; use crate::benchmark::Benchmark; -pub fn is_that_you(item: &Yaml) -> bool { - item["request"].as_hash().is_some() && item["with_items_range"].as_hash().is_some() +pub fn is_that_you(item: &Value) -> bool { + item.get("request").and_then(|v| v.as_mapping()).is_some() && item.get("with_items_range").and_then(|v| v.as_mapping()).is_some() } -pub fn expand(item: &Yaml, benchmark: &mut Benchmark) { - if let Some(with_iter_items) = item["with_items_range"].as_hash() { - let init = Yaml::Integer(1); - let lstart = Yaml::String("start".into()); - let lstep = Yaml::String("step".into()); - let lstop = Yaml::String("stop".into()); +pub fn expand(item: &Value, benchmark: &mut Benchmark) { + if let Some(with_iter_items) = item.get("with_items_range").and_then(|v| v.as_mapping()) { + let lstart = Value::String("start".into()); + let lstep = Value::String("step".into()); + let lstop = Value::String("stop".into()); - let vstart: &Yaml = with_iter_items.get(&lstart).expect("Start property is mandatory"); - let vstep: &Yaml = with_iter_items.get(&lstep).unwrap_or(&init); - let vstop: &Yaml = with_iter_items.get(&lstop).expect("Stop property is mandatory"); + let vstart = with_iter_items.get(&lstart).expect("Start property is mandatory"); + let default_step = Value::Number(Number::from(1)); + let vstep = with_iter_items.get(&lstep).unwrap_or(&default_step); + let vstop = with_iter_items.get(&lstop).expect("Stop property is mandatory"); let start: &str = vstart.as_str().unwrap_or(""); let step: &str = vstep.as_str().unwrap_or(""); let stop: &str = vstop.as_str().unwrap_or(""); - if INTERPOLATION_REGEX.is_match(&start) { + if INTERPOLATION_REGEX.is_match(start) { panic!("Interpolations not supported in 'start' property!"); } - if INTERPOLATION_REGEX.is_match(&step) { + if INTERPOLATION_REGEX.is_match(step) { panic!("Interpolations not supported in 'step' property!"); } - if INTERPOLATION_REGEX.is_match(&stop) { + if INTERPOLATION_REGEX.is_match(stop) { panic!("Interpolations not supported in 'stop' property!"); } @@ -47,17 +49,21 @@ pub fn expand(item: &Yaml, benchmark: &mut Benchmark) { if stop > start && start > 0 { let mut with_items: Vec = (start..stop).step_by(step as usize).collect(); - if let Some(shuffle) = item["shuffle"].as_bool() { + if let Some(shuffle) = item.get("shuffle").and_then(|v| v.as_bool()) { if shuffle { let mut rng = thread_rng(); with_items.shuffle(&mut rng); } } + if let Some(pick) = item.get("pick").and_then(|v| v.as_i64()) { + with_items.truncate(pick.try_into().expect("pick can't be larger than size of range")) + } + for (index, value) in with_items.iter().enumerate() { let index = index as u32; - benchmark.push(Box::new(Request::new(item, Some(Yaml::Integer(*value)), Some(index)))); + benchmark.push(Box::new(Request::new(item, Some(Value::Number(Number::from(*value))), Some(index)))); } } } @@ -70,35 +76,48 @@ mod tests { #[test] fn expand_multi_range() { let text = "---\nname: foobar\nrequest:\n url: /api/{{ item }}\nwith_items_range:\n start: 2\n step: 2\n stop: 20"; - let docs = yaml_rust::YamlLoader::load_from_str(text).unwrap(); + let docs = crate::reader::read_file_as_yml_from_str(text); let doc = &docs[0]; let mut benchmark: Benchmark = Benchmark::new(); - expand(&doc, &mut benchmark); + expand(doc, &mut benchmark); - assert_eq!(is_that_you(&doc), true); + assert!(is_that_you(doc)); assert_eq!(benchmark.len(), 10); } + #[test] + fn expand_multi_range_should_limit_requests_using_the_pick_option() { + let text = "---\nname: foobar\nrequest:\n url: /api/{{ item }}\npick: 3\nwith_items_range:\n start: 2\n step: 2\n stop: 20"; + let docs = crate::reader::read_file_as_yml_from_str(text); + let doc = &docs[0]; + let mut benchmark: Benchmark = Benchmark::new(); + + expand(doc, &mut benchmark); + + assert!(is_that_you(doc)); + assert_eq!(benchmark.len(), 3); + } + #[test] #[should_panic] fn invalid_expand() { let text = "---\nname: foobar\nrequest:\n url: /api/{{ item }}\nwith_items_range:\n start: 1\n step: 2\n stop: foo"; - let docs = yaml_rust::YamlLoader::load_from_str(text).unwrap(); + let docs = crate::reader::read_file_as_yml_from_str(text); let doc = &docs[0]; let mut benchmark: Benchmark = Benchmark::new(); - expand(&doc, &mut benchmark); + expand(doc, &mut benchmark); } #[test] #[should_panic] fn runtime_expand() { let text = "---\nname: foobar\nrequest:\n url: /api/{{ item }}\nwith_items_range:\n start: 1\n step: 2\n stop: \"{{ memory }}\""; - let docs = yaml_rust::YamlLoader::load_from_str(text).unwrap(); + let docs = crate::reader::read_file_as_yml_from_str(text); let doc = &docs[0]; let mut benchmark: Benchmark = Benchmark::new(); - expand(&doc, &mut benchmark); + expand(doc, &mut benchmark); } } diff --git a/src/expandable/multi_request.rs b/src/expandable/multi_request.rs index 0c0173a..4824f8f 100644 --- a/src/expandable/multi_request.rs +++ b/src/expandable/multi_request.rs @@ -1,28 +1,29 @@ use rand::seq::SliceRandom; use rand::thread_rng; -use yaml_rust::Yaml; - -use crate::interpolator::INTERPOLATION_REGEX; +use serde_yaml::Value; +use super::pick; use crate::actions::Request; use crate::benchmark::Benchmark; +use crate::interpolator::INTERPOLATION_REGEX; -pub fn is_that_you(item: &Yaml) -> bool { - item["request"].as_hash().is_some() && item["with_items"].as_vec().is_some() +pub fn is_that_you(item: &Value) -> bool { + item.get("request").and_then(|v| v.as_mapping()).is_some() && item.get("with_items").and_then(|v| v.as_sequence()).is_some() } -pub fn expand(item: &Yaml, benchmark: &mut Benchmark) { - if let Some(with_items) = item["with_items"].as_vec() { +pub fn expand(item: &Value, benchmark: &mut Benchmark) { + if let Some(with_items) = item.get("with_items").and_then(|v| v.as_sequence()) { let mut with_items_list = with_items.clone(); - if let Some(shuffle) = item["shuffle"].as_bool() { + if let Some(shuffle) = item.get("shuffle").and_then(|v| v.as_bool()) { if shuffle { let mut rng = thread_rng(); with_items_list.shuffle(&mut rng); } } - for (index, with_item) in with_items_list.iter().enumerate() { + let pick = pick(item, &with_items_list); + for (index, with_item) in with_items_list.iter().take(pick).enumerate() { let index = index as u32; let value: &str = with_item.as_str().unwrap_or(""); @@ -43,24 +44,50 @@ mod tests { #[test] fn expand_multi() { let text = "---\nname: foobar\nrequest:\n url: /api/{{ item }}\nwith_items:\n - 1\n - 2\n - 3"; - let docs = yaml_rust::YamlLoader::load_from_str(text).unwrap(); + let docs = crate::reader::read_file_as_yml_from_str(text); let doc = &docs[0]; let mut benchmark: Benchmark = Benchmark::new(); - expand(&doc, &mut benchmark); + expand(doc, &mut benchmark); - assert_eq!(is_that_you(&doc), true); + assert!(is_that_you(doc)); assert_eq!(benchmark.len(), 3); } + #[test] + fn expand_multi_should_limit_requests_using_the_pick_option() { + let text = "---\nname: foobar\nrequest:\n url: /api/{{ item }}\npick: 2\nwith_items:\n - 1\n - 2\n - 3"; + let docs = crate::reader::read_file_as_yml_from_str(text); + let doc = &docs[0]; + let mut benchmark: Benchmark = Benchmark::new(); + + expand(doc, &mut benchmark); + + assert!(is_that_you(doc)); + assert_eq!(benchmark.len(), 2); + } + + #[test] + fn expand_multi_should_work_with_pick_and_shuffle() { + let text = "---\nname: foobar\nrequest:\n url: /api/{{ item }}\npick: 1\nshuffle: true\nwith_items:\n - 1\n - 2\n - 3"; + let docs = crate::reader::read_file_as_yml_from_str(text); + let doc = &docs[0]; + let mut benchmark: Benchmark = Benchmark::new(); + + expand(doc, &mut benchmark); + + assert!(is_that_you(doc)); + assert_eq!(benchmark.len(), 1); + } + #[test] #[should_panic] fn runtime_expand() { let text = "---\nname: foobar\nrequest:\n url: /api/{{ item }}\nwith_items:\n - 1\n - 2\n - foo{{ memory }}"; - let docs = yaml_rust::YamlLoader::load_from_str(text).unwrap(); + let docs = crate::reader::read_file_as_yml_from_str(text); let doc = &docs[0]; let mut benchmark: Benchmark = Benchmark::new(); - expand(&doc, &mut benchmark); + expand(doc, &mut benchmark); } } diff --git a/src/interpolator.rs b/src/interpolator.rs index bc16767..04161c9 100644 --- a/src/interpolator.rs +++ b/src/interpolator.rs @@ -1,15 +1,16 @@ use colored::*; use lazy_static::lazy_static; use regex::{Captures, Regex}; +use serde_json::json; use crate::benchmark::Context; -static INTERPOLATION_PREFIX: &'static str = "{{"; -static INTERPOLATION_SUFFIX: &'static str = "}}"; +static INTERPOLATION_PREFIX: &str = "{{"; +static INTERPOLATION_SUFFIX: &str = "}}"; lazy_static! { pub static ref INTERPOLATION_REGEX: Regex = { - let regexp = format!("{}{}{}", regex::escape(INTERPOLATION_PREFIX), r" *([a-zA-Z\-\._]+[a-zA-Z\-\._0-9]*) *", regex::escape(INTERPOLATION_SUFFIX)); + let regexp = format!("{}{}{}", regex::escape(INTERPOLATION_PREFIX), r" *([a-zA-Z]+[a-zA-Z\-\._\$0-9\[\]]*) *", regex::escape(INTERPOLATION_SUFFIX)); Regex::new(regexp.as_str()).unwrap() }; @@ -31,7 +32,7 @@ impl<'a> Interpolator<'a> { .replace_all(url, |caps: &Captures| { let capture = &caps[1]; - if let Some(item) = self.resolve_context_interpolation(capture.split('.').collect()) { + if let Some(item) = self.resolve_context_interpolation(capture) { return item; } @@ -57,22 +58,22 @@ impl<'a> Interpolator<'a> { } } - fn resolve_context_interpolation(&self, cap_path: Vec<&str>) -> Option { - let (cap_root, cap_tail) = cap_path.split_at(1); - - cap_tail - .iter() - .fold(self.context.get(cap_root[0]), |json, k| match json { - Some(json) => json.get(k), - _ => None, - }) - .map(|value| { - if value.is_string() { - String::from(value.as_str().unwrap()) - } else { - value.to_string() - } - }) + fn resolve_context_interpolation(&self, value: &str) -> Option { + // convert "." and "[" to "/" and "]" to "" to look like a json pointer + let val: String = format!("/{}", value.replace(['.', '['], "/").replace(']', "")); + + // force the context into a Value, and acess by pointer + if let Some(item) = json!(self.context).pointer(&val).to_owned() { + return Some(match item.to_owned() { + serde_json::Value::Null => "".to_owned(), + serde_json::Value::Bool(v) => v.to_string(), + serde_json::Value::Number(v) => v.to_string(), + serde_json::Value::String(v) => v, + serde_json::Value::Array(v) => serde_json::to_string(&v).unwrap(), + serde_json::Value::Object(v) => serde_json::to_string(&v).unwrap(), + }); + } + None } } @@ -95,6 +96,32 @@ mod tests { assert_eq!(interpolated, "http://example.com/users/12/view/12/chunked"); } + #[test] + fn interpolates_variables_nested() { + let mut context: Context = Context::new(); + + context.insert(String::from("Null"), serde_json::Value::Null); + context.insert(String::from("Bool"), json!(true)); + context.insert(String::from("Number"), json!(12)); + context.insert(String::from("String"), json!("string")); + context.insert(String::from("Array"), json!(["a", "b", "c"])); + context.insert(String::from("Object"), json!({"this": "that"})); + context.insert(String::from("Nested"), json!({"this": {"that": {"those": [{"wow": 1}, {"so": 2}, {"deee": {"eeee": "eeep"}}]}}})); + context.insert(String::from("ArrayNested"), json!([{"a": [{}, {"aa": 2, "aaa": [{"aaaa": 123, "$aaaa": "$123"}]}]}])); + + let interpolator = Interpolator::new(&context); + + assert_eq!(interpolator.resolve("{{ Null }}", true), "".to_string()); + assert_eq!(interpolator.resolve("{{ Bool }}", true), "true".to_string()); + assert_eq!(interpolator.resolve("{{ Number }}", true), "12".to_string()); + assert_eq!(interpolator.resolve("{{ String }}", true), "string".to_string()); + assert_eq!(interpolator.resolve("{{ Array }}", true), "[\"a\",\"b\",\"c\"]".to_string()); + assert_eq!(interpolator.resolve("{{ Object }}", true), "{\"this\":\"that\"}".to_string()); + assert_eq!(interpolator.resolve("{{ Nested.this.that.those[2].deee.eeee }}", true), "eeep".to_string()); + assert_eq!(interpolator.resolve("{{ ArrayNested[0].a[1].aaa[0].aaaa }}", true), "123".to_string()); + assert_eq!(interpolator.resolve("{{ ArrayNested[0].a[1].aaa[0].$aaaa }}", true), "$123".to_string()); + } + #[test] #[should_panic] fn interpolates_missing_variable() { @@ -144,7 +171,8 @@ mod tests { #[test] fn interpolates_environment_variables() { - std::env::set_var("FOO", "BAR"); + // TODO: Audit that the environment access only happens in single-threaded code. + unsafe { std::env::set_var("FOO", "BAR") }; let context: Context = Context::new(); let interpolator = Interpolator::new(&context); diff --git a/src/main.rs b/src/main.rs index ec0b687..a02f88b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ mod config; mod expandable; mod interpolator; mod reader; +mod tags; mod writer; use crate::actions::Report; @@ -29,10 +30,27 @@ fn main() { let nanosec = matches.is_present("nanosec"); let timeout = matches.value_of("timeout"); let verbose = matches.is_present("verbose"); + let tags_option = matches.value_of("tags"); + let skip_tags_option = matches.value_of("skip-tags"); + let list_tags = matches.is_present("list-tags"); + let list_tasks = matches.is_present("list-tasks"); + #[cfg(windows)] let _ = control::set_virtual_terminal(true); - let benchmark_result = benchmark::execute(benchmark_file, report_path_option, relaxed_interpolations, no_check_certificate, quiet, nanosec, timeout, verbose); + if list_tags { + tags::list_benchmark_file_tags(benchmark_file); + process::exit(0); + }; + + let tags = tags::Tags::new(tags_option, skip_tags_option); + + if list_tasks { + tags::list_benchmark_file_tasks(benchmark_file, &tags); + process::exit(0); + }; + + let benchmark_result = benchmark::execute(benchmark_file, report_path_option, relaxed_interpolations, no_check_certificate, quiet, nanosec, timeout, verbose, &tags); let list_reports = benchmark_result.reports; let duration = benchmark_result.duration; @@ -53,6 +71,10 @@ fn app_args<'a>() -> clap::ArgMatches<'a> { .arg(Arg::with_name("threshold").short("t").long("threshold").help("Sets a threshold value in ms amongst the compared file").takes_value(true).conflicts_with("report")) .arg(Arg::with_name("relaxed-interpolations").long("relaxed-interpolations").help("Do not panic if an interpolation is not present. (Not recommended)").takes_value(false)) .arg(Arg::with_name("no-check-certificate").long("no-check-certificate").help("Disables SSL certification check. (Not recommended)").takes_value(false)) + .arg(Arg::with_name("tags").long("tags").help("Tags to include").takes_value(true)) + .arg(Arg::with_name("skip-tags").long("skip-tags").help("Tags to exclude").takes_value(true)) + .arg(Arg::with_name("list-tags").long("list-tags").help("List all benchmark tags").takes_value(false).conflicts_with_all(&["tags", "skip-tags"])) + .arg(Arg::with_name("list-tasks").long("list-tasks").help("List benchmark tasks (executes --tags/--skip-tags filter)").takes_value(false)) .arg(Arg::with_name("quiet").short("q").long("quiet").help("Disables output").takes_value(false)) .arg(Arg::with_name("timeout").short("o").long("timeout").help("Set timeout in seconds for all requests").takes_value(true)) .arg(Arg::with_name("nanosec").short("n").long("nanosec").help("Shows statistics in nanoseconds").takes_value(false)) @@ -146,11 +168,11 @@ fn show_stats(list_reports: &[Vec], stats_option: bool, nanosec: bool, d let requests_per_second = global_stats.total_requests as f64 / duration; println!(); - println!("{:width2$} {} {}", "Time taken for tests".yellow(), format!("{:.1}", duration).purple(), "seconds".purple(), width2 = 25); + println!("{:width2$} {} {}", "Time taken for tests".yellow(), format!("{duration:.1}").purple(), "seconds".purple(), width2 = 25); println!("{:width2$} {}", "Total requests".yellow(), global_stats.total_requests.to_string().purple(), width2 = 25); println!("{:width2$} {}", "Successful requests".yellow(), global_stats.successful_requests.to_string().purple(), width2 = 25); println!("{:width2$} {}", "Failed requests".yellow(), global_stats.failed_requests.to_string().purple(), width2 = 25); - println!("{:width2$} {} {}", "Requests per second".yellow(), format!("{:.2}", requests_per_second).purple(), "[#/sec]".purple(), width2 = 25); + println!("{:width2$} {} {}", "Requests per second".yellow(), format!("{requests_per_second:.2}").purple(), "[#/sec]".purple(), width2 = 25); println!("{:width2$} {}", "Median time per request".yellow(), format_time(global_stats.median_duration(), nanosec).purple(), width2 = 25); println!("{:width2$} {}", "Average time per request".yellow(), format_time(global_stats.mean_duration(), nanosec).purple(), width2 = 25); println!("{:width2$} {}", "Sample standard deviation".yellow(), format_time(global_stats.stdev_duration(), nanosec).purple(), width2 = 25); @@ -162,7 +184,7 @@ fn show_stats(list_reports: &[Vec], stats_option: bool, nanosec: bool, d fn compare_benchmark(list_reports: &[Vec], compare_path_option: Option<&str>, threshold_option: Option<&str>) { if let Some(compare_path) = compare_path_option { if let Some(threshold) = threshold_option { - let compare_result = checker::compare(&list_reports, compare_path, threshold); + let compare_result = checker::compare(list_reports, compare_path, threshold); match compare_result { Ok(_) => process::exit(0), diff --git a/src/reader.rs b/src/reader.rs index b2ab826..b1aa1c5 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -1,3 +1,4 @@ +use serde_yaml::{Mapping, Value}; use std::fs::File; use std::io::{prelude::*, BufReader}; use std::path::Path; @@ -8,37 +9,115 @@ pub fn read_file(filepath: &str) -> String { let display = path.display(); // Open the path in read-only mode, returns `io::Result` - let mut file = match File::open(&path) { - Err(why) => panic!("couldn't open {}: {}", display, why), + let mut file = match File::open(path) { + Err(why) => panic!("couldn't open {display}: {why}"), Ok(file) => file, }; // Read the file contents into a string, returns `io::Result` let mut content = String::new(); if let Err(why) = file.read_to_string(&mut content) { - panic!("couldn't read {}: {}", display, why); + panic!("couldn't read {display}: {why}"); } content } -pub fn read_file_as_yml(filepath: &str) -> yaml_rust::yaml::Array { +fn parse_yaml_content(content: &str) -> Vec { + // serde_yaml doesn't support multiple documents natively, so we split by "---\n" and parse each + let mut docs = Vec::new(); + let trimmed_content = content.trim(); + + // Handle multi-document YAML (separated by "---\n") + if trimmed_content.contains("\n---\n") || (trimmed_content.starts_with("---\n") && trimmed_content.matches("---\n").count() > 1) { + let parts: Vec<&str> = trimmed_content.split("---\n").collect(); + for doc_str in parts { + let trimmed = doc_str.trim(); + // Skip empty parts and parts that are only comments + if !trimmed.is_empty() && !trimmed.chars().all(|c| c == '#' || c.is_whitespace() || c == '\n') { + match serde_yaml::from_str::(trimmed) { + Ok(doc) => { + // Skip Null documents (which can result from comments-only content) + if !matches!(doc, Value::Null) { + docs.push(doc); + } + } + Err(e) => { + eprintln!("Error parsing YAML document: {e}"); + panic!("Failed to parse YAML: {e}"); + } + } + } + } + } + + // If no documents were found (empty file or no "---"), try parsing the whole content + if docs.is_empty() { + // Remove leading "---\n" if present for single-document files + let content_to_parse = trimmed_content.strip_prefix("---\n").unwrap_or(trimmed_content); + match serde_yaml::from_str::(content_to_parse.trim()) { + Ok(doc) => { + if !matches!(doc, Value::Null) { + docs.push(doc); + } + } + Err(e) => { + eprintln!("Error parsing YAML content: {e}"); + panic!("Failed to parse YAML: {e}"); + } + } + } + + // If still empty, return a single Null document to maintain compatibility + if docs.is_empty() { + docs.push(Value::Null); + } + + docs +} + +pub fn read_file_as_yml(filepath: &str) -> Vec { + let content = read_file(filepath); + parse_yaml_content(&content) +} + +#[cfg(test)] +pub fn read_file_as_yml_from_str(content: &str) -> Vec { + parse_yaml_content(content) +} + +pub fn read_yaml_doc_accessor<'a>(doc: &'a Value, accessor: Option<&str>) -> &'a Vec { + if let Some(accessor_id) = accessor { + match doc.get(accessor_id).and_then(|v| v.as_sequence()) { + Some(items) => items, + None => { + println!("Node missing on config: {accessor_id}"); + println!("Exiting."); + std::process::exit(1) + } + } + } else { + doc.as_sequence().unwrap_or_else(|| panic!("Expected document to be a sequence, got: {doc:?}")) + } +} + +pub fn read_file_as_yml_array(filepath: &str) -> Vec { let path = Path::new(filepath); let display = path.display(); - let file = match File::open(&path) { - Err(why) => panic!("couldn't open {}: {}", display, why), + let file = match File::open(path) { + Err(why) => panic!("couldn't open {display}: {why}"), Ok(file) => file, }; let reader = BufReader::new(file); - let mut items = yaml_rust::yaml::Array::new(); + let mut items = Vec::new(); for line in reader.lines() { match line { Ok(text) => { - items.push(yaml_rust::Yaml::String(text)); + items.push(Value::String(text)); } - Err(e) => println!("error parsing line: {:?}", e), + Err(e) => println!("error parsing line: {e:?}"), } } @@ -46,41 +125,41 @@ pub fn read_file_as_yml(filepath: &str) -> yaml_rust::yaml::Array { } // TODO: Try to split this fn into two -pub fn read_csv_file_as_yml(filepath: &str, quote: u8) -> yaml_rust::yaml::Array { +pub fn read_csv_file_as_yml(filepath: &str, quote: u8) -> Vec { // Create a path to the desired file let path = Path::new(filepath); let display = path.display(); // Open the path in read-only mode, returns `io::Result` - let file = match File::open(&path) { - Err(why) => panic!("couldn't open {}: {}", display, why), + let file = match File::open(path) { + Err(why) => panic!("couldn't open {display}: {why}"), Ok(file) => file, }; let mut rdr = csv::ReaderBuilder::new().has_headers(true).quote(quote).from_reader(file); - let mut items = yaml_rust::yaml::Array::new(); + let mut items = Vec::new(); let headers = match rdr.headers() { - Err(why) => panic!("error parsing header: {:?}", why), + Err(why) => panic!("error parsing header: {why:?}"), Ok(h) => h.clone(), }; for result in rdr.records() { match result { Ok(record) => { - let mut linked_hash_map = linked_hash_map::LinkedHashMap::new(); + let mut mapping = Mapping::new(); for (i, header) in headers.iter().enumerate() { - let item_key = yaml_rust::Yaml::String(header.to_string()); - let item_value = yaml_rust::Yaml::String(record.get(i).unwrap().to_string()); + let item_key = Value::String(header.to_string()); + let item_value = Value::String(record.get(i).unwrap().to_string()); - linked_hash_map.insert(item_key, item_value); + mapping.insert(item_key, item_value); } - items.push(yaml_rust::Yaml::Hash(linked_hash_map)); + items.push(Value::Mapping(mapping)); } - Err(e) => println!("error parsing header: {:?}", e), + Err(e) => println!("error parsing header: {e:?}"), } } diff --git a/src/tags.rs b/src/tags.rs new file mode 100644 index 0000000..7c28571 --- /dev/null +++ b/src/tags.rs @@ -0,0 +1,225 @@ +use crate::reader; +use colored::*; +use serde_yaml::Value; +use std::collections::HashSet; + +#[derive(Debug)] +pub struct Tags<'a> { + pub tags: Option>, + pub skip_tags: Option>, +} + +impl<'a> Tags<'a> { + pub fn new(tags_option: Option<&'a str>, skip_tags_option: Option<&'a str>) -> Self { + let tags: Option> = tags_option.map(|m| m.split(',').map(|s| s.trim()).collect()); + let skip_tags: Option> = skip_tags_option.map(|m| m.split(',').map(|s| s.trim()).collect()); + + if let (Some(t), Some(s)) = (&tags, &skip_tags) { + if !t.is_disjoint(s) { + panic!("`tags` and `skip-tags` must not contain the same values!"); + } + } + + Tags { + tags, + skip_tags, + } + } + + pub fn should_skip_item(&self, item: &Value) -> bool { + match item.get("tags").and_then(|v| v.as_sequence()) { + Some(item_tags_raw) => { + let item_tags: HashSet<&str> = item_tags_raw.iter().filter_map(|t| t.as_str()).collect(); + if let Some(s) = &self.skip_tags { + if !s.is_disjoint(&item_tags) { + return true; + } + } + if let Some(t) = &self.tags { + if item_tags.contains("never") && !t.contains("never") { + return true; + } + if !t.is_disjoint(&item_tags) { + return false; + } + } + if item_tags.contains("always") { + return false; + } + if item_tags.contains("never") { + return true; + } + self.tags.is_some() + } + None => self.tags.is_some(), + } + } +} + +pub fn list_benchmark_file_tasks(benchmark_file: &str, tags: &Tags) { + let docs = reader::read_file_as_yml(benchmark_file); + let items = reader::read_yaml_doc_accessor(&docs[0], Some("plan")); + + println!(); + + if let Some(tags) = &tags.tags { + let mut tags: Vec<_> = tags.iter().collect(); + tags.sort(); + println!("{:width$} {:width2$?}", "Tags".green(), &tags, width = 15, width2 = 25); + } + if let Some(tags) = &tags.skip_tags { + let mut tags: Vec<_> = tags.iter().collect(); + tags.sort(); + println!("{:width$} {:width2$?}", "Skip-Tags".green(), &tags, width = 15, width2 = 25); + } + + let items: Vec<_> = items.iter().filter(|item| !tags.should_skip_item(item)).collect(); + + if items.is_empty() { + println!("{}", "No items".red()); + std::process::exit(1) + } + + for item in items { + let out_str = serde_yaml::to_string(item).unwrap(); + println!("{out_str}"); + } +} + +pub fn list_benchmark_file_tags(benchmark_file: &str) { + let docs = reader::read_file_as_yml(benchmark_file); + let items = reader::read_yaml_doc_accessor(&docs[0], Some("plan")); + + println!(); + + if items.is_empty() { + println!("{}", "No items".red()); + std::process::exit(1) + } + let mut tags: HashSet<&str> = HashSet::new(); + for item in items { + if let Some(item_tags_raw) = item.get("tags").and_then(|v| v.as_sequence()) { + tags.extend(item_tags_raw.iter().filter_map(|t| t.as_str())); + } + } + + let mut tags: Vec<_> = tags.into_iter().collect(); + tags.sort_unstable(); + println!("{:width$} {:?}", "Tags".green(), &tags, width = 15); +} + +#[cfg(test)] +mod tests { + use super::*; + + fn str_to_yaml(text: &str) -> Value { + let docs = crate::reader::read_file_as_yml_from_str(text); + docs[0].clone() + } + + fn prepare_default_item() -> Value { + str_to_yaml("---\nname: foo\nrequest:\n url: /\ntags:\n - tag1\n - tag2") + } + + #[test] + #[should_panic] + fn same_tags_and_skip_tags() { + let _ = Tags::new(Some("tag1"), Some("tag1")); + } + + #[test] + fn empty_tags_both() { + let item = str_to_yaml("---\nname: foo\nrequest:\n url: /"); + let tags = Tags::new(None, None); + assert!(!tags.should_skip_item(&item)); + } + + #[test] + fn empty_tags() { + let tags = Tags::new(None, None); + assert!(!tags.should_skip_item(&prepare_default_item())); + } + + #[test] + fn tags_contains() { + let tags = Tags::new(Some("tag1"), None); + assert!(!tags.should_skip_item(&prepare_default_item())); + } + + #[test] + fn tags_contains_second() { + let tags = Tags::new(Some("tag2"), None); + assert!(!tags.should_skip_item(&prepare_default_item())); + } + + #[test] + fn tags_contains_both() { + let tags = Tags::new(Some("tag1,tag2"), None); + assert!(!tags.should_skip_item(&prepare_default_item())); + } + + #[test] + fn tags_not_contains() { + let tags = Tags::new(Some("tag99"), None); + assert!(tags.should_skip_item(&prepare_default_item())); + } + + #[test] + fn skip_tags_not_contains() { + let tags = Tags::new(None, Some("tag99")); + assert!(!tags.should_skip_item(&prepare_default_item())); + } + + #[test] + fn skip_tags_contains() { + let tags = Tags::new(None, Some("tag1")); + assert!(tags.should_skip_item(&prepare_default_item())); + } + + #[test] + fn skip_tags_contains_second() { + let tags = Tags::new(None, Some("tag2")); + assert!(tags.should_skip_item(&prepare_default_item())); + } + + #[test] + fn tags_contains_but_also_skip_tags_contains() { + let tags = Tags::new(Some("tag1"), Some("tag2")); + assert!(tags.should_skip_item(&prepare_default_item())); + } + + #[test] + fn never_skipped_by_default() { + let item = str_to_yaml("---\nname: foo\nrequest:\n url: /\ntags:\n - never\n - tag2"); + let tags = Tags::new(None, None); + assert!(tags.should_skip_item(&item)); + } + + #[test] + fn never_tag_skipped_even_when_other_tag_included() { + let item = str_to_yaml("---\nname: foo\nrequest:\n url: /\ntags:\n - never\n - tag2"); + let tags = Tags::new(Some("tag2"), None); + assert!(tags.should_skip_item(&item)); + } + + #[test] + fn include_never_tag() { + let item = str_to_yaml("---\nname: foo\nrequest:\n url: /\ntags:\n - never\n - tag2"); + let tags = Tags::new(Some("never"), None); + assert!(!tags.should_skip_item(&item)); + } + + #[test] + fn always_tag_included_by_default() { + let item = str_to_yaml("---\nname: foo\nrequest:\n url: /\ntags:\n - always\n - tag2"); + let tags = Tags::new(Some("tag99"), None); + assert!(!tags.should_skip_item(&item)); + } + + #[test] + fn skip_always_tag() { + let item = str_to_yaml("---\nname: foo\nrequest:\n url: /\ntags:\n - always\n - tag2"); + let tags = Tags::new(None, Some("always")); + assert!(tags.should_skip_item(&item)); + } +} diff --git a/src/writer.rs b/src/writer.rs index d7be9c8..ddde44e 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -6,12 +6,12 @@ pub fn write_file(filepath: &str, content: String) { let path = Path::new(filepath); let display = path.display(); - let mut file = match File::create(&path) { - Err(why) => panic!("couldn't create {}: {:?}", display, why), + let mut file = match File::create(path) { + Err(why) => panic!("couldn't create {display}: {why:?}"), Ok(file) => file, }; if let Err(why) = file.write_all(content.as_bytes()) { - panic!("couldn't write to {}: {:?}", display, why); + panic!("couldn't write to {display}: {why:?}"); } }