├── .envrc ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── flake.lock ├── flake.nix ├── preview.png ├── src └── main.rs └── tests ├── success.rs ├── test-lib-dep ├── Cargo.toml └── src │ └── lib.rs ├── test-lib ├── Cargo.toml └── src │ └── lib.rs └── test-ws ├── Cargo.lock ├── Cargo.toml └── src └── lib.rs /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'master' 7 | pull_request: 8 | branches: 9 | - 'master' 10 | 11 | env: 12 | CARGO_TERM_COLOR: always 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-20.04 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - uses: cachix/install-nix-action@v13 21 | with: 22 | install_url: https://nixos-nix-install-tests.cachix.org/serve/pqndq1i9g5agiyr5iwwyl061s1c71kl6/install 23 | install_options: '--tarball-url-prefix https://nixos-nix-install-tests.cachix.org/serve' 24 | extra_nix_config: | 25 | experimental-features = nix-command flakes 26 | - run: nix build 27 | 28 | format: 29 | runs-on: ubuntu-20.04 30 | 31 | steps: 32 | - uses: actions/checkout@v2 33 | - name: Check Rustfmt 34 | run: cargo fmt -- --check 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.direnv 3 | /.idea 4 | /tests/*/target 5 | result 6 | result-* 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Unreleased 2 | 3 | * Print `cargo metadata` error message rather than panic #30 4 | 5 | # 0.7.0 6 | 7 | * Support Rust 1.60.0 8 | * More styling 9 | * Support renamed optional package 10 | 11 | # 0.6.0 12 | 13 | * **Breaking** Add `target` option to set `[target]` section (https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#platform-specific-dependencies) 14 | * List features now also print optional dependencies 15 | * List features now have stable order 16 | * Fix some documents 17 | * Let all tests check their outputs 18 | 19 | # 0.5.5 20 | 21 | * Fix #23 22 | * Fix manifest description 23 | 24 | # 0.5.4 25 | 26 | * Fix wrong behavior with `+default` #21 27 | * Update toml_edit to 0.6.0 28 | 29 | # 0.5.3 30 | 31 | * Support {en, dis}able default features 32 | 33 | # 0.5.2 34 | 35 | * Support nix build 36 | 37 | # 0.5.1 38 | 39 | * Set default dependency command `+` 40 | * Support target dependencies 41 | 42 | # 0.5.0 43 | 44 | * Using `^` for remove feature command 45 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "0.7.19" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "ansi_term" 16 | version = "0.12.1" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 19 | dependencies = [ 20 | "winapi", 21 | ] 22 | 23 | [[package]] 24 | name = "assert_cmd" 25 | version = "2.0.4" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "93ae1ddd39efd67689deb1979d80bad3bf7f2b09c6e6117c8d1f2443b5e2f83e" 28 | dependencies = [ 29 | "bstr", 30 | "doc-comment", 31 | "predicates", 32 | "predicates-core", 33 | "predicates-tree", 34 | "wait-timeout", 35 | ] 36 | 37 | [[package]] 38 | name = "atty" 39 | version = "0.2.14" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 42 | dependencies = [ 43 | "hermit-abi", 44 | "libc", 45 | "winapi", 46 | ] 47 | 48 | [[package]] 49 | name = "autocfg" 50 | version = "1.1.0" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 53 | 54 | [[package]] 55 | name = "bitflags" 56 | version = "1.3.2" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 59 | 60 | [[package]] 61 | name = "bstr" 62 | version = "0.2.17" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" 65 | dependencies = [ 66 | "lazy_static", 67 | "memchr", 68 | "regex-automata", 69 | ] 70 | 71 | [[package]] 72 | name = "bytes" 73 | version = "1.2.1" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" 76 | 77 | [[package]] 78 | name = "camino" 79 | version = "1.1.1" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "88ad0e1e3e88dd237a156ab9f571021b8a158caa0ae44b1968a241efb5144c1e" 82 | dependencies = [ 83 | "serde", 84 | ] 85 | 86 | [[package]] 87 | name = "cargo-feature" 88 | version = "0.7.1" 89 | dependencies = [ 90 | "ansi_term", 91 | "assert_cmd", 92 | "cargo_metadata", 93 | "pad", 94 | "predicates", 95 | "structopt", 96 | "toml_edit", 97 | ] 98 | 99 | [[package]] 100 | name = "cargo-platform" 101 | version = "0.1.2" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" 104 | dependencies = [ 105 | "serde", 106 | ] 107 | 108 | [[package]] 109 | name = "cargo_metadata" 110 | version = "0.15.0" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "3abb7553d5b9b8421c6de7cb02606ff15e0c6eea7d8eadd75ef013fd636bec36" 113 | dependencies = [ 114 | "camino", 115 | "cargo-platform", 116 | "semver", 117 | "serde", 118 | "serde_json", 119 | ] 120 | 121 | [[package]] 122 | name = "clap" 123 | version = "2.34.0" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" 126 | dependencies = [ 127 | "ansi_term", 128 | "atty", 129 | "bitflags", 130 | "strsim", 131 | "textwrap", 132 | "unicode-width", 133 | "vec_map", 134 | ] 135 | 136 | [[package]] 137 | name = "combine" 138 | version = "4.6.6" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" 141 | dependencies = [ 142 | "bytes", 143 | "memchr", 144 | ] 145 | 146 | [[package]] 147 | name = "difflib" 148 | version = "0.4.0" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" 151 | 152 | [[package]] 153 | name = "doc-comment" 154 | version = "0.3.3" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" 157 | 158 | [[package]] 159 | name = "either" 160 | version = "1.8.0" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" 163 | 164 | [[package]] 165 | name = "float-cmp" 166 | version = "0.9.0" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" 169 | dependencies = [ 170 | "num-traits", 171 | ] 172 | 173 | [[package]] 174 | name = "hashbrown" 175 | version = "0.12.3" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 178 | 179 | [[package]] 180 | name = "heck" 181 | version = "0.3.3" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 184 | dependencies = [ 185 | "unicode-segmentation", 186 | ] 187 | 188 | [[package]] 189 | name = "hermit-abi" 190 | version = "0.1.19" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 193 | dependencies = [ 194 | "libc", 195 | ] 196 | 197 | [[package]] 198 | name = "indexmap" 199 | version = "1.9.1" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" 202 | dependencies = [ 203 | "autocfg", 204 | "hashbrown", 205 | ] 206 | 207 | [[package]] 208 | name = "itertools" 209 | version = "0.10.4" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "d8bf247779e67a9082a4790b45e71ac7cfd1321331a5c856a74a9faebdab78d0" 212 | dependencies = [ 213 | "either", 214 | ] 215 | 216 | [[package]] 217 | name = "itoa" 218 | version = "1.0.3" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" 221 | 222 | [[package]] 223 | name = "lazy_static" 224 | version = "1.4.0" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 227 | 228 | [[package]] 229 | name = "libc" 230 | version = "0.2.133" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" 233 | 234 | [[package]] 235 | name = "memchr" 236 | version = "2.5.0" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 239 | 240 | [[package]] 241 | name = "normalize-line-endings" 242 | version = "0.3.0" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" 245 | 246 | [[package]] 247 | name = "num-traits" 248 | version = "0.2.15" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 251 | dependencies = [ 252 | "autocfg", 253 | ] 254 | 255 | [[package]] 256 | name = "pad" 257 | version = "0.1.6" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "d2ad9b889f1b12e0b9ee24db044b5129150d5eada288edc800f789928dc8c0e3" 260 | dependencies = [ 261 | "unicode-width", 262 | ] 263 | 264 | [[package]] 265 | name = "predicates" 266 | version = "2.1.1" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "a5aab5be6e4732b473071984b3164dbbfb7a3674d30ea5ff44410b6bcd960c3c" 269 | dependencies = [ 270 | "difflib", 271 | "float-cmp", 272 | "itertools", 273 | "normalize-line-endings", 274 | "predicates-core", 275 | "regex", 276 | ] 277 | 278 | [[package]] 279 | name = "predicates-core" 280 | version = "1.0.3" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "da1c2388b1513e1b605fcec39a95e0a9e8ef088f71443ef37099fa9ae6673fcb" 283 | 284 | [[package]] 285 | name = "predicates-tree" 286 | version = "1.0.5" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "4d86de6de25020a36c6d3643a86d9a6a9f552107c0559c60ea03551b5e16c032" 289 | dependencies = [ 290 | "predicates-core", 291 | "termtree", 292 | ] 293 | 294 | [[package]] 295 | name = "proc-macro-error" 296 | version = "1.0.4" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 299 | dependencies = [ 300 | "proc-macro-error-attr", 301 | "proc-macro2", 302 | "quote", 303 | "syn", 304 | "version_check", 305 | ] 306 | 307 | [[package]] 308 | name = "proc-macro-error-attr" 309 | version = "1.0.4" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 312 | dependencies = [ 313 | "proc-macro2", 314 | "quote", 315 | "version_check", 316 | ] 317 | 318 | [[package]] 319 | name = "proc-macro2" 320 | version = "1.0.43" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" 323 | dependencies = [ 324 | "unicode-ident", 325 | ] 326 | 327 | [[package]] 328 | name = "quote" 329 | version = "1.0.21" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 332 | dependencies = [ 333 | "proc-macro2", 334 | ] 335 | 336 | [[package]] 337 | name = "regex" 338 | version = "1.6.0" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" 341 | dependencies = [ 342 | "aho-corasick", 343 | "memchr", 344 | "regex-syntax", 345 | ] 346 | 347 | [[package]] 348 | name = "regex-automata" 349 | version = "0.1.10" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 352 | 353 | [[package]] 354 | name = "regex-syntax" 355 | version = "0.6.27" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" 358 | 359 | [[package]] 360 | name = "ryu" 361 | version = "1.0.11" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" 364 | 365 | [[package]] 366 | name = "semver" 367 | version = "1.0.14" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" 370 | dependencies = [ 371 | "serde", 372 | ] 373 | 374 | [[package]] 375 | name = "serde" 376 | version = "1.0.144" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" 379 | dependencies = [ 380 | "serde_derive", 381 | ] 382 | 383 | [[package]] 384 | name = "serde_derive" 385 | version = "1.0.144" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" 388 | dependencies = [ 389 | "proc-macro2", 390 | "quote", 391 | "syn", 392 | ] 393 | 394 | [[package]] 395 | name = "serde_json" 396 | version = "1.0.85" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" 399 | dependencies = [ 400 | "itoa", 401 | "ryu", 402 | "serde", 403 | ] 404 | 405 | [[package]] 406 | name = "strsim" 407 | version = "0.8.0" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 410 | 411 | [[package]] 412 | name = "structopt" 413 | version = "0.3.26" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" 416 | dependencies = [ 417 | "clap", 418 | "lazy_static", 419 | "structopt-derive", 420 | ] 421 | 422 | [[package]] 423 | name = "structopt-derive" 424 | version = "0.4.18" 425 | source = "registry+https://github.com/rust-lang/crates.io-index" 426 | checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" 427 | dependencies = [ 428 | "heck", 429 | "proc-macro-error", 430 | "proc-macro2", 431 | "quote", 432 | "syn", 433 | ] 434 | 435 | [[package]] 436 | name = "syn" 437 | version = "1.0.100" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e" 440 | dependencies = [ 441 | "proc-macro2", 442 | "quote", 443 | "unicode-ident", 444 | ] 445 | 446 | [[package]] 447 | name = "termtree" 448 | version = "0.2.4" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" 451 | 452 | [[package]] 453 | name = "textwrap" 454 | version = "0.11.0" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 457 | dependencies = [ 458 | "unicode-width", 459 | ] 460 | 461 | [[package]] 462 | name = "toml_edit" 463 | version = "0.14.4" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "5376256e44f2443f8896ac012507c19a012df0fe8758b55246ae51a2279db51f" 466 | dependencies = [ 467 | "combine", 468 | "indexmap", 469 | "itertools", 470 | ] 471 | 472 | [[package]] 473 | name = "unicode-ident" 474 | version = "1.0.4" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" 477 | 478 | [[package]] 479 | name = "unicode-segmentation" 480 | version = "1.10.0" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" 483 | 484 | [[package]] 485 | name = "unicode-width" 486 | version = "0.1.10" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" 489 | 490 | [[package]] 491 | name = "vec_map" 492 | version = "0.8.2" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 495 | 496 | [[package]] 497 | name = "version_check" 498 | version = "0.9.4" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 501 | 502 | [[package]] 503 | name = "wait-timeout" 504 | version = "0.2.0" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" 507 | dependencies = [ 508 | "libc", 509 | ] 510 | 511 | [[package]] 512 | name = "winapi" 513 | version = "0.3.9" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 516 | dependencies = [ 517 | "winapi-i686-pc-windows-gnu", 518 | "winapi-x86_64-pc-windows-gnu", 519 | ] 520 | 521 | [[package]] 522 | name = "winapi-i686-pc-windows-gnu" 523 | version = "0.4.0" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 526 | 527 | [[package]] 528 | name = "winapi-x86_64-pc-windows-gnu" 529 | version = "0.4.0" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 532 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cargo-feature" 3 | description = "Cargo plugin to manage dependency features" 4 | version = "0.7.1" 5 | authors = ["Riey "] 6 | edition = "2021" 7 | license = "MIT" 8 | homepage = "https://github.com/Riey/cargo-feature" 9 | repository = "https://github.com/Riey/cargo-feature" 10 | documentation = "https://github.com/Riey/cargo-feature#usage" 11 | readme = "README.md" 12 | categories = ["command-line-interface", "development-tools", "development-tools::cargo-plugins"] 13 | keywords = [ 14 | "cargo", 15 | "cargo-subcommand", 16 | "cli", 17 | "feature", 18 | "crates", 19 | ] 20 | 21 | [dependencies] 22 | structopt = "0.3.20" 23 | toml_edit = "0.14.3" 24 | ansi_term = "0.12.1" 25 | pad = "0.1.6" 26 | cargo_metadata = "0.15.0" 27 | 28 | [dev-dependencies] 29 | assert_cmd = "2.0.1" 30 | predicates = "2.0.2" 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Riey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cargo-feature 2 | 3 | [![CI](https://github.com/Riey/cargo-feature/actions/workflows/ci.yml/badge.svg)](https://github.com/Riey/cargo-feature/actions/workflows/ci.yml) 4 | [![Crates.io](https://img.shields.io/crates/v/cargo-feature)](https://crates.io/crates/cargo-feature) 5 | 6 | [![Packaging status](https://repology.org/badge/vertical-allrepos/cargo-feature.svg)](https://repology.org/project/cargo-feature/versions) 7 | 8 | ![preview](https://github.com/Riey/cargo-feature/raw/master/preview.png) 9 | 10 | ## Install 11 | 12 | ### Cargo 13 | 14 | `cargo install cargo-feature` 15 | 16 | ### Arch 17 | 18 | `pacman -S cargo-feature` 19 | 20 | ### NixOS 21 | 22 | `nix-env -iA nixos.cargo-feature` 23 | 24 | ## Usage 25 | 26 | ``` 27 | # add serde_derive feature to build-dependency of serde 28 | cargo feature -t build serde +serde_derive 29 | 30 | # disable default-features 31 | cargo feature serde ^default 32 | 33 | # same as above but more explict 34 | cargo feature serde --disable-default-features 35 | 36 | # if you want list all features, just type crate name 37 | cargo feature serde 38 | 39 | # enable default-features 40 | cargo feature serde default 41 | 42 | # same as above but more explict 43 | cargo feature serde --enable-default-features 44 | 45 | # add HtmlDivElement feature to dependency of web_sys 46 | cargo feature web_sys +HtmlDivElement 47 | 48 | # you can skip typing + 49 | cargo feature web_sys HtmlDivElement 50 | 51 | # same as above but use `target.'cfg(target_arch = "wasm32")'.dependencies` 52 | cargo feature --target="cfg(target_arch = \"wasm32\")" web_sys HtmlDivElement 53 | 54 | # use `^` to remove feature 55 | cargo feature web_sys ^HtmlDivElement 56 | ``` 57 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "locked": { 5 | "lastModified": 1659877975, 6 | "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", 7 | "owner": "numtide", 8 | "repo": "flake-utils", 9 | "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "numtide", 14 | "repo": "flake-utils", 15 | "type": "github" 16 | } 17 | }, 18 | "nixpkgs": { 19 | "locked": { 20 | "lastModified": 1663593187, 21 | "narHash": "sha256-5tVGqzGc4HB6OjE3I6j5BPIslc0vmUkFoswv/A0XRKY=", 22 | "owner": "NixOS", 23 | "repo": "nixpkgs", 24 | "rev": "3a150bd8456edbc4de3345bed856c2cbeb2e0e5e", 25 | "type": "github" 26 | }, 27 | "original": { 28 | "owner": "NixOS", 29 | "repo": "nixpkgs", 30 | "type": "github" 31 | } 32 | }, 33 | "root": { 34 | "inputs": { 35 | "flake-utils": "flake-utils", 36 | "nixpkgs": "nixpkgs" 37 | } 38 | } 39 | }, 40 | "root": "root", 41 | "version": 7 42 | } 43 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "cargo-feature"; 3 | 4 | inputs = { 5 | nixpkgs.url = github:NixOS/nixpkgs; 6 | flake-utils.url = github:numtide/flake-utils; 7 | }; 8 | 9 | outputs = { self, nixpkgs, flake-utils }: 10 | flake-utils.lib.eachDefaultSystem 11 | (system: 12 | let 13 | pkgs = nixpkgs.legacyPackages.${system}; 14 | in 15 | { 16 | devShell = pkgs.mkShell { 17 | name = "cargo-feature-shell"; 18 | nativeBuildInputs = with pkgs; [ 19 | pkg-config 20 | rustfmt 21 | rustc 22 | cargo 23 | ]; 24 | RUST_BACKTRACE=1; 25 | }; 26 | defaultPackage = pkgs.rustPlatform.buildRustPackage rec { 27 | pname = "cargo-feature"; 28 | version = "0.7.1"; 29 | 30 | src = ./.; 31 | 32 | cargoLock = { 33 | lockFile = ./Cargo.lock; 34 | }; 35 | 36 | meta = with pkgs.lib; { 37 | description = "Cargo plugin to manage dependency features"; 38 | homepage = "https://github.com/Riey/cargo-feature"; 39 | license = licenses.mit; 40 | platforms = platforms.unix; 41 | maintainers = with maintainers; [ riey ]; 42 | }; 43 | }; 44 | } 45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Riey/cargo-feature/0b8d5ba5d7ebd8ab998deabb651baa5aae20454d/preview.png -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use structopt::StructOpt; 2 | 3 | use ansi_term::Color; 4 | use pad::PadStr; 5 | use std::collections::{BTreeSet, HashSet}; 6 | use std::fmt; 7 | use std::io::Write; 8 | use std::path::PathBuf; 9 | use std::str::FromStr; 10 | use toml_edit::{Array, Document, Entry, InlineTable, Item, Value}; 11 | 12 | #[derive(StructOpt)] 13 | struct Command { 14 | #[structopt(name = "crate", help = "Target crate name")] 15 | krate: String, 16 | #[structopt( 17 | name = "features", 18 | help = "List of features you want to add or remove you can add `+` or `^` left of the feature name default is `+`" 19 | )] 20 | features: Vec, 21 | } 22 | 23 | #[derive(Eq, PartialEq)] 24 | enum DependencyType { 25 | Normal, 26 | Dev, 27 | Build, 28 | } 29 | 30 | impl FromStr for DependencyType { 31 | type Err = String; 32 | 33 | fn from_str(s: &str) -> Result { 34 | match s { 35 | "normal" => Ok(DependencyType::Normal), 36 | "dev" => Ok(DependencyType::Dev), 37 | "build" => Ok(DependencyType::Build), 38 | _ => Err(format!("{} is not valid dependency type", s)), 39 | } 40 | } 41 | } 42 | 43 | enum DependencyCommand { 44 | Add, 45 | Remove, 46 | } 47 | 48 | #[derive(StructOpt)] 49 | struct Opt { 50 | #[structopt( 51 | long = "manifest-path", 52 | parse(from_os_str), 53 | help = "Override manifest path default is `./Cargo.toml`" 54 | )] 55 | manifest_path: Option, 56 | 57 | #[structopt( 58 | long, 59 | help = "Set target section (e.g. `cfg(target_arch = \"wasm32\")`)" 60 | )] 61 | target: Option, 62 | 63 | #[structopt( 64 | short = "p", 65 | long, 66 | help = "Don't write manifest file just print to stdout" 67 | )] 68 | preview: bool, 69 | 70 | #[structopt(short = "i", long, help = "Don't print progress output")] 71 | ignore_progress: bool, 72 | 73 | #[structopt( 74 | name = "dependency-type", 75 | short = "t", 76 | long, 77 | parse(try_from_str), 78 | default_value = "normal", 79 | help = "Dependency type you can choose one of `normal`, `dev`, `build`" 80 | )] 81 | dep_ty: DependencyType, 82 | 83 | #[structopt( 84 | long, 85 | help = "Disable crate's default features same as `cargo feature ^default` override `enable_default_features`" 86 | )] 87 | disable_default_features: bool, 88 | 89 | #[structopt( 90 | long, 91 | help = "Enable crate's default features same as `cargo feature +default`" 92 | )] 93 | enable_default_features: bool, 94 | 95 | #[structopt(flatten)] 96 | command: Command, 97 | } 98 | 99 | #[derive(StructOpt)] 100 | #[structopt(bin_name = "cargo")] 101 | enum CargoOpt { 102 | #[structopt(name = "feature")] 103 | Feature(Opt), 104 | } 105 | 106 | macro_rules! get_item { 107 | ($table:expr, $key:expr, $item:ident) => { 108 | let mut $item = match $table.entry($key) { 109 | Entry::Occupied(item) => item, 110 | Entry::Vacant(_) => return, 111 | }; 112 | 113 | let $item = $item.get_mut(); 114 | }; 115 | } 116 | 117 | fn get_target_table<'d>( 118 | doc: &'d mut toml_edit::Table, 119 | target: &str, 120 | ) -> Option<&'d mut toml_edit::Table> { 121 | let target_dic = doc.get_mut("target")?.as_table_mut()?; 122 | 123 | target_dic.get_mut(&target)?.as_table_mut() 124 | } 125 | 126 | fn try_process_dependency( 127 | doc: &mut Document, 128 | target: Option, 129 | func: impl Fn(&mut Item, DependencyType), 130 | ) { 131 | let mut doc = doc.as_table_mut(); 132 | 133 | macro_rules! try_find { 134 | ($key:expr, $ty:expr) => { 135 | get_item!(doc, $key, item); 136 | 137 | if !item.is_none() { 138 | func(item, $ty); 139 | } 140 | }; 141 | } 142 | 143 | if let Some(target) = target { 144 | doc = get_target_table(doc, &target).expect("Find target table"); 145 | } 146 | 147 | try_find!("dependencies", DependencyType::Normal); 148 | try_find!("build-dependencies", DependencyType::Build); 149 | try_find!("dev-dependencies", DependencyType::Dev); 150 | } 151 | 152 | fn parse_feature(feature: &str) -> (DependencyCommand, &str) { 153 | if let Some(command) = feature.strip_prefix("+") { 154 | (DependencyCommand::Add, command) 155 | } else if let Some(command) = feature.strip_prefix("^") { 156 | (DependencyCommand::Remove, command) 157 | } else { 158 | (DependencyCommand::Add, feature) 159 | } 160 | } 161 | 162 | fn normalize_name(name: &str) -> String { 163 | name.replace("-", "_") 164 | } 165 | 166 | fn find_package(name: &str) -> impl Fn(&cargo_metadata::Package) -> bool { 167 | let name = normalize_name(name); 168 | move |package: &cargo_metadata::Package| normalize_name(&package.name) == name 169 | } 170 | 171 | fn find_feature(feature: &str) -> impl Fn(&Value) -> bool + '_ { 172 | move |value: &Value| value.as_str() == Some(feature) 173 | } 174 | 175 | fn main() { 176 | let CargoOpt::Feature(Opt { 177 | command: Command { 178 | features: command_features, 179 | krate, 180 | }, 181 | dep_ty: target_dep_ty, 182 | mut disable_default_features, 183 | mut enable_default_features, 184 | ignore_progress, 185 | manifest_path, 186 | target, 187 | preview, 188 | }) = CargoOpt::from_args(); 189 | 190 | let mut command_features = command_features.into_iter().collect::>(); 191 | 192 | if command_features.remove("^default") { 193 | disable_default_features = true; 194 | } 195 | 196 | // Don't use shortcut `||` here because we need remove both 197 | if command_features.remove("default") | command_features.remove("+default") { 198 | enable_default_features = true; 199 | } 200 | 201 | let default_features = if disable_default_features { 202 | Some(false) 203 | } else if enable_default_features { 204 | Some(true) 205 | } else { 206 | None 207 | }; 208 | 209 | let command_features = command_features; 210 | 211 | let manifest_path = manifest_path.unwrap_or_else(|| PathBuf::from("./Cargo.toml")); 212 | 213 | let metadata = { 214 | let mut cmd = cargo_metadata::MetadataCommand::new(); 215 | cmd.manifest_path(&manifest_path); 216 | cmd.exec().unwrap_or_else(|err| { 217 | eprintln!("{err}",); 218 | std::process::exit(-1); 219 | }) 220 | }; 221 | 222 | let mut package = metadata 223 | .packages 224 | .into_iter() 225 | .find(find_package(&krate)) 226 | .unwrap_or_else(|| { 227 | eprintln!( 228 | "Can't find package from metadata! please check package `{}` is exists in manifest", 229 | krate 230 | ); 231 | std::process::exit(-1); 232 | }); 233 | 234 | if command_features.is_empty() && !enable_default_features && !disable_default_features { 235 | eprintln!( 236 | "{} features for `{}`", 237 | Color::Cyan 238 | .bold() 239 | .paint("Available".pad_to_width_with_alignment(12, pad::Alignment::Right)), 240 | krate 241 | ); 242 | 243 | if let Some(default_sub_features) = package.features.remove("default") { 244 | // different color for default 245 | println!( 246 | "{} = {}", 247 | Color::Purple.bold().paint("default"), 248 | PaintSlice(&default_sub_features), 249 | ); 250 | } 251 | 252 | let mut features = package.features.into_iter().collect::>(); 253 | 254 | features.sort_by(|l, r| l.0.cmp(&r.0)); 255 | 256 | // Since Rust 1.60.0, optional feature is included in feature section but below version is not. 257 | // https://github.com/Riey/cargo-feature/issues/26 258 | let optional_features = package 259 | .dependencies 260 | .into_iter() 261 | .filter_map(|d| { 262 | if d.optional { 263 | Some(d.rename.unwrap_or(d.name)) 264 | } else { 265 | None 266 | } 267 | }) 268 | .collect::>(); 269 | 270 | for (feature, sub_features) in features { 271 | if optional_features.contains(&feature) { 272 | continue; 273 | } 274 | 275 | println!( 276 | "{} = {}", 277 | Color::Green.bold().paint(feature), 278 | PaintSlice(&sub_features), 279 | ); 280 | } 281 | 282 | for optional_feature in optional_features { 283 | println!( 284 | "{} {}", 285 | Color::Yellow.bold().paint(optional_feature), 286 | Color::Yellow.dimmed().paint("(optional)") 287 | ); 288 | } 289 | 290 | return; 291 | } 292 | 293 | let manifest = std::fs::read_to_string(&manifest_path).expect("Read Cargo.toml"); 294 | 295 | let mut document = Document::from_str(&manifest).expect("Parse Cargo.toml"); 296 | 297 | try_process_dependency(&mut document, target, |item, dep_ty| { 298 | let dependencies_table = item.as_table_mut().expect("dependencies is not table"); 299 | 300 | get_item!(dependencies_table, &krate, dep); 301 | 302 | if let Some(version) = dep.as_str() { 303 | let mut table = InlineTable::default(); 304 | table.get_or_insert("version", version.to_string()); 305 | table.decor_mut().set_prefix(" "); 306 | table.decor_mut().set_suffix(""); 307 | *dep.as_value_mut().unwrap() = table.into(); 308 | } 309 | 310 | const DEFAULT_FEATURES_KEY: &str = "default-features"; 311 | 312 | let features = if let Some(table) = dep.as_inline_table_mut() { 313 | if let Some(default_features) = default_features { 314 | if default_features { 315 | table.remove(DEFAULT_FEATURES_KEY); 316 | } else { 317 | table.get_or_insert(DEFAULT_FEATURES_KEY, false); 318 | } 319 | } 320 | 321 | table 322 | .get_or_insert("features", Value::Array(Array::default())) 323 | .as_array_mut() 324 | } else if let Some(table) = dep.as_table_mut() { 325 | if let Some(default_features) = default_features { 326 | if default_features { 327 | table.remove(DEFAULT_FEATURES_KEY); 328 | } else { 329 | table.insert(DEFAULT_FEATURES_KEY, Item::Value(false.into())); 330 | } 331 | } 332 | 333 | table 334 | .entry("features") 335 | .or_insert(Item::Value(Value::Array(Array::default()))) 336 | .as_array_mut() 337 | } else { 338 | eprintln!("dependency is not table"); 339 | return; 340 | }; 341 | 342 | let features = features.expect("features array"); 343 | 344 | for feature in command_features.iter() { 345 | if dep_ty != target_dep_ty { 346 | continue; 347 | } 348 | 349 | let (dep_command, feature) = parse_feature(feature); 350 | 351 | if !package.features.contains_key(feature) 352 | && !package 353 | .dependencies 354 | .iter() 355 | .any(|x| x.optional && x.name == feature) 356 | { 357 | if !ignore_progress { 358 | eprintln!( 359 | "{} crate `{}` doesn't have feature `{}`", 360 | Color::Yellow.bold().paint( 361 | "Skipping".pad_to_width_with_alignment(12, pad::Alignment::Right) 362 | ), 363 | krate, 364 | feature 365 | ); 366 | } 367 | continue; 368 | } 369 | 370 | let finder = find_feature(feature); 371 | match dep_command { 372 | DependencyCommand::Add => { 373 | if !features.iter().any(finder) { 374 | if !ignore_progress { 375 | eprintln!( 376 | "{} feature `{}` to crate `{}`", 377 | Color::Green.bold().paint( 378 | "Adding".pad_to_width_with_alignment(12, pad::Alignment::Right) 379 | ), 380 | feature, 381 | krate 382 | ); 383 | } 384 | features.push(feature); 385 | features.fmt(); 386 | } 387 | } 388 | DependencyCommand::Remove => { 389 | let pos = features.iter().position(finder); 390 | 391 | if let Some(pos) = pos { 392 | if !ignore_progress { 393 | eprintln!( 394 | "{} feature `{}` to crate `{}`", 395 | Color::Green.bold().paint( 396 | "Removing" 397 | .pad_to_width_with_alignment(12, pad::Alignment::Right) 398 | ), 399 | feature, 400 | krate 401 | ); 402 | } 403 | features.remove(pos); 404 | features.fmt(); 405 | } 406 | } 407 | } 408 | } 409 | 410 | if features.is_empty() { 411 | if let Some(table) = dep.as_table_mut() { 412 | table.remove("features"); 413 | } else if let Some(table) = dep.as_inline_table_mut() { 414 | table.remove("features"); 415 | } 416 | } 417 | 418 | let dep_table = dep.as_table_like().unwrap(); 419 | if dep_table.len() == 1 { 420 | if let Some(version) = dep_table.get("version") { 421 | *dep = version.clone(); 422 | } 423 | } 424 | 425 | if let Some(table) = dep.as_inline_table_mut() { 426 | table.fmt(); 427 | } 428 | }); 429 | 430 | if preview { 431 | print!("{}", document); 432 | } else { 433 | let mut file = std::fs::File::create(manifest_path).expect("Create manifest"); 434 | write!(file, "{}", document).expect("Write manifest"); 435 | file.flush().expect("Flush manifest"); 436 | } 437 | } 438 | 439 | struct PaintSlice<'a>(&'a [String]); 440 | 441 | impl fmt::Display for PaintSlice<'_> { 442 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 443 | use fmt::Write; 444 | 445 | f.write_char('[')?; 446 | let mut items = self.0.iter(); 447 | let style = Color::Yellow.normal(); 448 | 449 | if let Some(first) = items.next() { 450 | write!(f, "{}\"{}\"{}", style.prefix(), first, style.suffix(),)?; 451 | } 452 | for item in items { 453 | write!(f, ", {}\"{}\"{}", style.prefix(), item, style.suffix(),)?; 454 | } 455 | f.write_char(']')?; 456 | Ok(()) 457 | } 458 | } 459 | -------------------------------------------------------------------------------- /tests/success.rs: -------------------------------------------------------------------------------- 1 | use ansi_term::Color; 2 | use assert_cmd::Command; 3 | use predicates::prelude::*; 4 | 5 | fn bin() -> Command { 6 | let mut cmd = Command::cargo_bin("cargo-feature").unwrap(); 7 | cmd.current_dir("tests/test-ws").arg("feature").arg("-p"); 8 | cmd 9 | } 10 | 11 | #[test] 12 | fn list_features() { 13 | let mut cmd = bin(); 14 | cmd.arg("test_lib"); 15 | cmd.assert().success().stdout(predicate::str::diff(format!( 16 | "{} = [{}, {}]\n{} = []\n{} = []\n", 17 | Color::Purple.bold().paint("default"), 18 | Color::Yellow.paint("\"foo\""), 19 | Color::Yellow.paint("\"bar\""), 20 | Color::Green.bold().paint("bar"), 21 | Color::Green.bold().paint("foo"), 22 | ))); 23 | } 24 | 25 | #[test] 26 | fn list_optional_deps_as_feature() { 27 | let mut cmd = bin(); 28 | cmd.arg("test_lib_dep"); 29 | cmd.assert().success().stdout(predicate::str::diff(format!( 30 | "{} {}\n{} {}\n", 31 | Color::Yellow.bold().paint("test-lib"), 32 | Color::Yellow.dimmed().paint("(optional)"), 33 | Color::Yellow.bold().paint("test-lib-rename"), 34 | Color::Yellow.dimmed().paint("(optional)"), 35 | ))); 36 | } 37 | 38 | #[test] 39 | // https://github.com/Riey/cargo-feature/issues/6 40 | fn hypen_underline() { 41 | let mut cmd = bin(); 42 | cmd.arg("test_lib"); 43 | cmd.assert().success().stdout(predicate::str::diff(format!( 44 | "{} = [{}, {}]\n{} = []\n{} = []\n", 45 | Color::Purple.bold().paint("default"), 46 | Color::Yellow.paint("\"foo\""), 47 | Color::Yellow.paint("\"bar\""), 48 | Color::Green.bold().paint("bar"), 49 | Color::Green.bold().paint("foo"), 50 | ))); 51 | } 52 | 53 | #[test] 54 | // https://github.com/Riey/cargo-feature/issues/12 55 | fn default_add() { 56 | let mut cmd = bin(); 57 | cmd.arg("test-lib").arg("foo"); 58 | cmd.assert().success().stdout(predicate::str::diff( 59 | r#"[package] 60 | name = "test-ws" 61 | version = "0.1.0" 62 | authors = ["Riey "] 63 | edition = "2018" 64 | 65 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 66 | 67 | [dependencies] 68 | test-lib = { path = "../test-lib", features = ["bar", "foo"] } 69 | 70 | [target.'cfg(target_arch = "wasm32")'.dependencies] 71 | # issue #9, #11 72 | test-lib-dep = { path = "../test-lib-dep" } 73 | "#, 74 | )); 75 | } 76 | 77 | #[test] 78 | // https://github.com/Riey/cargo-feature/issues/9 79 | fn remove_feature() { 80 | let mut cmd = bin(); 81 | cmd.arg("test-lib").arg("^bar"); 82 | cmd.assert().success().stdout(predicate::str::diff( 83 | r#"[package] 84 | name = "test-ws" 85 | version = "0.1.0" 86 | authors = ["Riey "] 87 | edition = "2018" 88 | 89 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 90 | 91 | [dependencies] 92 | test-lib = { path = "../test-lib" } 93 | 94 | [target.'cfg(target_arch = "wasm32")'.dependencies] 95 | # issue #9, #11 96 | test-lib-dep = { path = "../test-lib-dep" } 97 | "#, 98 | )); 99 | } 100 | 101 | #[test] 102 | fn disable_default_features() { 103 | let mut cmd = bin(); 104 | cmd.arg("test-lib") 105 | .arg("^default") 106 | .arg("--disable-default-features"); 107 | cmd.assert().success().stdout(predicate::str::diff( 108 | r#"[package] 109 | name = "test-ws" 110 | version = "0.1.0" 111 | authors = ["Riey "] 112 | edition = "2018" 113 | 114 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 115 | 116 | [dependencies] 117 | test-lib = { path = "../test-lib", features = ["bar"], default-features = false } 118 | 119 | [target.'cfg(target_arch = "wasm32")'.dependencies] 120 | # issue #9, #11 121 | test-lib-dep = { path = "../test-lib-dep" } 122 | "#, 123 | )); 124 | } 125 | 126 | #[test] 127 | // https://github.com/Riey/cargo-feature/issues/11 128 | fn add_target_feature() { 129 | let mut cmd = bin(); 130 | cmd.arg("test-lib-dep") 131 | .arg("+test-lib") 132 | .arg("--target=cfg(target_arch = \"wasm32\")"); 133 | cmd.assert().success().stdout(predicate::str::diff( 134 | r#"[package] 135 | name = "test-ws" 136 | version = "0.1.0" 137 | authors = ["Riey "] 138 | edition = "2018" 139 | 140 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 141 | 142 | [dependencies] 143 | test-lib = { path = "../test-lib", features = ["bar"] } 144 | 145 | [target.'cfg(target_arch = "wasm32")'.dependencies] 146 | # issue #9, #11 147 | test-lib-dep = { path = "../test-lib-dep", features = ["test-lib"] } 148 | "#, 149 | )); 150 | } 151 | 152 | #[test] 153 | fn ui_not_exist() { 154 | let mut cmd = bin(); 155 | cmd.arg("not-exists").arg("+foo"); 156 | cmd.assert().failure().stderr(predicate::eq("Can't find package from metadata! please check package `not-exists` is exists in manifest\n")); 157 | } 158 | -------------------------------------------------------------------------------- /tests/test-lib-dep/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-lib-dep" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | test-lib = { path = "../test-lib", optional = true } 10 | test-lib-rename = { path = "../test-lib", package = "test-lib", optional = true } 11 | -------------------------------------------------------------------------------- /tests/test-lib-dep/src/lib.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Riey/cargo-feature/0b8d5ba5d7ebd8ab998deabb651baa5aae20454d/tests/test-lib-dep/src/lib.rs -------------------------------------------------------------------------------- /tests/test-lib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-lib" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [features] 7 | default = ["foo", "bar"] 8 | foo = [] 9 | bar = [] 10 | 11 | [dependencies] 12 | -------------------------------------------------------------------------------- /tests/test-lib/src/lib.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Riey/cargo-feature/0b8d5ba5d7ebd8ab998deabb651baa5aae20454d/tests/test-lib/src/lib.rs -------------------------------------------------------------------------------- /tests/test-ws/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "test-lib" 7 | version = "0.1.0" 8 | 9 | [[package]] 10 | name = "test-lib-dep" 11 | version = "0.1.0" 12 | 13 | [[package]] 14 | name = "test-ws" 15 | version = "0.1.0" 16 | dependencies = [ 17 | "test-lib", 18 | "test-lib-dep", 19 | ] 20 | -------------------------------------------------------------------------------- /tests/test-ws/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-ws" 3 | version = "0.1.0" 4 | authors = ["Riey "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | test-lib = { path = "../test-lib", features = ["bar"] } 11 | 12 | [target.'cfg(target_arch = "wasm32")'.dependencies] 13 | # issue #9, #11 14 | test-lib-dep = { path = "../test-lib-dep" } 15 | -------------------------------------------------------------------------------- /tests/test-ws/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | --------------------------------------------------------------------------------