diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 16c749e3..39450633 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -14,16 +14,14 @@ concurrency: jobs: lint: name: Lint - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable with: - toolchain: stable - override: true + components: "clippy, rustfmt" # make sure all code has been formatted with rustfmt and linted with clippy - - run: rustup component add rustfmt clippy - name: rustfmt run: cargo fmt -- --check --color always @@ -36,14 +34,11 @@ jobs: name: Test strategy: matrix: - os: [ubuntu-20.04, windows-2022, macos-11] + os: [ubuntu-22.04, windows-2022, macos-11] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable - run: cargo fetch - name: cargo test build run: cargo build --tests @@ -51,6 +46,8 @@ jobs: # Run the tests we usually don't want to run when # testing locally - run: cargo test -- --ignored + # Don't run on macos, too slow and flaky + if: matrix.os != 'macos-11' # Verifies we can build aarch64-apple-darwin binaries until GHA actually has # runners for them that we can actually run tests on @@ -60,12 +57,10 @@ jobs: # Only run this PRs if: github.ref != 'refs/heads/main' steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable with: - toolchain: stable - override: true - - run: rustup target add aarch64-apple-darwin + targets: aarch64-apple-darwin - run: cargo fetch --target aarch64-apple-darwin - run: cargo build --release --target aarch64-apple-darwin @@ -74,20 +69,18 @@ jobs: strategy: matrix: include: - - os: ubuntu-20.04 + - os: ubuntu-22.04 target: x86_64-unknown-linux-musl runs-on: ${{ matrix.os }} env: TARGET: x86_64-unknown-linux-musl steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable with: - toolchain: stable - override: true target: ${{ matrix.target }} - name: Install musl tools - if: matrix.os == 'ubuntu-20.04' + if: matrix.target == 'x86_64-unknown-linux-musl' run: | sudo apt-get install -y musl-tools - name: cargo fetch @@ -109,7 +102,7 @@ jobs: name: Build the book runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - run: | set -e curl -L https://github.com/rust-lang-nursery/mdBook/releases/download/v0.3.1/mdbook-v0.3.1-x86_64-unknown-linux-gnu.tar.gz | tar xzf - @@ -122,26 +115,23 @@ jobs: publish-check: name: Publish Check - runs-on: ubuntu-20.04 + if: false + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable - run: cargo fetch - name: cargo publish run: cargo publish --dry-run msrv-check: name: Minimum Stable Rust Version Check - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: dtolnay/rust-toolchain@stable with: toolchain: "1.60.0" - override: true - run: cargo fetch - name: cargo check run: cargo check --all-targets @@ -153,7 +143,7 @@ jobs: strategy: matrix: include: - - os: ubuntu-20.04 + - os: ubuntu-22.04 rust: stable target: x86_64-unknown-linux-musl bin: cargo-deny @@ -171,18 +161,16 @@ jobs: bin: cargo-deny runs-on: ${{ matrix.os }} steps: + - uses: actions/checkout@v3 - name: Install stable toolchain - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - override: true target: ${{ matrix.target }} - name: Install musl tools - if: matrix.os == 'ubuntu-20.04' + if: matrix.target == 'x86_64-unknown-linux-musl' run: | sudo apt-get install -y musl-tools - - name: Checkout - uses: actions/checkout@v2 - name: cargo fetch run: cargo fetch --target ${{ matrix.target }} - name: Release build @@ -224,7 +212,7 @@ jobs: publish: name: Publish Docs needs: [doc-book] - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 if: github.event_name == 'push' && github.ref == 'refs/heads/main' steps: - name: Download book diff --git a/Cargo.lock b/Cargo.lock index 7ffc8c97..01f26d19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -219,6 +219,7 @@ dependencies = [ "fern", "git2", "home", + "insta", "krates", "log", "rayon", @@ -302,9 +303,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.10.3" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aacacf4d96c24b2ad6eb8ee6df040e4f27b0d0b39a5710c30091baa830485db" +checksum = "b0357a6402b295ca3a86bc148e84df46c02e41f41fef186bda662557ef6328aa" dependencies = [ "smallvec", "target-lexicon", @@ -402,6 +403,19 @@ dependencies = [ "libc", ] +[[package]] +name = "console" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c050367d967ced717c04b65d8c619d863ef9292ce0c5760028655a2fb298718c" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "terminal_size", + "winapi", +] + [[package]] name = "core-foundation" version = "0.9.3" @@ -586,6 +600,12 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "env_logger" version = "0.9.1" @@ -847,6 +867,19 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "insta" +version = "1.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581d4e3314cae4536e5d22ffd23189d4a374696c5ef733eadafae0ed273fd303" +dependencies = [ + "console", + "lazy_static", + "linked-hash-map", + "similar", + "yaml-rust", +] + [[package]] name = "instant" version = "0.1.12" @@ -883,8 +916,7 @@ dependencies = [ [[package]] name = "krates" version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a857d4b6450fbc2b85c03684b2beae51916a5a90e2fb5f9c2b8acc920ad29e49" +source = "git+https://github.com/EmbarkStudios/krates?branch=add-features#ad91555dd9f5718212a0acedcaa2676cd48ab191" dependencies = [ "cargo_metadata", "cfg-expr", @@ -969,6 +1001,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "log" version = "0.4.17" @@ -1447,6 +1485,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" +[[package]] +name = "similar" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ac7f900db32bf3fd12e0117dd3dc4da74bc52ebaac97f39668446d89694803" + [[package]] name = "sized-chunks" version = "0.6.5" @@ -1571,6 +1615,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "terminal_size" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "textwrap" version = "0.15.1" @@ -1855,6 +1909,15 @@ dependencies = [ "tap", ] +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "zstd" version = "0.11.2+zstd.1.5.2" diff --git a/Cargo.toml b/Cargo.toml index e2a82913..5974f470 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -98,5 +98,10 @@ twox-hash = { version = "1.5", default-features = false } url = "2.1" [dev-dependencies] +# Snapshot testing +insta = "1.21" # We use this for creating fake crate directories for crawling license files on disk tempfile = "3.1.0" + +[patch.crates-io] +krates = { git = "https://github.com/EmbarkStudios/krates", branch = "add-features" } diff --git a/deny.toml b/deny.toml index 124552fb..c2642ced 100644 --- a/deny.toml +++ b/deny.toml @@ -30,7 +30,9 @@ skip = [ [sources] unknown-registry = "deny" unknown-git = "deny" -allow-git = [] + +[sources.allow-org] +github = ["EmbarkStudios"] [licenses] unlicensed = "deny" diff --git a/docs/src/checks/bans/cfg.md b/docs/src/checks/bans/cfg.md index e88ac6cc..852f6773 100644 --- a/docs/src/checks/bans/cfg.md +++ b/docs/src/checks/bans/cfg.md @@ -77,3 +77,21 @@ Note that by default, the `depth` is infinite. ### The `allow-build-scripts` field (optional) Specifies all the crates that are allowed to have a build script. If this option is omitted, all crates are allowed to have a build script, and if this option is set to an empty list, no crate is allowed to have a build script. + +### The `deny-features` field (optional) + +If any of the denied features for a specific crate is used in the dependency graph, cargo-deny will deny it. + +**Note:** If this field is provided, cargo-deny will not ban the crate, unless it uses denied features. + +### The `allow-features` field (optional) + +A specific crate can only use the features provided in this config entry. If this is an empty set, it will have no effect. + +**Note:** If this field is provided, cargo-deny will not ban the crate, unless it uses non-allowed features. + +### The `exact-features` field (optional) + +Makes `allow-features` strict. If this is true, the feature set of the crate must be exactly the same as the `allow-features` set. + +**Note:** If this field is provided, cargo-deny will not ban the crate, unless the feature set doesn't match exactly. diff --git a/examples/11_feature_bans/Cargo.lock b/examples/11_feature_bans/Cargo.lock new file mode 100644 index 00000000..3d77ffac --- /dev/null +++ b/examples/11_feature_bans/Cargo.lock @@ -0,0 +1,806 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bumpalo" +version = "3.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "feature-bans" +version = "0.1.0" +dependencies = [ + "reqwest", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" + +[[package]] +name = "futures-sink" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" + +[[package]] +name = "futures-task" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" + +[[package]] +name = "futures-util" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "h2" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" +dependencies = [ + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c6392766afd7964e2531940894cffe4bd8d7d17dbc3c1c4857040fd4b33bdb3" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" + +[[package]] +name = "itoa" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" + +[[package]] +name = "js-sys" +version = "0.3.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mio" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "once_cell" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro2" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "reqwest" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-rustls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustls" +version = "0.20.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7522c9de787ff061458fe9a829dc790a3f5b22dc571694fc5883f448b94d9a9" +dependencies = [ + "base64", +] + +[[package]] +name = "ryu" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "serde" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" + +[[package]] +name = "serde_json" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "slab" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" + +[[package]] +name = "socket2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "syn" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" +dependencies = [ + "bytes", + "libc", + "memchr", + "mio", + "once_cell", + "pin-project-lite", + "socket2", + "winapi", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-util" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7709595b8878a4965ce5e87ebf880a7d39c9afc6837721b21a5a816a8117d921" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" + +[[package]] +name = "web-sys" +version = "0.3.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d8de8415c823c8abd270ad483c6feeac771fad964890779f9a8cb24fbbc1bf" +dependencies = [ + "webpki", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] diff --git a/examples/11_feature_bans/Cargo.toml b/examples/11_feature_bans/Cargo.toml new file mode 100644 index 00000000..ccb85a52 --- /dev/null +++ b/examples/11_feature_bans/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "feature-bans" +version = "0.1.0" +edition = "2021" + +[dependencies] +reqwest = { version = "=0.11.11", default-features = false, features = [ + "rustls-tls-webpki-roots", +] } + +[features] +foo = [] diff --git a/examples/11_feature_bans/deny.toml b/examples/11_feature_bans/deny.toml new file mode 100644 index 00000000..36824b9c --- /dev/null +++ b/examples/11_feature_bans/deny.toml @@ -0,0 +1,7 @@ +[bans] +multiple-versions = "allow" # We don't care about these for this example + +[[bans.deny]] +name = "reqwest" +#deny-features = ["json"] +allow-features = ["rustls", "__rustls", "__tls", "hyper-rustls", "rustls", "rustls-pemfile", "rustls-tls-webpki-roots", "tokio-rustls", "webpki-roots"] diff --git a/examples/11_feature_bans/src/lib.rs b/examples/11_feature_bans/src/lib.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/examples/11_feature_bans/src/lib.rs @@ -0,0 +1 @@ + diff --git a/src/advisories/helpers.rs b/src/advisories/helpers.rs index 4dd3e02c..588b5ff9 100644 --- a/src/advisories/helpers.rs +++ b/src/advisories/helpers.rs @@ -643,19 +643,19 @@ impl PrunedLockfile { #[inline] pub(crate) fn krate_for_pkg<'a>( krates: &'a Krates, - pkg: &rustsec::package::Package, + pkg: &'a rustsec::package::Package, ) -> Option<(krates::NodeId, &'a Krate)> { krates .krates_by_name(pkg.name.as_str()) - .find(|(_, kn)| { - pkg.version == kn.krate.version - && match (&pkg.source, &kn.krate.source) { + .find(|(_, krate)| { + pkg.version == krate.version + && match (&pkg.source, &krate.source) { (Some(psrc), Some(ksrc)) => psrc == ksrc, (None, None) => true, _ => false, } }) - .map(|(ind, krate)| (ind, &krate.krate)) + .map(|(ind, krate)| (ind, krate)) } pub use rustsec::{Warning, WarningKind}; diff --git a/src/bans.rs b/src/bans.rs index 1e25c6f4..f789ad5e 100644 --- a/src/bans.rs +++ b/src/bans.rs @@ -11,7 +11,7 @@ use anyhow::Error; use semver::VersionReq; use std::fmt; -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, Clone)] #[cfg_attr(test, derive(Debug))] pub struct KrateId { pub(crate) name: String, @@ -83,8 +83,8 @@ impl TreeSkipper { for krate in krates .krates_by_name(&ts.value.id.name) - .filter(|(_index, node)| { - crate::match_req(&node.krate.version, ts.value.id.version.as_ref()) + .filter(|(_index, krate)| { + crate::match_req(&krate.version, ts.value.id.version.as_ref()) }) { roots.push(Self::build_skip_root(ts.clone(), krate.0, krates)); @@ -120,12 +120,19 @@ impl TreeSkipper { let mut pending = vec![(krate_id, 1)]; while let Some((node_id, depth)) = pending.pop() { - let pkg_id = &krates[node_id].id; + let pkg_id = if let krates::Node::Krate { id, .. } = &graph[node_id] { + id + } else { + continue; + }; if let Err(i) = skip_crates.binary_search(pkg_id) { skip_crates.insert(i, pkg_id.clone()); if depth < max_depth { - for dep in graph.edges_directed(node_id, Direction::Outgoing) { + for dep in graph + .edges_directed(node_id, Direction::Outgoing) + .filter(|edge| !matches!(edge.weight(), krates::Edge::Feature)) + { pending.push((dep.target(), depth + 1)); } } @@ -200,8 +207,10 @@ pub fn check( sink.push(build_diags); } - let (denied_ids, ban_wrappers): (Vec<_>, Vec<_>) = - denied.into_iter().map(|kb| (kb.id, kb.wrappers)).unzip(); + let (denied_ids, ban_wrappers): (Vec<_>, Vec<_>) = denied + .iter() + .map(|kb| (kb.id.clone(), kb.wrappers.clone())) + .unzip(); // Keep track of all the crates we skip, and emit a warning if // we encounter a skip that didn't actually match any crate version @@ -214,7 +223,7 @@ pub fn check( } let mut multi_detector = MultiDetector { - name: &ctx.krates.krates().next().unwrap().krate.name, + name: &ctx.krates.krates().next().unwrap().name, dupes: smallvec::SmallVec::new(), }; @@ -280,7 +289,13 @@ pub fn check( } .into(); - diag.kids = kids.into_iter().map(|dupe| dupe.id).collect(); + diag.graph_nodes = kids + .into_iter() + .map(|dupe| crate::diag::GraphNode { + kid: dupe.id, + feature: None, + }) + .collect(); let mut pack = Pack::new(Check::Bans); pack.push(diag); @@ -310,7 +325,7 @@ pub fn check( } }; - for (i, krate) in ctx.krates.krates().map(|kn| &kn.krate).enumerate() { + for (i, krate) in ctx.krates.krates().enumerate() { let mut pack = Pack::with_kid(Check::Bans, krate.id.clone()); if let Some(matches) = matches(&denied_ids, krate) { @@ -323,8 +338,13 @@ pub fn check( // The crate is banned, but it might have be allowed if it's wrapped // by one or more particular crates let wrappers = ban_wrappers.get(rm.index); - let is_allowed = match wrappers { - Some(wrappers) => { + + // We can also ban specific features only, so a wrapper crate would + // not be required + let feature_bans = &denied[rm.index].features; + + let is_allowed_by_wrapper = match wrappers { + Some(wrappers) if !wrappers.is_empty() => { let nid = ctx.krates.nid_for_kid(&krate.id).unwrap(); let graph = ctx.krates.graph(); @@ -332,12 +352,18 @@ pub fn check( // on the banned crate is an allowed wrapper graph .edges_directed(nid, Direction::Incoming) - .map(|edge| edge.source()) + .filter_map(|edge| { + if let krates::Edge::Dep { .. } = edge.weight() { + Some(edge.source()) + } else { + None + } + }) .all(|nid| { - let node = &graph[nid]; + let src = &ctx.krates[nid]; let (diag, is_allowed): (Diag, _) = - match wrappers.iter().find(|aw| aw.value == node.krate.name) { + match wrappers.iter().find(|aw| aw.value == src.name) { Some(aw) => ( diags::BannedAllowedByWrapper { ban_cfg: ban_cfg.clone(), @@ -346,7 +372,7 @@ pub fn check( span: aw.span.clone(), }, banned_krate: krate, - wrapper_krate: &node.krate, + wrapper_krate: src, } .into(), true, @@ -355,7 +381,7 @@ pub fn check( diags::BannedUnmatchedWrapper { ban_cfg: ban_cfg.clone(), banned_krate: krate, - parent_krate: &node.krate, + parent_krate: src, } .into(), false, @@ -366,11 +392,118 @@ pub fn check( is_allowed }) } - None => false, + _ => feature_bans.is_some(), + }; + + // Ensure that the feature set of this krate, wherever it's used + // as a dependency, matches the ban entry. + let feature_set_allowed = if let Some(feature_bans) = feature_bans { + let enabled_features = ctx.krates.get_enabled_features(&krate.id).unwrap(); + + // Gather features that were present, but not explicitly allowed + let not_explicitly_allowed: Vec<_> = enabled_features + .iter() + .filter_map(|ef| { + if !feature_bans.allow.value.iter().any(|af| &af.value == ef) { + Some(ef.as_str()) + } else { + None + } + }) + .collect(); + + if feature_bans.exact.value { + // Gather features allowed, but not present + let missing_allowed: Vec<_> = feature_bans + .allow + .value + .iter() + .filter_map(|af| { + if !enabled_features.contains(&af.value) { + Some(CfgCoord { + file: file_id, + span: af.span.clone(), + }) + } else { + None + } + }) + .collect(); + + if missing_allowed.is_empty() && not_explicitly_allowed.is_empty() { + true + } else { + pack.push(diags::ExactFeaturesMismatch { + missing_allowed, + not_allowed: ¬_explicitly_allowed, + exact_coord: CfgCoord { + file: file_id, + span: feature_bans.exact.span.clone(), + }, + krate, + }); + false + } + } else { + // Mark the number of current diagnostics, if we add more + // the check has failed + let diag_count = pack.len(); + + // Add diagnostics if features were explicitly allowed, but weren't present + if !feature_bans.allow.value.is_empty() { + for feature in ¬_explicitly_allowed { + pack.push(diags::FeatureNotExplicitlyAllowed { + krate, + feature, + allowed: CfgCoord { + file: file_id, + span: feature_bans.allow.span.clone(), + }, + }); + } + } + + for feature in feature_bans + .deny + .iter() + .filter(|feat| enabled_features.contains(&feat.value)) + { + pack.push(diags::FeatureExplicitlyDenied { + krate, + feature, + file_id, + }); + } + + diag_count <= pack.len() + } + } else { + true }; - if !is_allowed { + if !is_allowed_by_wrapper || !feature_set_allowed { pack.push(diags::ExplicitlyBanned { krate, ban_cfg }); + } else if let Some(feature_bans) = &denied[rm.index].features { + // If the crate isn't actually banned, but does reference + // features that don't exist, emit warnings about them so + // the user can cleanup their config. We _could_ emit these + // warnings if the crate is banned, but feature graphs in + // particular can be massive and adding warnings into the mix + // will just make parsing the error graphs harder + for feature in feature_bans + .allow + .value + .iter() + .chain(feature_bans.deny.iter()) + { + if !krate.features.contains_key(&feature.value) { + pack.push(diags::UnknownFeature { + krate, + feature, + file_id, + }); + } + } } } } diff --git a/src/bans/cfg.rs b/src/bans/cfg.rs index 308ad621..5d9b83c5 100644 --- a/src/bans/cfg.rs +++ b/src/bans/cfg.rs @@ -19,6 +19,21 @@ pub struct CrateId { #[derive(Deserialize, Clone)] #[cfg_attr(test, derive(Debug, PartialEq, Eq))] #[serde(deny_unknown_fields)] +pub struct FeatureBans { + /// All features that are allowed to be used. + #[serde(default)] + pub allow: Spanned>>, + /// All features that are denied. + #[serde(default)] + pub deny: Vec>, + /// The actual feature set has to exactly match the `allow` set. + #[serde(default)] + pub exact: Spanned, +} + +#[derive(Deserialize, Clone)] +#[cfg_attr(test, derive(Debug, PartialEq, Eq))] +#[serde(rename_all = "kebab-case", deny_unknown_fields)] pub struct CrateBan { pub name: Spanned, pub version: Option, @@ -26,6 +41,8 @@ pub struct CrateBan { /// direct dependency #[serde(default)] pub wrappers: Vec>, + #[serde(default)] + pub features: Option, } #[derive(Deserialize, Clone)] @@ -136,40 +153,63 @@ impl crate::cfg::UnvalidatedConfig for Config { cb.name.span, ), wrappers: cb.wrappers, + features: cb.features, }) .collect(); let allowed: Vec<_> = self.allow.into_iter().map(from).collect(); let skipped: Vec<_> = self.skip.into_iter().map(from).collect(); - let mut add_diag = |first: (&Skrate, &str), second: (&Skrate, &str)| { - diags.push( - Diagnostic::error() - .with_message(format!( - "a crate was specified in both `{}` and `{}`", - second.1, first.1 - )) - .with_labels(vec![ - Label::secondary(cfg_file, first.0.span.clone()) - .with_message(format!("marked as `{}`", first.1)), - Label::secondary(cfg_file, second.0.span.clone()) - .with_message(format!("marked as `{}`", second.1)), - ]), - ); + let dupe_crate_diag = |first: (&Skrate, &str), second: (&Skrate, &str)| -> Diagnostic { + Diagnostic::error() + .with_message(format!( + "a crate was specified in both `{}` and `{}`", + second.1, first.1 + )) + .with_labels(vec![ + Label::secondary(cfg_file, first.0.span.clone()) + .with_message(format!("marked as `{}`", first.1)), + Label::secondary(cfg_file, second.0.span.clone()) + .with_message(format!("marked as `{}`", second.1)), + ]) + }; + + let dupe_feature_diag = |krate: &Skrate, + allow: &Spanned, + deny: &Spanned| + -> Diagnostic { + Diagnostic::error() + .with_message("a crate feature was specified as both allowed and denied") + .with_labels(vec![ + Label::primary(cfg_file, krate.span.clone()).with_message("crate ban entry"), + Label::secondary(cfg_file, allow.span.clone()) + .with_message("marked as `allow`"), + Label::secondary(cfg_file, deny.span.clone()).with_message("marked as `deny`"), + ]) }; for d in &denied { if let Some(dupe) = exact_match(&allowed, &d.id.value) { - add_diag((&d.id, "deny"), (dupe, "allow")); + diags.push(dupe_crate_diag((&d.id, "deny"), (dupe, "allow"))); } + if let Some(dupe) = exact_match(&skipped, &d.id.value) { - add_diag((&d.id, "deny"), (dupe, "skip")); + diags.push(dupe_crate_diag((&d.id, "deny"), (dupe, "skip"))); + } + + // Ensure that a feature isn't both allowed and denied + if let Some(fb) = &d.features { + for allowed in &fb.allow.value { + if let Some(denied) = fb.deny.iter().find(|df| df.value == allowed.value) { + diags.push(dupe_feature_diag(&d.id, allowed, denied)); + } + } } } for all in &allowed { if let Some(dupe) = exact_match(&skipped, &all.value) { - add_diag((all, "allow"), (dupe, "skip")); + diags.push(dupe_crate_diag((all, "allow"), (dupe, "skip"))); } } @@ -213,6 +253,7 @@ pub(crate) type Skrate = Spanned; pub(crate) struct KrateBan { pub id: Skrate, pub wrappers: Vec>, + pub features: Option, } pub struct ValidConfig { diff --git a/src/bans/diags.rs b/src/bans/diags.rs index 2d3f1e8d..63a12343 100644 --- a/src/bans/diags.rs +++ b/src/bans/diags.rs @@ -1,7 +1,9 @@ use crate::{ bans::KrateId, - diag::{CfgCoord, Check, Diag, Diagnostic, KrateCoord, Label, Pack, Severity}, - Krate, + diag::{ + CfgCoord, Check, Diag, Diagnostic, FileId, GraphNode, KrateCoord, Label, Pack, Severity, + }, + Krate, Spanned, }; pub(crate) struct ExplicitlyBanned<'a> { @@ -252,3 +254,154 @@ impl<'a> From> for Diag { .into() } } + +pub(crate) struct ExactFeaturesMismatch<'a> { + pub(crate) missing_allowed: Vec, + pub(crate) not_allowed: &'a [&'a str], + pub(crate) exact_coord: CfgCoord, + pub(crate) krate: &'a Krate, +} + +impl From> for Diag { + fn from(efm: ExactFeaturesMismatch<'_>) -> Self { + let mut labels = vec![efm + .exact_coord + .into_label() + .with_message("exact enabled here")]; + + labels.extend( + efm.missing_allowed + .into_iter() + .map(|ma| ma.into_label().with_message("allowed feature not present")), + ); + + let diag = Diagnostic::new(Severity::Error) + .with_message(format!( + "feature set for crate '{}' did not match exactly", + efm.krate + )) + .with_code("B013") + .with_labels(labels) + .with_notes( + efm.not_allowed + .iter() + .map(|na| format!("'{na}' feature was enabled but not explicitly allowed")) + .collect(), + ); + + let graph_nodes = if efm.not_allowed.is_empty() { + vec![GraphNode { + kid: efm.krate.id.clone(), + feature: None, + }] + } else { + efm.not_allowed + .iter() + .map(|feat| GraphNode { + kid: efm.krate.id.clone(), + feature: Some((*feat).to_owned()), + }) + .collect() + }; + + Diag { + diag, + graph_nodes: graph_nodes.into(), + extra: None, + with_features: true, + } + } +} + +pub(crate) struct FeatureNotExplicitlyAllowed<'a> { + pub(crate) krate: &'a Krate, + pub(crate) feature: &'a str, + pub(crate) allowed: CfgCoord, +} + +impl From> for Diag { + fn from(fna: FeatureNotExplicitlyAllowed<'_>) -> Diag { + let diag = Diagnostic::new(Severity::Error) + .with_message(format!( + "feature '{}' for crate '{}' was not explicitly allowed", + fna.feature, fna.krate, + )) + .with_code("B014") + .with_labels(vec![fna + .allowed + .into_label() + .with_message("allowed features")]); + + Diag { + diag, + graph_nodes: std::iter::once(GraphNode { + kid: fna.krate.id.clone(), + feature: Some(fna.feature.to_owned()), + }) + .collect(), + extra: None, + with_features: true, + } + } +} + +pub(crate) struct FeatureExplicitlyDenied<'a> { + pub(crate) krate: &'a Krate, + pub(crate) feature: &'a Spanned, + pub(crate) file_id: FileId, +} + +impl From> for Diag { + fn from(fed: FeatureExplicitlyDenied<'_>) -> Diag { + let diag = Diagnostic::new(Severity::Error) + .with_message(format!( + "feature '{}' for crate '{}' is explicitly denied", + fed.feature.value, fed.krate, + )) + .with_code("B015") + .with_labels(vec![Label::primary(fed.file_id, fed.feature.span.clone()) + .with_message("feature denied here")]); + + Diag { + diag, + graph_nodes: std::iter::once(GraphNode { + kid: fed.krate.id.clone(), + feature: Some(fed.feature.value.clone()), + }) + .collect(), + extra: None, + with_features: true, + } + } +} + +pub(crate) struct UnknownFeature<'a> { + pub(crate) krate: &'a Krate, + pub(crate) feature: &'a Spanned, + pub(crate) file_id: FileId, +} + +impl From> for Diag { + fn from(uf: UnknownFeature<'_>) -> Diag { + let diag = Diagnostic::new(Severity::Warning) + .with_message(format!( + "found unknown feature '{}' for crate '{}'", + uf.feature.value, uf.krate, + )) + .with_code("B016") + .with_labels(vec![ + Label::primary(uf.file_id, uf.feature.span.clone()).with_message("unknown feature") + ]); + + Diag { + diag, + graph_nodes: std::iter::once(GraphNode { + kid: uf.krate.id.clone(), + feature: None, + }) + .collect(), + extra: None, + with_features: false, + } + } +} diff --git a/src/bans/graph.rs b/src/bans/graph.rs index cbba128b..5e3e3f3d 100644 --- a/src/bans/graph.rs +++ b/src/bans/graph.rs @@ -102,12 +102,18 @@ pub(crate) fn create_graph( let tid = krates.nid_for_kid(pid).context("unable to find crate")?; for incoming in krates.graph().edges_directed(tid, pg::Direction::Incoming) { let parent = &krates[incoming.source()]; + let kind = if let krates::Edge::Dep { kind, .. } = incoming.weight() { + *kind + } else { + continue; + }; + if let Some(pindex) = node_map.get(&parent.id) { - graph.update_edge(*pindex, target, incoming.weight().kind); + graph.update_edge(*pindex, target, kind); } else { let pindex = graph.add_node(&parent.id); - graph.update_edge(pindex, target, incoming.weight().kind); + graph.update_edge(pindex, target, kind); node_map.insert(&parent.id, pindex); node_stack.push(&parent.id); @@ -235,9 +241,9 @@ pub(crate) fn create_graph( use std::fmt::Write; for (i, (name, ids)) in dupe_nodes.iter().enumerate() { - writeln!(output, "{}subgraph cluster_{} {{", INDENT, i)?; + writeln!(output, "{INDENT}subgraph cluster_{i} {{")?; - write!(output, "{}{}{{rank=same ", INDENT, INDENT)?; + write!(output, "{0}{0}{{rank=same ", INDENT)?; for nid in ids { write!(output, "{} ", nid.index())?; @@ -245,14 +251,10 @@ pub(crate) fn create_graph( writeln!( output, - "}}\n{}{}style=\"rounded{}\";\n{}{}label=\"{}\"\n{}}}", - INDENT, + "}}\n{0}{0}style=\"rounded{1}\";\n{0}{0}label=\"{2}\"\n{0}}}", INDENT, if name == &dup_name { ",filled" } else { "" }, - INDENT, - INDENT, name, - INDENT )?; } @@ -279,7 +281,7 @@ where // output all nodes for node in graph.node_references() { - write!(output, "{}{}", INDENT, graph.to_index(node.id()))?; + write!(output, "{INDENT}{}", graph.to_index(node.id()))?; let attrs = node_print(node); @@ -292,41 +294,30 @@ where write!(output, " [")?; if let Some(label) = attrs.label { - write!(output, "label=\"{}\"", label)?; + write!(output, "label=\"{label}\"")?; append = true; } if let Some(shape) = attrs.shape { - write!( - output, - "{}shape={:?}", - if append { ", " } else { "" }, - shape - )?; + write!(output, "{}shape={shape:?}", if append { ", " } else { "" },)?; append = true; } if let Some(style) = attrs.style { - write!( - output, - "{}style={:?}", - if append { ", " } else { "" }, - style - )?; + write!(output, "{}style={style:?}", if append { ", " } else { "" },)?; append = true; } if let Some(color) = attrs.color { - write!(output, "{}color={}", if append { ", " } else { "" }, color)?; + write!(output, "{}color={color}", if append { ", " } else { "" })?; append = true; } if let Some(color) = attrs.fill_color { write!( output, - "{}fillcolor={}", + "{}fillcolor={color}", if append { ", " } else { "" }, - color )?; } @@ -337,8 +328,7 @@ where for edge in graph.edge_references() { write!( output, - "{}{} -> {}", - INDENT, + "{INDENT}{} -> {}", graph.to_index(edge.source()), graph.to_index(edge.target()), )?; @@ -350,12 +340,12 @@ where let mut append = false; if let Some(label) = attrs.label { - write!(output, "label=\"{}\"", label)?; + write!(output, "label=\"{label}\"")?; append = true; } if let Some(color) = attrs.color { - write!(output, "{}color={}", if append { ", " } else { "" }, color)?; + write!(output, "{}color={color}", if append { ", " } else { "" })?; //append = true; } diff --git a/src/cargo-deny/check.rs b/src/cargo-deny/check.rs index ebfe8bab..04204dae 100644 --- a/src/cargo-deny/check.rs +++ b/src/cargo-deny/check.rs @@ -335,6 +335,13 @@ pub(crate) fn cmd( let audit_compatible_output = args.audit_compatible_output && log_ctx.format == crate::Format::Json; + let colorize = log_ctx.format == crate::Format::Human + && match log_ctx.color { + crate::Color::Auto => atty::is(atty::Stream::Stderr), + crate::Color::Always => true, + crate::Color::Never => false, + }; + rayon::scope(|s| { // Asynchronously displays messages sent from the checks s.spawn(|_| { @@ -359,6 +366,7 @@ pub(crate) fn cmd( krates, krate_spans: &krate_spans, serialize_extra, + colorize, }; s.spawn(move |_| { @@ -409,6 +417,7 @@ pub(crate) fn cmd( krates, krate_spans: &krate_spans, serialize_extra, + colorize, }; s.spawn(|_| { @@ -429,6 +438,7 @@ pub(crate) fn cmd( krates, krate_spans: &krate_spans, serialize_extra, + colorize, }; s.spawn(|_| { @@ -447,6 +457,7 @@ pub(crate) fn cmd( krates, krate_spans: &krate_spans, serialize_extra, + colorize, }; s.spawn(move |_| { diff --git a/src/cargo-deny/common.rs b/src/cargo-deny/common.rs index 92a7ad2f..16a6f20a 100644 --- a/src/cargo-deny/common.rs +++ b/src/cargo-deny/common.rs @@ -286,7 +286,7 @@ type CsDiag = codespan_reporting::diagnostic::Diagnostic; pub struct Human<'a> { stream: term::termcolor::StandardStream, - grapher: Option>, + grapher: Option>, config: term::Config, } @@ -306,7 +306,7 @@ impl StdioStream { pub struct Json<'a> { stream: StdioStream, - grapher: Option>, + grapher: Option>, } #[allow(clippy::large_enum_variant)] @@ -390,9 +390,12 @@ impl<'a, 'b> OutputLock<'a, 'b> { } if let Some(grapher) = &cfg.grapher { - for kid in diag.kids { - if let Ok(graph) = grapher.write_graph(&kid) { - diag.diag.notes.push(graph); + for gn in diag.graph_nodes { + if let Ok(graph) = + grapher.build_graph(&gn, if diag.with_features { 1 } else { 0 }) + { + let graph_text = diag::write_graph_as_text(&graph); + diag.diag.notes.push(graph_text); } } } @@ -444,7 +447,7 @@ impl<'a> DiagPrinter<'a> { Self { which: OutputFormat::Human(Human { stream, - grapher: krates.map(diag::TextGrapher::new), + grapher: krates.map(diag::InclusionGrapher::new), config: term::Config::default(), }), max_severity, @@ -453,7 +456,7 @@ impl<'a> DiagPrinter<'a> { crate::Format::Json => Self { which: OutputFormat::Json(Json { stream: StdioStream::Err(std::io::stderr()), - grapher: krates.map(diag::ObjectGrapher::new), + grapher: krates.map(diag::InclusionGrapher::new), }), max_severity, }, diff --git a/src/cfg.rs b/src/cfg.rs index e19c1fac..aef9eae1 100644 --- a/src/cfg.rs +++ b/src/cfg.rs @@ -16,6 +16,7 @@ pub(crate) const START: &str = "$__toml_private_start"; pub(crate) const END: &str = "$__toml_private_end"; pub(crate) const VALUE: &str = "$__toml_private_value"; +#[derive(Default)] pub struct Spanned { pub(crate) value: T, pub(crate) span: std::ops::Range, diff --git a/src/diag.rs b/src/diag.rs index 7d0735e6..411818c7 100644 --- a/src/diag.rs +++ b/src/diag.rs @@ -1,10 +1,8 @@ -mod obj_grapher; +mod grapher; mod sink; -mod text_grapher; -pub use obj_grapher::{cs_diag_to_json, diag_to_json, ObjectGrapher}; +pub use grapher::{cs_diag_to_json, diag_to_json, write_graph_as_text, InclusionGrapher}; pub use sink::ErrorSink; -pub use text_grapher::TextGrapher; use std::{collections::HashMap, ops::Range}; @@ -31,18 +29,25 @@ impl From for Severity { } } +pub struct GraphNode { + pub kid: Kid, + pub feature: Option, +} + pub struct Diag { pub diag: Diagnostic, - pub kids: smallvec::SmallVec<[Kid; 2]>, + pub graph_nodes: smallvec::SmallVec<[GraphNode; 2]>, pub extra: Option<(&'static str, serde_json::Value)>, + pub with_features: bool, } impl Diag { pub(crate) fn new(diag: Diagnostic) -> Self { Self { diag, - kids: smallvec::SmallVec::new(), + graph_nodes: smallvec::SmallVec::new(), extra: None, + with_features: false, } } } @@ -67,6 +72,7 @@ pub struct Pack { } impl Pack { + #[inline] pub(crate) fn new(check: Check) -> Self { Self { check, @@ -75,6 +81,7 @@ impl Pack { } } + #[inline] pub(crate) fn with_kid(check: Check, kid: Kid) -> Self { Self { check, @@ -83,11 +90,12 @@ impl Pack { } } + #[inline] pub(crate) fn push(&mut self, diag: impl Into) -> &mut Diag { let mut diag = diag.into(); - if diag.kids.is_empty() { + if diag.graph_nodes.is_empty() { if let Some(kid) = self.kid.take() { - diag.kids.push(kid); + diag.graph_nodes.push(GraphNode { kid, feature: None }); } } @@ -95,6 +103,12 @@ impl Pack { self.diags.last_mut().unwrap() } + #[inline] + pub(crate) fn len(&self) -> usize { + self.diags.len() + } + + #[inline] pub(crate) fn is_empty(&self) -> bool { self.diags.is_empty() } @@ -154,7 +168,7 @@ impl KrateSpans { let mut spans = Vec::with_capacity(krates.len()); let mut cargo_spans = RawCargoSpans::new(); - let mut krates: Vec<_> = krates.krates().map(|kn| &kn.krate).collect(); + let mut krates: Vec<_> = krates.krates().collect(); // [Krates::krates] guarantees the krates to be ordered by name but we // want the outputs of diagnostics to also be stable in regards to // their version, so we do an additional sort for that here. @@ -235,8 +249,7 @@ impl From for Label { } } -struct NodePrint<'a> { - krate: &'a crate::Krate, - id: krates::NodeId, - kind: &'static str, +struct NodePrint { + node: krates::NodeId, + edge: Option, } diff --git a/src/diag/grapher.rs b/src/diag/grapher.rs new file mode 100644 index 00000000..0dcbf7c7 --- /dev/null +++ b/src/diag/grapher.rs @@ -0,0 +1,368 @@ +use super::NodePrint; +use crate::{DepKind, Krates}; +use anyhow::Context; +use krates::{petgraph as pg, Edge, Node}; +use std::collections::HashSet; + +#[derive(serde::Serialize)] +pub struct GraphNode { + #[serde(flatten)] + inner: NodeInner, + #[serde(skip_serializing_if = "is_false")] + repeat: bool, + #[serde(skip_serializing_if = "is_empty")] + parents: Vec, +} + +#[derive(serde::Serialize)] +pub enum NodeInner { + Krate { + name: String, + version: semver::Version, + #[serde(skip_serializing_if = "Option::is_none")] + kind: Option<&'static str>, + }, + Feature { + crate_name: String, + name: String, + }, +} + +#[allow(clippy::trivially_copy_pass_by_ref)] +fn is_false(v: &bool) -> bool { + !v +} + +#[allow(clippy::ptr_arg)] +fn is_empty(v: &Vec) -> bool { + v.is_empty() +} + +/// Provides the `InclusionGrapher::write_graph` method which creates a reverse +/// dependency graph rooted at a specific node +pub struct InclusionGrapher<'a> { + krates: &'a Krates, +} + +impl<'a> InclusionGrapher<'a> { + pub fn new(krates: &'a Krates) -> Self { + Self { krates } + } + + /// Creates an inclusion graph rooted at the specified node. + pub fn build_graph( + &self, + id: &super::GraphNode, + max_feature_depth: usize, + ) -> anyhow::Result { + let mut visited = HashSet::new(); + + let (node_id, _node) = self + .krates + .get_node(&id.kid, id.feature.as_deref()) + .context("unable to find node")?; + + let np = NodePrint { + node: node_id, + edge: None, + }; + + let root = self.append_node(np, 0, max_feature_depth, &mut visited)?; + + // If the graph was rooted on a feature node, we want to use that as the + // root when building the graph, but want the actual crate the feature + // belongs to be the root of the graph the user sees + if id.feature.is_some() { + let (_id, root_krate) = self.krates.get_node(&id.kid, None).with_context(|| { + format!( + "graph was built but we were unable to find the node for {}", + id.kid + ) + })?; + + let inner = if let Node::Krate { krate, .. } = root_krate { + NodeInner::Krate { + name: krate.name.clone(), + version: krate.version.clone(), + kind: None, + } + } else { + anyhow::bail!("unable to find crate node for {}", id.kid); + }; + + Ok(GraphNode { + inner, + repeat: false, + parents: vec![root], + }) + } else { + Ok(root) + } + } + + fn make_node(&self, np: NodePrint) -> NodeInner { + match &self.krates.graph()[np.node] { + Node::Krate { krate, .. } => { + let kind = np.edge.and_then(|eid| match self.krates.graph()[eid] { + Edge::Dep { kind, .. } | Edge::DepFeature { kind, .. } => match kind { + DepKind::Normal => None, //Some("feature (normal)"), + DepKind::Dev => Some("dev"), + DepKind::Build => Some("build"), + }, + Edge::Feature => None, + }); + + NodeInner::Krate { + name: krate.name.clone(), + version: krate.version.clone(), + kind, + } + } + Node::Feature { name, krate_index } => { + let crate_name = + if let Node::Krate { krate, .. } = &self.krates.graph()[*krate_index] { + krate.name.clone() + } else { + "".to_owned() + }; + + NodeInner::Feature { + crate_name, + name: name.clone(), + } + } + } + } + + fn append_node( + &self, + np: NodePrint, + depth: usize, + max_feature_depth: usize, + visited: &mut HashSet, + ) -> anyhow::Result { + use pg::visit::EdgeRef; + + if !visited.insert(np.node) { + return Ok(GraphNode { + inner: self.make_node(np), + repeat: true, + parents: Vec::new(), + }); + } + + let mut node_parents = smallvec::SmallVec::<[NodePrint; 10]>::new(); + let graph = self.krates.graph(); + + if depth < max_feature_depth { + node_parents.extend(graph.edges_directed(np.node, pg::Direction::Incoming).map( + |edge| NodePrint { + node: edge.source(), + edge: Some(edge.id()), + }, + )); + } else { + // If we're not adding features we need to walk up any feature edges + // until we reach + let mut node_stack = vec![np.node]; + let mut visited_nodes = HashSet::new(); + + while let Some(node) = node_stack.pop() { + for edge in graph.edges_directed(node, pg::Direction::Incoming) { + if let Edge::Feature = edge.weight() { + if visited_nodes.insert(edge.source()) { + node_stack.push(edge.source()); + } + } else if !node_parents.iter().any(|np| np.node == edge.source()) { + node_parents.push(NodePrint { + node: edge.source(), + edge: Some(edge.id()), + }); + } + } + } + } + + let parents = if !node_parents.is_empty() { + // Resolve uses Hash data types internally but we want consistent output ordering + node_parents.sort_by(|a, b| match (&graph[a.node], &graph[b.node]) { + (Node::Krate { krate: a, .. }, Node::Krate { krate: b, .. }) => a.id.cmp(&b.id), + (Node::Krate { .. }, Node::Feature { .. }) => std::cmp::Ordering::Less, + (Node::Feature { .. }, Node::Krate { .. }) => std::cmp::Ordering::Greater, + (Node::Feature { name: a, .. }, Node::Feature { name: b, .. }) => a.cmp(b), + }); + + let mut parents = Vec::with_capacity(node_parents.len()); + + for parent in node_parents { + let pnode = self.append_node(parent, depth + 1, max_feature_depth, visited)?; + parents.push(pnode); + } + + parents + } else { + Vec::new() + }; + + Ok(GraphNode { + inner: self.make_node(np), + repeat: false, + parents, + }) + } +} + +use super::{Diag, FileId, Files, Severity}; + +pub type CsDiag = codespan_reporting::diagnostic::Diagnostic; + +pub fn cs_diag_to_json(diag: CsDiag, files: &Files) -> serde_json::Value { + let mut val = serde_json::json!({ + "type": "diagnostic", + "fields": { + "severity": match diag.severity { + Severity::Error => "error", + Severity::Warning => "warning", + Severity::Note => "note", + Severity::Help => "help", + Severity::Bug => "bug", + }, + "message": diag.message, + }, + }); + + { + let obj = val.as_object_mut().unwrap(); + let obj = obj.get_mut("fields").unwrap().as_object_mut().unwrap(); + + if let Some(code) = diag.code { + obj.insert("code".to_owned(), serde_json::Value::String(code)); + } + + if !diag.labels.is_empty() { + let mut labels = Vec::with_capacity(diag.labels.len()); + + for label in diag.labels { + let location = files + .location(label.file_id, label.range.start as u32) + .unwrap(); + labels.push(serde_json::json!({ + "message": label.message, + "span": files.source(label.file_id)[label.range].trim_matches('"'), + "line": location.line.to_usize() + 1, + "column": location.column.to_usize() + 1, + })); + } + + obj.insert("labels".to_owned(), serde_json::Value::Array(labels)); + } + + if !diag.notes.is_empty() { + obj.insert( + "notes".to_owned(), + serde_json::Value::Array( + diag.notes + .into_iter() + .map(serde_json::Value::String) + .collect(), + ), + ); + } + } + + val +} + +pub fn diag_to_json( + diag: Diag, + files: &Files, + grapher: Option<&InclusionGrapher<'_>>, +) -> serde_json::Value { + let mut to_print = cs_diag_to_json(diag.diag, files); + + let obj = to_print.as_object_mut().unwrap(); + let fields = obj.get_mut("fields").unwrap().as_object_mut().unwrap(); + + if let Some(grapher) = &grapher { + let mut graphs = Vec::new(); + for gn in diag.graph_nodes { + if let Ok(graph) = + grapher.build_graph(&gn, if diag.with_features { usize::MAX } else { 0 }) + { + if let Ok(sgraph) = serde_json::value::to_value(graph) { + graphs.push(sgraph); + } + } + } + + fields.insert("graphs".to_owned(), serde_json::Value::Array(graphs)); + } + + if let Some((key, val)) = diag.extra { + fields.insert(key.to_owned(), val); + } + + to_print +} + +pub fn write_graph_as_text(root: &GraphNode) -> String { + use std::fmt::Write; + + const DWN: char = '│'; + const TEE: char = '├'; + const ELL: char = 'â””'; + const RGT: char = '─'; + + let mut out = String::with_capacity(256); + let mut levels = smallvec::SmallVec::<[bool; 10]>::new(); + + fn write( + node: &GraphNode, + out: &mut String, + levels_continue: &mut smallvec::SmallVec<[bool; 10]>, + ) { + let star = if !node.repeat { "" } else { " (*)" }; + + if let Some((&last_continues, rest)) = levels_continue.split_last() { + for &continues in rest { + let c = if continues { DWN } else { ' ' }; + write!(out, "{c} ").unwrap(); + } + + let c = if last_continues { TEE } else { ELL }; + write!(out, "{c}{0}{0} ", RGT).unwrap(); + } + + match &node.inner { + NodeInner::Krate { + name, + version, + kind, + } => { + if let Some(kind) = kind { + write!(out, "({kind}) ").unwrap(); + } + + writeln!(out, "{name} v{version}{star}").unwrap(); + } + NodeInner::Feature { crate_name, name } => { + writeln!(out, "{crate_name} feature '{name}' {star}").unwrap(); + } + } + + if node.parents.is_empty() { + return; + } + + let cont = node.parents.len() - 1; + + for (i, parent) in node.parents.iter().enumerate() { + levels_continue.push(i < cont); + write(parent, out, levels_continue); + levels_continue.pop(); + } + } + + write(root, &mut out, &mut levels); + out +} diff --git a/src/diag/obj_grapher.rs b/src/diag/obj_grapher.rs deleted file mode 100644 index 0000b034..00000000 --- a/src/diag/obj_grapher.rs +++ /dev/null @@ -1,203 +0,0 @@ -use super::NodePrint; -use crate::{DepKind, Kid, Krates}; -use anyhow::{Context, Error}; -use krates::petgraph as pg; -use std::collections::HashSet; - -#[derive(serde::Serialize)] -pub struct GraphNode { - name: String, - version: semver::Version, - #[serde(skip_serializing_if = "is_normal")] - kind: &'static str, - #[serde(skip_serializing_if = "is_false")] - repeat: bool, - #[serde(skip_serializing_if = "is_empty")] - parents: Vec, -} - -#[allow(clippy::trivially_copy_pass_by_ref)] -fn is_false(v: &bool) -> bool { - !v -} - -fn is_normal(v: &'static str) -> bool { - v.is_empty() -} - -#[allow(clippy::ptr_arg)] -fn is_empty(v: &Vec) -> bool { - v.is_empty() -} - -/// As with the textgrapher, only crates inclusion graphs, but in the form of -/// a serializable object rather than a text string -pub struct ObjectGrapher<'a> { - krates: &'a Krates, -} - -impl<'a> ObjectGrapher<'a> { - pub fn new(krates: &'a Krates) -> Self { - Self { krates } - } - - pub fn write_graph(&self, id: &Kid) -> Result { - let mut visited = HashSet::new(); - - let node_id = self.krates.nid_for_kid(id).context("unable to find node")?; - let krate = &self.krates[node_id]; - - let np = NodePrint { - krate, - id: node_id, - kind: "", - }; - - self.write_parent(np, &mut visited) - } - - fn write_parent( - &self, - np: NodePrint<'a>, - visited: &mut HashSet, - ) -> Result { - use pg::visit::EdgeRef; - - let repeat = !visited.insert(np.id); - - let mut node = GraphNode { - name: np.krate.name.clone(), - version: np.krate.version.clone(), - kind: np.kind, - repeat, - parents: Vec::new(), - }; - - if repeat { - return Ok(node); - } - - let mut parents = smallvec::SmallVec::<[NodePrint<'a>; 10]>::new(); - let graph = self.krates.graph(); - for edge in graph.edges_directed(np.id, pg::Direction::Incoming) { - let parent_id = edge.source(); - let parent = &graph[parent_id]; - - let kind = match edge.weight().kind { - DepKind::Normal => "", - DepKind::Dev => "dev", - DepKind::Build => "build", - }; - - parents.push(NodePrint { - krate: &parent.krate, - id: parent_id, - kind, - }); - } - - if !parents.is_empty() { - // Resolve uses Hash data types internally but we want consistent output ordering - parents.sort_by_key(|n| &n.krate.id); - node.parents.reserve(parents.len()); - - for parent in parents { - let pnode = self.write_parent(parent, visited)?; - node.parents.push(pnode); - } - } - - Ok(node) - } -} - -use super::{Diag, FileId, Files, Severity}; - -pub type CsDiag = codespan_reporting::diagnostic::Diagnostic; - -pub fn cs_diag_to_json(diag: CsDiag, files: &Files) -> serde_json::Value { - let mut val = serde_json::json!({ - "type": "diagnostic", - "fields": { - "severity": match diag.severity { - Severity::Error => "error", - Severity::Warning => "warning", - Severity::Note => "note", - Severity::Help => "help", - Severity::Bug => "bug", - }, - "message": diag.message, - }, - }); - - { - let obj = val.as_object_mut().unwrap(); - let obj = obj.get_mut("fields").unwrap().as_object_mut().unwrap(); - - if let Some(code) = diag.code { - obj.insert("code".to_owned(), serde_json::Value::String(code)); - } - - if !diag.labels.is_empty() { - let mut labels = Vec::with_capacity(diag.labels.len()); - - for label in diag.labels { - let location = files - .location(label.file_id, label.range.start as u32) - .unwrap(); - labels.push(serde_json::json!({ - "message": label.message, - "span": files.source(label.file_id)[label.range].trim_matches('"'), - "line": location.line.to_usize() + 1, - "column": location.column.to_usize() + 1, - })); - } - - obj.insert("labels".to_owned(), serde_json::Value::Array(labels)); - } - - if !diag.notes.is_empty() { - obj.insert( - "notes".to_owned(), - serde_json::Value::Array( - diag.notes - .into_iter() - .map(serde_json::Value::String) - .collect(), - ), - ); - } - } - - val -} - -pub fn diag_to_json( - diag: Diag, - files: &Files, - grapher: Option<&ObjectGrapher<'_>>, -) -> serde_json::Value { - let mut to_print = cs_diag_to_json(diag.diag, files); - - let obj = to_print.as_object_mut().unwrap(); - let fields = obj.get_mut("fields").unwrap().as_object_mut().unwrap(); - - if let Some(grapher) = &grapher { - let mut graphs = Vec::new(); - for kid in diag.kids { - if let Ok(graph) = grapher.write_graph(&kid) { - if let Ok(sgraph) = serde_json::value::to_value(graph) { - graphs.push(sgraph); - } - } - } - - fields.insert("graphs".to_owned(), serde_json::Value::Array(graphs)); - } - - if let Some((key, val)) = diag.extra { - fields.insert(key.to_owned(), val); - } - - to_print -} diff --git a/src/diag/text_grapher.rs b/src/diag/text_grapher.rs deleted file mode 100644 index 2cabcdce..00000000 --- a/src/diag/text_grapher.rs +++ /dev/null @@ -1,126 +0,0 @@ -use super::NodePrint; -use crate::{DepKind, Kid, Krates}; -use anyhow::{Context, Error}; -use krates::petgraph as pg; -use std::collections::HashSet; - -/// Simplified copy of what cargo tree does to display dependency graphs. -/// In our case, we only care about the inverted form, ie, not what the -/// dependencies of a package are, but rather how a particular package -/// is actually pulled in via 1 or more root crates -pub struct TextGrapher<'a> { - krates: &'a Krates, -} - -const DWN: char = '│'; -const TEE: char = '├'; -const ELL: char = 'â””'; -const RGT: char = '─'; - -impl<'a> TextGrapher<'a> { - pub fn new(krates: &'a Krates) -> Self { - Self { krates } - } - - pub fn write_graph(&self, id: &Kid) -> Result { - let mut out = String::with_capacity(1024); - let mut levels = Vec::new(); - let mut visited = HashSet::new(); - - let node_id = self.krates.nid_for_kid(id).context("unable to find node")?; - let krate = &self.krates[node_id]; - - let np = NodePrint { - krate, - id: node_id, - kind: "", - }; - - self.write_parent(np, &mut out, &mut visited, &mut levels)?; - - Ok(out) - } - - #[allow(clippy::ptr_arg)] - fn write_parent( - &self, - np: NodePrint<'a>, - out: &mut String, - visited: &mut HashSet, - levels_continue: &mut Vec, - ) -> Result<(), Error> { - use pg::visit::EdgeRef; - use std::fmt::Write; - - let new = visited.insert(np.id); - let star = if new { "" } else { " (*)" }; - - if let Some((&last_continues, rest)) = levels_continue.split_last() { - for &continues in rest { - let c = if continues { DWN } else { ' ' }; - write!(out, "{} ", c)?; - } - - let c = if last_continues { TEE } else { ELL }; - write!(out, "{0}{1}{1} ", c, RGT)?; - } - - match np.kind { - "" => writeln!(out, "{} v{}{}", np.krate.name, np.krate.version, star), - kind => writeln!( - out, - "({}) {} v{}{}", - kind, np.krate.name, np.krate.version, star - ), - }?; - - if !new { - return Ok(()); - } - - let mut parents = smallvec::SmallVec::<[NodePrint<'a>; 10]>::new(); - let graph = self.krates.graph(); - for edge in graph.edges_directed(np.id, pg::Direction::Incoming) { - let parent_id = edge.source(); - let parent = &graph[parent_id]; - - let kind = match edge.weight().kind { - DepKind::Normal => "", - DepKind::Dev => "dev", - DepKind::Build => "build", - }; - - parents.push(NodePrint { - krate: &parent.krate, - id: parent_id, - kind, - }); - } - - if !parents.is_empty() { - // Resolve uses Hash data types internally but we want consistent output ordering - parents.sort_by_key(|n| &n.krate.id); - self.write_parents(parents, out, visited, levels_continue)?; - } - - Ok(()) - } - - fn write_parents( - &self, - parents: smallvec::SmallVec<[NodePrint<'a>; 10]>, - out: &mut String, - visited: &mut HashSet, - levels_continue: &mut Vec, - ) -> Result<(), Error> { - let cont = parents.len() - 1; - - for (i, parent) in parents.into_iter().enumerate() { - levels_continue.push(i < cont); - self.write_parent(parent, out, visited, levels_continue)?; - levels_continue.pop(); - } - - Ok(()) - } -} diff --git a/src/lib.rs b/src/lib.rs index 135433d3..643ee749 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,9 @@ pub mod diag; pub mod licenses; pub mod sources; +#[doc(hidden)] +pub mod test_utils; + pub use cfg::{Spanned, UnvalidatedConfig}; use krates::cm; pub use krates::{DepKind, Kid, Utf8PathBuf}; @@ -241,6 +244,8 @@ pub struct CheckCtx<'ctx, T> { /// Requests for additional information the check can provide to be /// serialized to the diagnostic pub serialize_extra: bool, + /// Allows for ANSI colorization of diagnostic content + pub colorize: bool, } /// Checks if a version satisfies the specifies the specified version requirement. diff --git a/src/licenses/gather.rs b/src/licenses/gather.rs index 16e85901..8b147d68 100644 --- a/src/licenses/gather.rs +++ b/src/licenses/gather.rs @@ -485,9 +485,7 @@ impl Gatherer { summary.nfos = krates .krates() .par_bridge() - .map(|kn| { - let krate = &kn.krate; - + .map(|krate| { // Attempt an SPDX expression that we can validate the user's acceptable // license terms with let mut synth_id = None; diff --git a/src/sources.rs b/src/sources.rs index a8598eed..c3ec34fb 100644 --- a/src/sources.rs +++ b/src/sources.rs @@ -35,7 +35,7 @@ pub fn check(ctx: crate::CheckCtx<'_, ValidConfig>, mut sink: ErrorSink) { ) }); - for (i, krate) in ctx.krates.krates().map(|kn| &kn.krate).enumerate() { + for (i, krate) in ctx.krates.krates().enumerate() { let source = match &krate.source { Some(source) => source, None => continue, diff --git a/src/test_utils.rs b/src/test_utils.rs new file mode 100644 index 00000000..44394535 --- /dev/null +++ b/src/test_utils.rs @@ -0,0 +1,184 @@ +use crate::{ + diag::{self, CargoSpans, ErrorSink, Files, KrateSpans}, + CheckCtx, +}; +use anyhow::Context as _; + +#[derive(Default)] +pub struct KrateGather<'k> { + pub name: &'k str, + pub features: &'k [&'k str], + pub all_features: bool, + pub no_default_features: bool, +} + +impl<'k> KrateGather<'k> { + pub fn new(name: &'k str) -> Self { + Self { + name, + ..Default::default() + } + } +} + +pub enum KratesSrc { + Provided(crate::Krates), + Cmd(krates::Cmd), +} + +impl From for KratesSrc { + fn from(krates: crate::Krates) -> Self { + Self::Provided(krates) + } +} + +impl<'k> From> for KratesSrc { + fn from(kg: KrateGather<'k>) -> Self { + let mut project_dir = std::path::PathBuf::from("./tests/test_data"); + project_dir.push(kg.name); + + let mut cmd = krates::Cmd::new(); + cmd.current_dir(project_dir); + + if kg.all_features { + cmd.all_features(); + } + + if kg.no_default_features { + cmd.no_default_features(); + } + + if !kg.features.is_empty() { + cmd.features(kg.features.iter().map(|f| (*f).to_owned())); + } + + Self::Cmd(cmd) + } +} + +pub fn gather_diagnostics( + ks: KS, + test_name: &str, + cfg: Option<&str>, + targets: Option<&[&str]>, + runner: R, +) -> anyhow::Result> +where + C: serde::de::DeserializeOwned + Default + crate::UnvalidatedConfig, + VC: Send, + R: FnOnce(CheckCtx<'_, VC>, CargoSpans, ErrorSink) + Send, + KS: Into, +{ + let krates = match ks.into() { + KratesSrc::Provided(krates) => krates, + KratesSrc::Cmd(cmd) => { + let mut kb = krates::Builder::new(); + + if let Some(targets) = targets { + kb.include_targets(targets.iter().map(|t| (t, vec![]))); + } + + kb.build(cmd, krates::NoneFilter) + .context("failed to build crate graph")? + } + }; + + let (spans, content, hashmap) = KrateSpans::synthesize(&krates); + let mut files = Files::new(); + + let spans_id = files.add(format!("{test_name}/Cargo.lock"), content); + + let spans = KrateSpans::with_spans(spans, spans_id); + + let (config, cfg_id) = match cfg { + Some(cfg) => { + let config: C = toml::from_str(cfg).context("failed to deserialize test config")?; + + let cfg_id = files.add(format!("{test_name}.toml"), cfg.to_owned()); + + (config, cfg_id) + } + None => ( + C::default(), + files.add(format!("{test_name}.toml"), "".to_owned()), + ), + }; + + let mut newmap = CargoSpans::new(); + for (key, val) in hashmap { + let cargo_id = files.add(val.0, val.1); + newmap.insert(key, (cargo_id, val.2)); + } + + let mut cfg_diags = Vec::new(); + let cfg = config.validate(cfg_id, &mut cfg_diags); + + if cfg_diags + .iter() + .any(|d| d.severity >= crate::diag::Severity::Error) + { + anyhow::bail!("encountered errors validating config: {cfg_diags:#?}"); + } + + let (tx, rx) = crossbeam::channel::unbounded(); + + let grapher = diag::InclusionGrapher::new(&krates); + + let (_, gathered) = rayon::join( + || { + let ctx = crate::CheckCtx { + krates: &krates, + krate_spans: &spans, + cfg, + serialize_extra: true, + colorize: false, + }; + runner(ctx, newmap, ErrorSink::Channel(tx)); + }, + || { + let mut diagnostics = Vec::new(); + + const TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10); + + let trx = crossbeam::channel::after(TIMEOUT); + loop { + crossbeam::select! { + recv(rx) -> msg => { + if let Ok(pack) = msg { + diagnostics.extend(pack.into_iter().map(|d| diag::diag_to_json(d, &files, Some(&grapher)))); + } else { + // Yay, the sender was dopped (i.e. check was finished) + break; + } + } + recv(trx) -> _ => { + anyhow::bail!("Timed out after {TIMEOUT:?}"); + } + } + } + + Ok(diagnostics) + }, + ); + + gathered +} + +#[inline] +pub fn to_snapshot(diags: Vec) -> String { + serde_json::to_string_pretty(&diags).unwrap() +} + +#[macro_export] +macro_rules! field_eq { + ($obj:expr, $field:expr, $expected:expr) => { + $obj.pointer($field) == Some(&serde_json::json!($expected)) + }; +} + +#[macro_export] +macro_rules! assert_field_eq { + ($obj:expr, $field:expr, $expected:expr) => { + assert_eq!($obj.pointer($field), Some(&serde_json::json!($expected))); + }; +} diff --git a/tests/advisories.rs b/tests/advisories.rs index cf2e1061..1f8847cc 100644 --- a/tests/advisories.rs +++ b/tests/advisories.rs @@ -1,9 +1,9 @@ use cargo_deny::{ advisories::{self, cfg}, + assert_field_eq, field_eq, + test_utils::{self as tu}, Krates, }; -#[macro_use] -mod utils; struct TestCtx { dbs: advisories::DbSet, @@ -63,7 +63,7 @@ fn detects_vulnerabilities() { let cfg = "vulnerability = 'deny'"; - let diags = utils::gather_diagnostics::( + let diags = tu::gather_diagnostics::( krates, "detects_vulnerabilities", Some(cfg), @@ -143,7 +143,7 @@ fn detects_unmaintained() { let cfg = "unmaintained = 'warn'"; - let diags = utils::gather_diagnostics::( + let diags = tu::gather_diagnostics::( krates, "detects_unmaintained", Some(cfg), @@ -182,7 +182,7 @@ fn detects_unsound() { let cfg = "unsound = 'warn'"; - let diags = utils::gather_diagnostics::( + let diags = tu::gather_diagnostics::( krates, "detects_unsound", Some(cfg), @@ -227,7 +227,7 @@ fn downgrades_lint_levels() { let cfg = "unmaintained = 'warn' ignore = ['RUSTSEC-2016-0004', 'RUSTSEC-2019-0001']"; - let diags = utils::gather_diagnostics::( + let diags = tu::gather_diagnostics::( krates, "downgrades_lint_levels", Some(cfg), @@ -273,7 +273,7 @@ fn detects_yanked() { vulnerability = 'allow' "; - let diags = utils::gather_diagnostics::( + let diags = tu::gather_diagnostics::( krates, "detects_yanked", Some(cfg), @@ -315,7 +315,7 @@ fn warns_on_ignored_and_withdrawn() { ignore = ['RUSTSEC-2020-0053'] "; - let diags = utils::gather_diagnostics::( + let diags = tu::gather_diagnostics::( krates, "warns_on_ignored_and_withdrawn", Some(cfg), diff --git a/tests/bans.rs b/tests/bans.rs index 26447152..222c9c97 100644 --- a/tests/bans.rs +++ b/tests/bans.rs @@ -1,16 +1,18 @@ -use cargo_deny::bans::{self, cfg}; - -#[macro_use] -mod utils; - -// Covers issue https://github.com/EmbarkStudios/cargo-deny/issues/184 +use cargo_deny::{ + assert_field_eq, + bans::{self, cfg}, + field_eq, + test_utils::{self as tu, KrateGather}, +}; + +/// Covers issue #[test] fn cyclic_dependencies_do_not_cause_infinite_loop() { - utils::gather_diagnostics::( - utils::get_test_data_krates("cyclic_dependencies").unwrap(), + tu::gather_diagnostics::( + KrateGather::new("cyclic_dependencies"), "cyclic_dependencies_do_not_cause_infinite_loop", None, - Some(std::time::Duration::from_millis(10000)), + None, |ctx, cs, tx| { bans::check(ctx, None, cs, tx); }, @@ -20,8 +22,8 @@ fn cyclic_dependencies_do_not_cause_infinite_loop() { #[test] fn allow_wrappers() { - let diags = utils::gather_diagnostics::( - utils::get_test_data_krates("allow_wrappers/maincrate").unwrap(), + let diags = tu::gather_diagnostics::( + KrateGather::new("allow_wrappers/maincrate"), "allow_wrappers", Some( r#" @@ -55,8 +57,8 @@ wrappers = ["safe-wrapper"] #[test] fn disallows_denied() { - let diags = utils::gather_diagnostics::( - utils::get_test_data_krates("allow_wrappers/maincrate").unwrap(), + let diags = tu::gather_diagnostics::( + KrateGather::new("allow_wrappers/maincrate"), "disallows_denied", Some( r#" @@ -87,11 +89,11 @@ name = "dangerous-dep" #[test] fn deny_wildcards() { - let diags = utils::gather_diagnostics::( - utils::get_test_data_krates("wildcards/maincrate").unwrap(), + let diags = tu::gather_diagnostics::( + KrateGather::new("wildcards/maincrate"), "deny_wildcards", Some("wildcards = 'deny'"), - Some(std::time::Duration::from_millis(10000)), + None, |ctx, cs, tx| { bans::check(ctx, None, cs, tx); }, @@ -107,57 +109,29 @@ fn deny_wildcards() { && field_eq!( v, "/fields/message", - format!("found 1 wildcard dependency for crate '{}'", exp) + format!("found 1 wildcard dependency for crate '{exp}'") ) }), - "unable to find error diagnostic for '{}'", - exp + "unable to find error diagnostic for '{exp}'" ); } } +/// Ensures that multiple versions are always deterministically sorted by +/// version number +/// See #[test] fn deterministic_duplicate_ordering() { - let diags = utils::gather_diagnostics::( - utils::get_test_data_krates("duplicates").unwrap(), + let diags = tu::gather_diagnostics::( + KrateGather::new("duplicates"), "deterministic_duplicate_ordering", Some("multiple-versions = 'deny'"), - Some(std::time::Duration::from_millis(10000)), + None, |ctx, cs, tx| { bans::check(ctx, None, cs, tx); }, ) .unwrap(); - let duplicates = [ - ("block-buffer", &["0.7.3", "0.10.2"]), - ("digest", &["0.8.1", "0.10.3"]), - ("generic-array", &["0.12.4", "0.14.5"]), - ]; - - for dup in &duplicates { - assert!( - diags.iter().any(|v| { - if !field_eq!(v, "/fields/severity", "error") - || !field_eq!( - v, - "/fields/message", - format!("found 2 duplicate entries for crate '{}'", dup.0) - ) - { - return false; - } - - for (i, version) in dup.1.iter().enumerate() { - if !field_eq!(v, &format!("/fields/graphs/{}/version", i), version) { - return false; - } - } - - true - }), - "unable to find error diagnostic for duplicate '{}'", - dup.0 - ); - } + insta::assert_snapshot!(tu::to_snapshot(diags)); } diff --git a/tests/feature_bans.rs b/tests/feature_bans.rs new file mode 100644 index 00000000..c7811d36 --- /dev/null +++ b/tests/feature_bans.rs @@ -0,0 +1,84 @@ +use cargo_deny::{ + bans::{self, cfg}, + test_utils::{self as tu, KrateGather}, +}; + +/// Ensures that you can ban features in your own workspace. `simple` is brought +/// in via the default features +#[test] +fn bans_workspace_features() { + let diags = tu::gather_diagnostics::( + KrateGather::new("features-galore"), + "bans_workspace_features", + Some("deny = [{ name = 'features-galore', features.deny = ['simple'] }]"), + Some(&["x86_64-unknown-linux-gnu"]), + |ctx, cs, tx| { + bans::check(ctx, None, cs, tx); + }, + ) + .unwrap(); + + insta::assert_snapshot!(tu::to_snapshot(diags)); +} + +/// Ensures non-workspace features are banned +#[test] +fn bans_external_features() { + let diags = tu::gather_diagnostics::( + KrateGather { + name: "features-galore", + features: &["zlib", "ssh"], + no_default_features: true, + ..Default::default() + }, + "bans_external_features", + Some("deny = [{ name = 'libssh2-sys', features.deny = ['zlib-ng-compat'] }]"), + Some(&["x86_64-unknown-linux-gnu"]), + |ctx, cs, tx| { + bans::check(ctx, None, cs, tx); + }, + ) + .unwrap(); + + insta::assert_snapshot!(tu::to_snapshot(diags)); +} + +/// Ensures features banned in a crate with multiple versions are all found +#[test] +fn bans_features_from_multiple_versions() { + let diags = tu::gather_diagnostics::( + KrateGather::new("features-galore"), + "bans_features_from_multiple_versions", + Some("multiple-versions = 'allow'\ndeny = [{ name = 'windows-sys', features.deny = ['Win32_System_LibraryLoader'] }]"), + Some(&["x86_64-pc-windows-msvc"]), + |ctx, cs, tx| { + bans::check(ctx, None, cs, tx); + }, + ) + .unwrap(); + + insta::assert_snapshot!(tu::to_snapshot(diags)); +} + +/// Ensures weak dependencies are properly pruned from the graph +/// See for more +#[test] +fn weak_dependencies_pruned() { + let diags = tu::gather_diagnostics::( + KrateGather { + name: "features-galore", + features: &["zlib"], + no_default_features: true, + ..Default::default() + }, + "weak_dependencies_pruned", + Some("deny = [{ name = 'libssh2-sys' }]"), + Some(&["x86_64-unknown-linux-gnu"]), + |ctx, cs, tx| { + bans::check(ctx, None, cs, tx); + }, + ) + .unwrap(); + + assert!(diags.is_empty()); +} diff --git a/tests/snapshots/bans__deterministic_duplicate_ordering.snap b/tests/snapshots/bans__deterministic_duplicate_ordering.snap new file mode 100644 index 00000000..90a28e05 --- /dev/null +++ b/tests/snapshots/bans__deterministic_duplicate_ordering.snap @@ -0,0 +1,753 @@ +--- +source: tests/bans.rs +expression: "tu::to_snapshot(diags)" +--- +[ + { + "fields": { + "code": "B004", + "graphs": [ + { + "Krate": { + "name": "block-buffer", + "version": "0.7.3" + }, + "parents": [ + { + "Krate": { + "name": "sha-1", + "version": "0.8.2" + }, + "parents": [ + { + "Krate": { + "kind": "build", + "name": "pest_meta", + "version": "2.1.3" + }, + "parents": [ + { + "Krate": { + "name": "pest_generator", + "version": "2.1.3" + }, + "parents": [ + { + "Krate": { + "name": "pest_derive", + "version": "2.1.0" + }, + "parents": [ + { + "Krate": { + "name": "async-graphql-parser", + "version": "3.0.38" + }, + "parents": [ + { + "Krate": { + "name": "async-graphql", + "version": "3.0.38" + }, + "parents": [ + { + "Krate": { + "name": "duplicates", + "version": "0.1.0" + } + } + ] + }, + { + "Krate": { + "name": "async-graphql-derive", + "version": "3.0.38" + }, + "parents": [ + { + "Krate": { + "name": "async-graphql", + "version": "3.0.38" + }, + "repeat": true + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "Krate": { + "name": "block-buffer", + "version": "0.10.2" + }, + "parents": [ + { + "Krate": { + "name": "digest", + "version": "0.10.3" + }, + "parents": [ + { + "Krate": { + "name": "duplicates", + "version": "0.1.0" + } + }, + { + "Krate": { + "name": "sha2", + "version": "0.10.2" + }, + "parents": [ + { + "Krate": { + "name": "duplicates", + "version": "0.1.0" + }, + "repeat": true + }, + { + "Krate": { + "name": "sqlx", + "version": "0.5.13" + }, + "parents": [ + { + "Krate": { + "name": "duplicates", + "version": "0.1.0" + }, + "repeat": true + } + ] + }, + { + "Krate": { + "name": "sqlx-core", + "version": "0.5.13" + }, + "parents": [ + { + "Krate": { + "name": "duplicates", + "version": "0.1.0" + }, + "repeat": true + }, + { + "Krate": { + "name": "sqlx", + "version": "0.5.13" + }, + "repeat": true + }, + { + "Krate": { + "name": "sqlx-macros", + "version": "0.5.13" + }, + "parents": [ + { + "Krate": { + "name": "duplicates", + "version": "0.1.0" + }, + "repeat": true + }, + { + "Krate": { + "name": "sqlx", + "version": "0.5.13" + }, + "repeat": true + } + ] + } + ] + }, + { + "Krate": { + "name": "sqlx-macros", + "version": "0.5.13" + }, + "repeat": true + } + ] + }, + { + "Krate": { + "name": "sqlx", + "version": "0.5.13" + }, + "repeat": true + }, + { + "Krate": { + "name": "sqlx-core", + "version": "0.5.13" + }, + "repeat": true + }, + { + "Krate": { + "name": "sqlx-macros", + "version": "0.5.13" + }, + "repeat": true + } + ] + }, + { + "Krate": { + "name": "sha2", + "version": "0.10.2" + }, + "repeat": true + } + ] + } + ], + "labels": [ + { + "column": 1, + "line": 15, + "message": "lock entries", + "span": "block-buffer 0.7.3 registry+https://github.com/rust-lang/crates.io-index\nblock-buffer 0.10.2 registry+https://github.com/rust-lang/crates.io-index" + } + ], + "message": "found 2 duplicate entries for crate 'block-buffer'", + "severity": "error" + }, + "type": "diagnostic" + }, + { + "fields": { + "code": "B004", + "graphs": [ + { + "Krate": { + "name": "digest", + "version": "0.8.1" + }, + "parents": [ + { + "Krate": { + "name": "sha-1", + "version": "0.8.2" + }, + "parents": [ + { + "Krate": { + "kind": "build", + "name": "pest_meta", + "version": "2.1.3" + }, + "parents": [ + { + "Krate": { + "name": "pest_generator", + "version": "2.1.3" + }, + "parents": [ + { + "Krate": { + "name": "pest_derive", + "version": "2.1.0" + }, + "parents": [ + { + "Krate": { + "name": "async-graphql-parser", + "version": "3.0.38" + }, + "parents": [ + { + "Krate": { + "name": "async-graphql", + "version": "3.0.38" + }, + "parents": [ + { + "Krate": { + "name": "duplicates", + "version": "0.1.0" + } + } + ] + }, + { + "Krate": { + "name": "async-graphql-derive", + "version": "3.0.38" + }, + "parents": [ + { + "Krate": { + "name": "async-graphql", + "version": "3.0.38" + }, + "repeat": true + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "Krate": { + "name": "digest", + "version": "0.10.3" + }, + "parents": [ + { + "Krate": { + "name": "duplicates", + "version": "0.1.0" + } + }, + { + "Krate": { + "name": "sha2", + "version": "0.10.2" + }, + "parents": [ + { + "Krate": { + "name": "duplicates", + "version": "0.1.0" + }, + "repeat": true + }, + { + "Krate": { + "name": "sqlx", + "version": "0.5.13" + }, + "parents": [ + { + "Krate": { + "name": "duplicates", + "version": "0.1.0" + }, + "repeat": true + } + ] + }, + { + "Krate": { + "name": "sqlx-core", + "version": "0.5.13" + }, + "parents": [ + { + "Krate": { + "name": "duplicates", + "version": "0.1.0" + }, + "repeat": true + }, + { + "Krate": { + "name": "sqlx", + "version": "0.5.13" + }, + "repeat": true + }, + { + "Krate": { + "name": "sqlx-macros", + "version": "0.5.13" + }, + "parents": [ + { + "Krate": { + "name": "duplicates", + "version": "0.1.0" + }, + "repeat": true + }, + { + "Krate": { + "name": "sqlx", + "version": "0.5.13" + }, + "repeat": true + } + ] + } + ] + }, + { + "Krate": { + "name": "sqlx-macros", + "version": "0.5.13" + }, + "repeat": true + } + ] + }, + { + "Krate": { + "name": "sqlx", + "version": "0.5.13" + }, + "repeat": true + }, + { + "Krate": { + "name": "sqlx-core", + "version": "0.5.13" + }, + "repeat": true + }, + { + "Krate": { + "name": "sqlx-macros", + "version": "0.5.13" + }, + "repeat": true + } + ] + } + ], + "labels": [ + { + "column": 1, + "line": 31, + "message": "lock entries", + "span": "digest 0.8.1 registry+https://github.com/rust-lang/crates.io-index\ndigest 0.10.3 registry+https://github.com/rust-lang/crates.io-index" + } + ], + "message": "found 2 duplicate entries for crate 'digest'", + "severity": "error" + }, + "type": "diagnostic" + }, + { + "fields": { + "code": "B004", + "graphs": [ + { + "Krate": { + "name": "generic-array", + "version": "0.12.4" + }, + "parents": [ + { + "Krate": { + "name": "block-buffer", + "version": "0.7.3" + }, + "parents": [ + { + "Krate": { + "name": "sha-1", + "version": "0.8.2" + }, + "parents": [ + { + "Krate": { + "kind": "build", + "name": "pest_meta", + "version": "2.1.3" + }, + "parents": [ + { + "Krate": { + "name": "pest_generator", + "version": "2.1.3" + }, + "parents": [ + { + "Krate": { + "name": "pest_derive", + "version": "2.1.0" + }, + "parents": [ + { + "Krate": { + "name": "async-graphql-parser", + "version": "3.0.38" + }, + "parents": [ + { + "Krate": { + "name": "async-graphql", + "version": "3.0.38" + }, + "parents": [ + { + "Krate": { + "name": "duplicates", + "version": "0.1.0" + } + } + ] + }, + { + "Krate": { + "name": "async-graphql-derive", + "version": "3.0.38" + }, + "parents": [ + { + "Krate": { + "name": "async-graphql", + "version": "3.0.38" + }, + "repeat": true + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "Krate": { + "name": "digest", + "version": "0.8.1" + }, + "parents": [ + { + "Krate": { + "name": "sha-1", + "version": "0.8.2" + }, + "repeat": true + } + ] + } + ] + }, + { + "Krate": { + "name": "generic-array", + "version": "0.14.5" + }, + "parents": [ + { + "Krate": { + "name": "block-buffer", + "version": "0.10.2" + }, + "parents": [ + { + "Krate": { + "name": "digest", + "version": "0.10.3" + }, + "parents": [ + { + "Krate": { + "name": "duplicates", + "version": "0.1.0" + } + }, + { + "Krate": { + "name": "sha2", + "version": "0.10.2" + }, + "parents": [ + { + "Krate": { + "name": "duplicates", + "version": "0.1.0" + }, + "repeat": true + }, + { + "Krate": { + "name": "sqlx", + "version": "0.5.13" + }, + "parents": [ + { + "Krate": { + "name": "duplicates", + "version": "0.1.0" + }, + "repeat": true + } + ] + }, + { + "Krate": { + "name": "sqlx-core", + "version": "0.5.13" + }, + "parents": [ + { + "Krate": { + "name": "duplicates", + "version": "0.1.0" + }, + "repeat": true + }, + { + "Krate": { + "name": "sqlx", + "version": "0.5.13" + }, + "repeat": true + }, + { + "Krate": { + "name": "sqlx-macros", + "version": "0.5.13" + }, + "parents": [ + { + "Krate": { + "name": "duplicates", + "version": "0.1.0" + }, + "repeat": true + }, + { + "Krate": { + "name": "sqlx", + "version": "0.5.13" + }, + "repeat": true + } + ] + } + ] + }, + { + "Krate": { + "name": "sqlx-macros", + "version": "0.5.13" + }, + "repeat": true + } + ] + }, + { + "Krate": { + "name": "sqlx", + "version": "0.5.13" + }, + "repeat": true + }, + { + "Krate": { + "name": "sqlx-core", + "version": "0.5.13" + }, + "repeat": true + }, + { + "Krate": { + "name": "sqlx-macros", + "version": "0.5.13" + }, + "repeat": true + } + ] + }, + { + "Krate": { + "name": "sha2", + "version": "0.10.2" + }, + "repeat": true + } + ] + }, + { + "Krate": { + "name": "crypto-common", + "version": "0.1.3" + }, + "parents": [ + { + "Krate": { + "name": "digest", + "version": "0.10.3" + }, + "repeat": true + }, + { + "Krate": { + "name": "duplicates", + "version": "0.1.0" + }, + "repeat": true + }, + { + "Krate": { + "name": "sha2", + "version": "0.10.2" + }, + "repeat": true + }, + { + "Krate": { + "name": "sqlx", + "version": "0.5.13" + }, + "repeat": true + }, + { + "Krate": { + "name": "sqlx-core", + "version": "0.5.13" + }, + "repeat": true + }, + { + "Krate": { + "name": "sqlx-macros", + "version": "0.5.13" + }, + "repeat": true + } + ] + } + ] + } + ], + "labels": [ + { + "column": 1, + "line": 50, + "message": "lock entries", + "span": "generic-array 0.12.4 registry+https://github.com/rust-lang/crates.io-index\ngeneric-array 0.14.5 registry+https://github.com/rust-lang/crates.io-index" + } + ], + "message": "found 2 duplicate entries for crate 'generic-array'", + "severity": "error" + }, + "type": "diagnostic" + } +] diff --git a/tests/snapshots/feature_bans__bans_external_features.snap b/tests/snapshots/feature_bans__bans_external_features.snap new file mode 100644 index 00000000..6bba1748 --- /dev/null +++ b/tests/snapshots/feature_bans__bans_external_features.snap @@ -0,0 +1,143 @@ +--- +source: tests/feature_bans.rs +expression: "tu::to_snapshot(diags)" +--- +[ + { + "fields": { + "code": "B015", + "graphs": [ + { + "Krate": { + "name": "libssh2-sys", + "version": "0.2.23" + }, + "parents": [ + { + "Feature": { + "crate_name": "libssh2-sys", + "name": "zlib-ng-compat" + }, + "parents": [ + { + "Krate": { + "name": "libgit2-sys", + "version": "0.13.4+1.4.2" + }, + "parents": [ + { + "Krate": { + "name": "git2", + "version": "0.14.4" + }, + "parents": [ + { + "Krate": { + "name": "features-galore", + "version": "0.1.0" + }, + "parents": [ + { + "Feature": { + "crate_name": "features-galore", + "name": "ssh" + } + }, + { + "Feature": { + "crate_name": "features-galore", + "name": "zlib" + } + } + ] + } + ] + }, + { + "Feature": { + "crate_name": "libgit2-sys", + "name": "libssh2-sys" + }, + "parents": [ + { + "Feature": { + "crate_name": "libgit2-sys", + "name": "ssh" + }, + "parents": [ + { + "Krate": { + "name": "git2", + "version": "0.14.4" + }, + "repeat": true + } + ] + } + ] + }, + { + "Feature": { + "crate_name": "libgit2-sys", + "name": "ssh" + }, + "repeat": true + }, + { + "Feature": { + "crate_name": "libgit2-sys", + "name": "ssh_key_from_memory" + }, + "parents": [ + { + "Krate": { + "name": "git2", + "version": "0.14.4" + }, + "repeat": true + } + ] + }, + { + "Feature": { + "crate_name": "libgit2-sys", + "name": "zlib-ng-compat" + }, + "parents": [ + { + "Krate": { + "name": "git2", + "version": "0.14.4" + }, + "repeat": true + } + ] + } + ] + }, + { + "Feature": { + "crate_name": "libgit2-sys", + "name": "zlib-ng-compat" + }, + "repeat": true + } + ] + } + ] + } + ], + "labels": [ + { + "column": 50, + "line": 1, + "message": "feature denied here", + "span": "'zlib-ng-compat'" + } + ], + "message": "feature 'zlib-ng-compat' for crate 'libssh2-sys = 0.2.23' is explicitly denied", + "severity": "error" + }, + "type": "diagnostic" + } +] diff --git a/tests/snapshots/feature_bans__bans_features_from_multiple_versions.snap b/tests/snapshots/feature_bans__bans_features_from_multiple_versions.snap new file mode 100644 index 00000000..9619528d --- /dev/null +++ b/tests/snapshots/feature_bans__bans_features_from_multiple_versions.snap @@ -0,0 +1,170 @@ +--- +source: tests/feature_bans.rs +expression: "tu::to_snapshot(diags)" +--- +[ + { + "fields": { + "code": "B015", + "graphs": [ + { + "Krate": { + "name": "windows-sys", + "version": "0.36.1" + }, + "parents": [ + { + "Feature": { + "crate_name": "windows-sys", + "name": "Win32_System_LibraryLoader" + }, + "parents": [ + { + "Krate": { + "name": "parking_lot_core", + "version": "0.9.3" + }, + "parents": [ + { + "Krate": { + "name": "features-galore", + "version": "0.1.0" + }, + "parents": [ + { + "Feature": { + "crate_name": "features-galore", + "name": "default" + } + }, + { + "Feature": { + "crate_name": "features-galore", + "name": "json" + }, + "parents": [ + { + "Feature": { + "crate_name": "features-galore", + "name": "simple" + }, + "parents": [ + { + "Feature": { + "crate_name": "features-galore", + "name": "default" + }, + "repeat": true + } + ] + } + ] + }, + { + "Feature": { + "crate_name": "features-galore", + "name": "simple" + }, + "repeat": true + } + ] + } + ] + } + ] + } + ] + } + ], + "labels": [ + { + "column": 50, + "line": 2, + "message": "feature denied here", + "span": "'Win32_System_LibraryLoader'" + } + ], + "message": "feature 'Win32_System_LibraryLoader' for crate 'windows-sys = 0.36.1' is explicitly denied", + "severity": "error" + }, + "type": "diagnostic" + }, + { + "fields": { + "code": "B015", + "graphs": [ + { + "Krate": { + "name": "windows-sys", + "version": "0.42.0" + }, + "parents": [ + { + "Feature": { + "crate_name": "windows-sys", + "name": "Win32_System_LibraryLoader" + }, + "parents": [ + { + "Krate": { + "name": "features-galore", + "version": "0.1.0" + }, + "parents": [ + { + "Feature": { + "crate_name": "features-galore", + "name": "default" + } + }, + { + "Feature": { + "crate_name": "features-galore", + "name": "json" + }, + "parents": [ + { + "Feature": { + "crate_name": "features-galore", + "name": "simple" + }, + "parents": [ + { + "Feature": { + "crate_name": "features-galore", + "name": "default" + }, + "repeat": true + } + ] + } + ] + }, + { + "Feature": { + "crate_name": "features-galore", + "name": "simple" + }, + "repeat": true + } + ] + } + ] + } + ] + } + ], + "labels": [ + { + "column": 50, + "line": 2, + "message": "feature denied here", + "span": "'Win32_System_LibraryLoader'" + } + ], + "message": "feature 'Win32_System_LibraryLoader' for crate 'windows-sys = 0.42.0' is explicitly denied", + "severity": "error" + }, + "type": "diagnostic" + } +] diff --git a/tests/snapshots/feature_bans__bans_workspace_features.snap b/tests/snapshots/feature_bans__bans_workspace_features.snap new file mode 100644 index 00000000..aab33da0 --- /dev/null +++ b/tests/snapshots/feature_bans__bans_workspace_features.snap @@ -0,0 +1,46 @@ +--- +source: tests/feature_bans.rs +expression: "tu::to_snapshot(diags)" +--- +[ + { + "fields": { + "code": "B015", + "graphs": [ + { + "Krate": { + "name": "features-galore", + "version": "0.1.0" + }, + "parents": [ + { + "Feature": { + "crate_name": "features-galore", + "name": "simple" + }, + "parents": [ + { + "Feature": { + "crate_name": "features-galore", + "name": "default" + } + } + ] + } + ] + } + ], + "labels": [ + { + "column": 54, + "line": 1, + "message": "feature denied here", + "span": "'simple'" + } + ], + "message": "feature 'simple' for crate 'features-galore = 0.1.0' is explicitly denied", + "severity": "error" + }, + "type": "diagnostic" + } +] diff --git a/tests/sources.rs b/tests/sources.rs index dee3f1f5..2620248c 100644 --- a/tests/sources.rs +++ b/tests/sources.rs @@ -1,15 +1,14 @@ -use cargo_deny::sources; - -#[macro_use] -mod utils; +use cargo_deny::{ + field_eq, sources, + test_utils::{self as tu, KrateGather}, +}; #[test] fn fails_unknown_git() { let cfg = "unknown-git = 'deny'"; - let krates = utils::get_test_data_krates("sources").unwrap(); - let diags = utils::gather_diagnostics::( - krates, + let diags = tu::gather_diagnostics::( + KrateGather::new("sources"), "fails_unknown_git", Some(cfg), None, @@ -57,9 +56,8 @@ fn allows_git() { 'https://bitbucket.org/marshallpierce/line-wrap-rs', ]"; - let krates = utils::get_test_data_krates("sources").unwrap(); - let diags = utils::gather_diagnostics::( - krates, + let diags = tu::gather_diagnostics::( + KrateGather::new("sources"), "fails_unknown_git", Some(cfg), None, @@ -102,9 +100,8 @@ fn allows_github_org() { github = ['EmbarkStudios'] "; - let krates = utils::get_test_data_krates("sources").unwrap(); - let diags = utils::gather_diagnostics::( - krates, + let diags = tu::gather_diagnostics::( + KrateGather::new("sources"), "allows_github_org", Some(cfg), None, @@ -151,9 +148,8 @@ fn allows_gitlab_org() { gitlab = ['amethyst-engine'] "; - let krates = utils::get_test_data_krates("sources").unwrap(); - let diags = utils::gather_diagnostics::( - krates, + let diags = tu::gather_diagnostics::( + KrateGather::new("sources"), "allows_gitlab_org", Some(cfg), None, @@ -197,9 +193,8 @@ fn allows_bitbucket_org() { bitbucket = ['marshallpierce'] "; - let krates = utils::get_test_data_krates("sources").unwrap(); - let diags = utils::gather_diagnostics::( - krates, + let diags = tu::gather_diagnostics::( + KrateGather::new("sources"), "allows_bitbucket_org", Some(cfg), None, @@ -261,9 +256,8 @@ fn validates_git_source_specs() { spec ); - let krates = utils::get_test_data_krates("sources").unwrap(); - let mut diags = utils::gather_diagnostics::( - krates, + let mut diags = tu::gather_diagnostics::( + KrateGather::new("sources"), "validates_git_source_specs", Some(&cfg), None, diff --git a/tests/test_data/features-galore/Cargo.lock b/tests/test_data/features-galore/Cargo.lock new file mode 100644 index 00000000..da5e68da --- /dev/null +++ b/tests/test_data/features-galore/Cargo.lock @@ -0,0 +1,1400 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "async-compression" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345fd392ab01f746c717b1357165b76f0b67a60192007b234058c9045fdcf695" +dependencies = [ + "brotli", + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bindgen" +version = "0.59.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "brotli" +version = "3.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bumpalo" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" + +[[package]] +name = "bytemuck" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da" + +[[package]] +name = "bytes" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cmake" +version = "0.1.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8ad8cef104ac57b68b89df3208164d228503abbdce70f6880ffa3d970e7443a" +dependencies = [ + "cc", +] + +[[package]] +name = "cookie" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "344adc371239ef32293cb1c4fe519592fcf21206c79c02854320afcdf3ab4917" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e4b6aa369f41f5faa04bb80c9b1f4216ea81646ed6124d76ba5c49a7aafd9cd" +dependencies = [ + "cookie", + "idna 0.2.3", + "log", + "publicsuffix", + "serde", + "serde_json", + "time", + "url", +] + +[[package]] +name = "core-foundation-sys" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" + +[[package]] +name = "coreaudio-rs" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d3719e41553597cc2d53170cdcb5c72a4c0b35d0c95a0fce1e2a7a4c2c74440" +dependencies = [ + "bitflags", + "core-foundation-sys", + "coreaudio-sys", +] + +[[package]] +name = "coreaudio-sys" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dff444d80630d7073077d38d40b4501fd518bd2b922c2a55edcc8b0f7be57e6" +dependencies = [ + "bindgen", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "features-galore" +version = "0.1.0" +dependencies = [ + "coreaudio-rs", + "git2", + "parking_lot_core", + "reqwest", + "rgb", + "rustls", + "serde", + "windows-sys 0.42.0", +] + +[[package]] +name = "flate2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" + +[[package]] +name = "futures-io" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" + +[[package]] +name = "futures-sink" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" + +[[package]] +name = "futures-task" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" + +[[package]] +name = "futures-util" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +dependencies = [ + "futures-core", + "futures-io", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "git2" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0155506aab710a86160ddb504a480d2964d7ab5b9e62419be69e0032bc5931c" +dependencies = [ + "bitflags", + "libc", + "libgit2-sys", + "log", + "url", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "h2" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" +dependencies = [ + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" + +[[package]] +name = "itoa" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" + +[[package]] +name = "jobserver" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.134" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" + +[[package]] +name = "libgit2-sys" +version = "0.13.4+1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0fa6563431ede25f5cc7f6d803c6afbc1c5d3ad3d4925d12c882bf2b526f5d1" +dependencies = [ + "cc", + "libc", + "libssh2-sys", + "libz-sys", + "pkg-config", +] + +[[package]] +name = "libloading" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libssh2-sys" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b094a36eb4b8b8c8a7b4b8ae43b2944502be3e59cd87687595cf6b0a71b3f4ca" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libz-sys" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" +dependencies = [ + "cc", + "cmake", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[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.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.36.1", +] + +[[package]] +name = "nom" +version = "7.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" + +[[package]] +name = "openssl-sys" +version = "0.9.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.36.1", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro2" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + +[[package]] +name = "publicsuffix" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeeedb0b429dc462f30ad27ef3de97058b060016f47790c066757be38ef792b4" +dependencies = [ + "idna 0.2.3", + "psl-types", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + +[[package]] +name = "reqwest" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" +dependencies = [ + "async-compression", + "base64", + "bytes", + "cookie", + "cookie_store", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "once_cell", + "percent-encoding", + "pin-project-lite", + "proc-macro-hack", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-rustls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "rgb" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3603b7d71ca82644f79b5a06d1220e9a58ede60bd32255f698cb1af8838b8db3" +dependencies = [ + "bytemuck", + "serde", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustls" +version = "0.20.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +dependencies = [ + "base64", +] + +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "serde" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "syn" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "time" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3f9a28b618c3a6b9251b6908e9c99e04b9e5c02e6581ccbb67d59c34ef7f9b" +dependencies = [ + "itoa", + "libc", + "num_threads", + "time-macros", +] + +[[package]] +name = "time-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "winapi", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-util" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +dependencies = [ + "once_cell", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna 0.3.0", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + +[[package]] +name = "web-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be" +dependencies = [ + "webpki", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", +] + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.0", + "windows_i686_gnu 0.42.0", + "windows_i686_msvc 0.42.0", + "windows_x86_64_gnu 0.42.0", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] diff --git a/tests/test_data/features-galore/Cargo.toml b/tests/test_data/features-galore/Cargo.toml new file mode 100644 index 00000000..0127361e --- /dev/null +++ b/tests/test_data/features-galore/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "features-galore" +version = "0.1.0" +edition = "2021" + +[dependencies] +parking_lot_core = "=0.9.3" +reqest = { version = "0.11", package = "reqwest", default-features = false, optional = true, features = [ + "brotli", +] } +rgb = { version = "0.8.25", optional = true } +serde = { version = "1.0.133", optional = true } +rustls = { version = "0.20", optional = true } + +[target.'cfg(target_os = "windows")'.dependencies] +windows-sys = { version = "0.42", features = ["Win32_System_LibraryLoader"] } + +[target.'cfg(target_os = "linux")'.dependencies] +git = { package = "git2", version = "0.14", default-features = false } + +[target.'cfg(target_os = "macos")'.dependencies] +audio = { package = "coreaudio-rs", version = "=0.11.1", default-features = false, optional = true } + +[build-dependencies] +reqest = { version = "0.11", package = "reqwest", default-features = false, optional = true, features = [ + "cookies", +] } + +[features] +default = ["simple"] +blocking = ["simple", "reqest?/blocking"] +json = ["reqest?/json"] +midi = ["audio?/core_midi"] +multipart = ["reqest?/multipart"] +serde = ["dep:serde", "rgb?/serde"] +simple = ["json"] +ssh = ["git/ssh", "git/ssh_key_from_memory"] +stream = ["reqest?/stream"] +tls = ["tls-no-reqwest", "reqest?/rustls-tls"] +tls-no-reqwest = ["rustls"] +zlib = ["git/zlib-ng-compat", "reqest?/deflate"] diff --git a/tests/test_data/features-galore/src/lib.rs b/tests/test_data/features-galore/src/lib.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/tests/test_data/features-galore/src/lib.rs @@ -0,0 +1 @@ + diff --git a/tests/test_data/features/Cargo.toml b/tests/test_data/features/Cargo.toml new file mode 100644 index 00000000..8c79fc3c --- /dev/null +++ b/tests/test_data/features/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "features" +version = "0.1.0" +edition = "2021" + +[dependencies] +reqest = { version = "0.11", package = "reqwest", default-features = false, optional = true, features = [ + "brotli", + "rustls-tls", +] } +rgb = { version = "0.8.25", optional = true } +serde = { version = "1.0.133", optional = true } + +[target.'cfg(target_os = "linux")'.dependencies] +git = { package = "git2", version = "0.15", default-features = false } + +[build-dependencies] +reqest = { version = "0.11", package = "reqwest", default-features = false, optional = true, features = [ + "cookies", +] } + +[features] +default = ["simple"] +blocking = ["simple", "reqest?/blocking"] +json = ["reqest?/json"] +multipart = ["reqest?/multipart"] +simple = ["json"] +serde = ["dep:serde", "rgb?/serde"] +stream = ["reqest?/stream"] +zlib = ["git/zlib-ng-compat", "reqest?/deflate"] +ssh = ["git/ssh", "git/ssh_key_from_memory"] diff --git a/tests/test_data/features/src/lib.rs b/tests/test_data/features/src/lib.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/tests/test_data/features/src/lib.rs @@ -0,0 +1 @@ + diff --git a/tests/utils.rs b/tests/utils.rs deleted file mode 100644 index 82f7d4da..00000000 --- a/tests/utils.rs +++ /dev/null @@ -1,131 +0,0 @@ -#![allow(unused_macros, dead_code)] - -use anyhow::{Context, Error}; -use cargo_deny::{ - diag::{CargoSpans, ErrorSink, Files, KrateSpans}, - CheckCtx, -}; - -pub fn get_test_data_krates(name: &str) -> Result { - let project_dir = std::path::Path::new("./tests/test_data").join(name); - - let mut metadata_cmd = krates::Cmd::new(); - metadata_cmd.current_dir(&project_dir); - - krates::Builder::new() - .build(metadata_cmd, krates::NoneFilter) - .context("failed to build crate graph") -} - -pub fn gather_diagnostics< - C: serde::de::DeserializeOwned + Default + cargo_deny::UnvalidatedConfig, - VC: Send, - R: FnOnce(CheckCtx<'_, VC>, CargoSpans, ErrorSink) + Send, ->( - krates: cargo_deny::Krates, - test_name: &str, - cfg: Option<&str>, - timeout: Option, - runner: R, -) -> Result, Error> { - let (spans, content, hashmap) = KrateSpans::synthesize(&krates); - let mut files = Files::new(); - - let spans_id = files.add(format!("{}/Cargo.lock", test_name), content); - - let spans = KrateSpans::with_spans(spans, spans_id); - - let (config, cfg_id) = match cfg { - Some(cfg) => { - let config: C = toml::from_str(cfg).context("failed to deserialize test config")?; - - let cfg_id = files.add(format!("{}.toml", test_name), cfg.to_owned()); - - (config, cfg_id) - } - None => ( - C::default(), - files.add(format!("{}.toml", test_name), "".to_owned()), - ), - }; - - let mut newmap = CargoSpans::new(); - for (key, val) in hashmap { - let cargo_id = files.add(val.0, val.1); - newmap.insert(key, (cargo_id, val.2)); - } - - let mut cfg_diags = Vec::new(); - let cfg = config.validate(cfg_id, &mut cfg_diags); - - if cfg_diags - .iter() - .any(|d| d.severity >= cargo_deny::diag::Severity::Error) - { - anyhow::bail!("encountered errors validating config: {:#?}", cfg_diags); - } - - let (tx, rx) = crossbeam::channel::unbounded(); - - let grapher = cargo_deny::diag::ObjectGrapher::new(&krates); - - let (_, gathered) = rayon::join( - || { - let ctx = cargo_deny::CheckCtx { - krates: &krates, - krate_spans: &spans, - cfg, - serialize_extra: true, - }; - runner(ctx, newmap, ErrorSink::Channel(tx)); - }, - || { - let mut diagnostics = Vec::new(); - - match timeout { - Some(timeout) => { - let trx = crossbeam::channel::after(timeout); - loop { - crossbeam::select! { - recv(rx) -> msg => { - if let Ok(pack) = msg { - diagnostics.extend(pack.into_iter().map(|d| cargo_deny::diag::diag_to_json(d, &files, Some(&grapher)))); - } else { - // Yay, the sender was dopped (i.e. check was finished) - break; - } - } - recv(trx) -> _ => { - anyhow::bail!("Timed out after {:?}", timeout); - } - } - } - } - None => { - while let Ok(pack) = rx.recv() { - diagnostics - .extend(pack.into_iter().map(|d| { - cargo_deny::diag::diag_to_json(d, &files, Some(&grapher)) - })); - } - } - } - - Ok(diagnostics) - }, - ); - - gathered -} - -macro_rules! field_eq { - ($obj:expr, $field:expr, $expected:expr) => { - $obj.pointer($field) == Some(&serde_json::json!($expected)) - }; -} - -macro_rules! assert_field_eq { - ($obj:expr, $field:expr, $expected:expr) => { - assert_eq!($obj.pointer($field), Some(&serde_json::json!($expected))); - }; -}