├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src └── main.rs /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: CI 4 | 5 | jobs: 6 | fmt: 7 | name: Rustfmt 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: actions-rs/toolchain@v1 12 | with: 13 | profile: minimal 14 | toolchain: stable 15 | override: true 16 | components: rustfmt 17 | - uses: actions-rs/cargo@v1 18 | with: 19 | command: fmt 20 | args: --all -- --check 21 | 22 | clippy: 23 | name: Clippy 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v3 27 | - uses: actions-rs/toolchain@v1 28 | with: 29 | profile: minimal 30 | toolchain: stable 31 | override: true 32 | components: clippy 33 | - uses: actions-rs/cargo@v1 34 | name: Clippy lint 35 | with: 36 | command: clippy 37 | args: --workspace --all-targets --all-features -- -D warnings 38 | 39 | docs: 40 | name: Build-test docs 41 | runs-on: ubuntu-latest 42 | steps: 43 | - uses: actions/checkout@v2 44 | - uses: actions-rs/toolchain@v1 45 | with: 46 | profile: minimal 47 | toolchain: stable 48 | override: true 49 | - uses: actions-rs/cargo@v1 50 | name: Document all crates 51 | env: 52 | RUSTDOCFLAGS: -Dwarnings 53 | with: 54 | command: doc 55 | args: --all --all-features --no-deps --document-private-items 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /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 = "autocfg" 7 | version = "1.1.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 10 | 11 | [[package]] 12 | name = "bitflags" 13 | version = "1.3.2" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 16 | 17 | [[package]] 18 | name = "camino" 19 | version = "1.1.2" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "c77df041dc383319cc661b428b6961a005db4d6808d5e12536931b1ca9556055" 22 | dependencies = [ 23 | "serde", 24 | ] 25 | 26 | [[package]] 27 | name = "cargo-dependency-inheritor" 28 | version = "0.1.1" 29 | dependencies = [ 30 | "cargo_metadata", 31 | "clap", 32 | "dunce", 33 | "toml_edit", 34 | ] 35 | 36 | [[package]] 37 | name = "cargo-platform" 38 | version = "0.1.2" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" 41 | dependencies = [ 42 | "serde", 43 | ] 44 | 45 | [[package]] 46 | name = "cargo_metadata" 47 | version = "0.15.2" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "982a0cf6a99c350d7246035613882e376d58cebe571785abc5da4f648d53ac0a" 50 | dependencies = [ 51 | "camino", 52 | "cargo-platform", 53 | "semver", 54 | "serde", 55 | "serde_json", 56 | "thiserror", 57 | ] 58 | 59 | [[package]] 60 | name = "cc" 61 | version = "1.0.78" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" 64 | 65 | [[package]] 66 | name = "clap" 67 | version = "4.1.4" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" 70 | dependencies = [ 71 | "bitflags", 72 | "clap_derive", 73 | "clap_lex", 74 | "is-terminal", 75 | "once_cell", 76 | "strsim", 77 | "termcolor", 78 | ] 79 | 80 | [[package]] 81 | name = "clap_derive" 82 | version = "4.1.0" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" 85 | dependencies = [ 86 | "heck", 87 | "proc-macro-error", 88 | "proc-macro2", 89 | "quote", 90 | "syn", 91 | ] 92 | 93 | [[package]] 94 | name = "clap_lex" 95 | version = "0.3.1" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" 98 | dependencies = [ 99 | "os_str_bytes", 100 | ] 101 | 102 | [[package]] 103 | name = "dunce" 104 | version = "1.0.3" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" 107 | 108 | [[package]] 109 | name = "errno" 110 | version = "0.2.8" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" 113 | dependencies = [ 114 | "errno-dragonfly", 115 | "libc", 116 | "winapi", 117 | ] 118 | 119 | [[package]] 120 | name = "errno-dragonfly" 121 | version = "0.1.2" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 124 | dependencies = [ 125 | "cc", 126 | "libc", 127 | ] 128 | 129 | [[package]] 130 | name = "hashbrown" 131 | version = "0.12.3" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 134 | 135 | [[package]] 136 | name = "heck" 137 | version = "0.4.0" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 140 | 141 | [[package]] 142 | name = "hermit-abi" 143 | version = "0.2.6" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" 146 | dependencies = [ 147 | "libc", 148 | ] 149 | 150 | [[package]] 151 | name = "indexmap" 152 | version = "1.9.2" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" 155 | dependencies = [ 156 | "autocfg", 157 | "hashbrown", 158 | ] 159 | 160 | [[package]] 161 | name = "io-lifetimes" 162 | version = "1.0.4" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" 165 | dependencies = [ 166 | "libc", 167 | "windows-sys", 168 | ] 169 | 170 | [[package]] 171 | name = "is-terminal" 172 | version = "0.4.2" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" 175 | dependencies = [ 176 | "hermit-abi", 177 | "io-lifetimes", 178 | "rustix", 179 | "windows-sys", 180 | ] 181 | 182 | [[package]] 183 | name = "itoa" 184 | version = "1.0.5" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" 187 | 188 | [[package]] 189 | name = "libc" 190 | version = "0.2.139" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" 193 | 194 | [[package]] 195 | name = "linux-raw-sys" 196 | version = "0.1.4" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" 199 | 200 | [[package]] 201 | name = "memchr" 202 | version = "2.5.0" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 205 | 206 | [[package]] 207 | name = "nom8" 208 | version = "0.2.0" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" 211 | dependencies = [ 212 | "memchr", 213 | ] 214 | 215 | [[package]] 216 | name = "once_cell" 217 | version = "1.17.0" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" 220 | 221 | [[package]] 222 | name = "os_str_bytes" 223 | version = "6.4.1" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" 226 | 227 | [[package]] 228 | name = "proc-macro-error" 229 | version = "1.0.4" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 232 | dependencies = [ 233 | "proc-macro-error-attr", 234 | "proc-macro2", 235 | "quote", 236 | "syn", 237 | "version_check", 238 | ] 239 | 240 | [[package]] 241 | name = "proc-macro-error-attr" 242 | version = "1.0.4" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 245 | dependencies = [ 246 | "proc-macro2", 247 | "quote", 248 | "version_check", 249 | ] 250 | 251 | [[package]] 252 | name = "proc-macro2" 253 | version = "1.0.50" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" 256 | dependencies = [ 257 | "unicode-ident", 258 | ] 259 | 260 | [[package]] 261 | name = "quote" 262 | version = "1.0.23" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" 265 | dependencies = [ 266 | "proc-macro2", 267 | ] 268 | 269 | [[package]] 270 | name = "rustix" 271 | version = "0.36.7" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03" 274 | dependencies = [ 275 | "bitflags", 276 | "errno", 277 | "io-lifetimes", 278 | "libc", 279 | "linux-raw-sys", 280 | "windows-sys", 281 | ] 282 | 283 | [[package]] 284 | name = "ryu" 285 | version = "1.0.12" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" 288 | 289 | [[package]] 290 | name = "semver" 291 | version = "1.0.16" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" 294 | dependencies = [ 295 | "serde", 296 | ] 297 | 298 | [[package]] 299 | name = "serde" 300 | version = "1.0.152" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" 303 | dependencies = [ 304 | "serde_derive", 305 | ] 306 | 307 | [[package]] 308 | name = "serde_derive" 309 | version = "1.0.152" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" 312 | dependencies = [ 313 | "proc-macro2", 314 | "quote", 315 | "syn", 316 | ] 317 | 318 | [[package]] 319 | name = "serde_json" 320 | version = "1.0.91" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" 323 | dependencies = [ 324 | "itoa", 325 | "ryu", 326 | "serde", 327 | ] 328 | 329 | [[package]] 330 | name = "strsim" 331 | version = "0.10.0" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 334 | 335 | [[package]] 336 | name = "syn" 337 | version = "1.0.107" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" 340 | dependencies = [ 341 | "proc-macro2", 342 | "quote", 343 | "unicode-ident", 344 | ] 345 | 346 | [[package]] 347 | name = "termcolor" 348 | version = "1.2.0" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" 351 | dependencies = [ 352 | "winapi-util", 353 | ] 354 | 355 | [[package]] 356 | name = "thiserror" 357 | version = "1.0.38" 358 | source = "registry+https://github.com/rust-lang/crates.io-index" 359 | checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" 360 | dependencies = [ 361 | "thiserror-impl", 362 | ] 363 | 364 | [[package]] 365 | name = "thiserror-impl" 366 | version = "1.0.38" 367 | source = "registry+https://github.com/rust-lang/crates.io-index" 368 | checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" 369 | dependencies = [ 370 | "proc-macro2", 371 | "quote", 372 | "syn", 373 | ] 374 | 375 | [[package]] 376 | name = "toml_datetime" 377 | version = "0.5.1" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" 380 | 381 | [[package]] 382 | name = "toml_edit" 383 | version = "0.18.0" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | checksum = "729bfd096e40da9c001f778f5cdecbd2957929a24e10e5883d9392220a751581" 386 | dependencies = [ 387 | "indexmap", 388 | "nom8", 389 | "toml_datetime", 390 | ] 391 | 392 | [[package]] 393 | name = "unicode-ident" 394 | version = "1.0.6" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" 397 | 398 | [[package]] 399 | name = "version_check" 400 | version = "0.9.4" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 403 | 404 | [[package]] 405 | name = "winapi" 406 | version = "0.3.9" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 409 | dependencies = [ 410 | "winapi-i686-pc-windows-gnu", 411 | "winapi-x86_64-pc-windows-gnu", 412 | ] 413 | 414 | [[package]] 415 | name = "winapi-i686-pc-windows-gnu" 416 | version = "0.4.0" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 419 | 420 | [[package]] 421 | name = "winapi-util" 422 | version = "0.1.5" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 425 | dependencies = [ 426 | "winapi", 427 | ] 428 | 429 | [[package]] 430 | name = "winapi-x86_64-pc-windows-gnu" 431 | version = "0.4.0" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 434 | 435 | [[package]] 436 | name = "windows-sys" 437 | version = "0.42.0" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" 440 | dependencies = [ 441 | "windows_aarch64_gnullvm", 442 | "windows_aarch64_msvc", 443 | "windows_i686_gnu", 444 | "windows_i686_msvc", 445 | "windows_x86_64_gnu", 446 | "windows_x86_64_gnullvm", 447 | "windows_x86_64_msvc", 448 | ] 449 | 450 | [[package]] 451 | name = "windows_aarch64_gnullvm" 452 | version = "0.42.1" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" 455 | 456 | [[package]] 457 | name = "windows_aarch64_msvc" 458 | version = "0.42.1" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" 461 | 462 | [[package]] 463 | name = "windows_i686_gnu" 464 | version = "0.42.1" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" 467 | 468 | [[package]] 469 | name = "windows_i686_msvc" 470 | version = "0.42.1" 471 | source = "registry+https://github.com/rust-lang/crates.io-index" 472 | checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" 473 | 474 | [[package]] 475 | name = "windows_x86_64_gnu" 476 | version = "0.42.1" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" 479 | 480 | [[package]] 481 | name = "windows_x86_64_gnullvm" 482 | version = "0.42.1" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" 485 | 486 | [[package]] 487 | name = "windows_x86_64_msvc" 488 | version = "0.42.1" 489 | source = "registry+https://github.com/rust-lang/crates.io-index" 490 | checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" 491 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cargo-dependency-inheritor" 3 | version = "0.1.1" 4 | edition = "2021" 5 | license = "MIT OR Apache-2.0" 6 | authors = ["T.Post"] 7 | description = "Utility that inherits dependencies from the main workspace if they occur 'n' or more times in the workspace." 8 | categories = ["command-line-utilities", "cargo"] 9 | 10 | [dependencies] 11 | cargo_metadata = "0.15.0" 12 | clap = { version="4.1.4", features=["derive"] } 13 | dunce = "1" 14 | toml_edit = { version = "0.18.0", default-features = true } 15 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [year] [fullname] 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 Workspace Dependency Inheritor 2 | 3 | Utility that inherits dependencies from the main workspace if they occur `n` or more times in the workspace. 4 | 5 | Workspace Inheritance was stabilized in version 1.64. 6 | See [`[workspace.package]`][1], [`[workspace.dependencies]`][2], and [inheriting-a-dependency-from-a-workspace][3] for more information. 7 | 8 | ## How to Use 9 | 10 | To inherit a dependency that occurs five or more times in the workspace, use the following command: 11 | 12 | ```bash 13 | cargo install cargo-dependency-inheritor 14 | cargo dependency-inheritor --workspace-path "path/to/workspace/Cargo.toml" -n 5 15 | ``` 16 | 17 | **This command edits your toml files, make sure to have a back up** 18 | 19 | ## Process 20 | 21 | Dependencies can be inherited from a workspace by specifying the dependency in the workspace's [`[workspace.dependencies]`][2] table. After that, add it to the `[dependencies]` table with workspace = true. 22 | This crate automates the process. 23 | 24 | 1. Read packages defined in `[workspace]` section of the workspace-file. 25 | 2. Note which dependencies occur `n` or more times. 26 | 3. Update all dependencies that occurred `n` or more times: 27 | 1. Turn `dependency = "0.1.3"` into inline tables. 28 | 2. Add `workspace = true` key-value to the dependency inline table. 29 | 3. Remove `version` from inline table if exists (this will be specified in the workspace file). 30 | 4. Add [`[workspace.dependencies]`][2] table to root workspace file with all the dependencies that occurred `n` times and their version. 31 | 32 | Result: 33 | 34 | ```toml 35 | # in a project 36 | [dependencies] 37 | tokio = { workspace = true } 38 | 39 | # in the workspace 40 | [workspace.dependencies] 41 | tokio = "1.0" 42 | ``` 43 | 44 | [1]: https://doc.rust-lang.org/nightly/cargo/reference/workspaces.html#the-package-table 45 | [2]: https://doc.rust-lang.org/nightly/cargo/reference/workspaces.html#the-dependencies-table 46 | [3]: https://doc.rust-lang.org/nightly/cargo/reference/specifying-dependencies.html#inheriting-a-dependency-from-a-workspace 47 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | //! # Cargo Workspace Dependency Inheritor 2 | //! 3 | //! Utility that inherits dependencies from the main workspace if they occur `n` or more times in the workspace. 4 | //! 5 | //! Workspace Inheritance was stabilized in version 1.64. 6 | //! See [`[workspace.package]`][1], [`[workspace.dependencies]`][2], and [inheriting-a-dependency-from-a-workspace][3] for more information. 7 | //! 8 | //! ## How to Use 9 | //! 10 | //! To inherit a dependency that occurs five or more times in the workspace, use the following command: 11 | //! 12 | //! (currently some issues using it via cargo subcommand, download this lib and compile it your self instead) 13 | //! 14 | //! ```bash 15 | //! cargo install cargo-dependency-inheritor 16 | //! cargo dependency-inheritor --workspace-path "path/to/workspace/Cargo.toml" -n 5 17 | //! ``` 18 | //! 19 | //! **This command edits your toml files, make sure to have a back up** 20 | //! 21 | //! ## Process 22 | //! 23 | //! Dependencies can be inherited from a workspace by specifying the dependency in the workspace's [`[workspace.dependencies]`][2] table. After that, add it to the `[dependencies]` table with workspace = true. 24 | //! This crate automates the process. 25 | //! 26 | //! 1. Read packages defined in `[workspace]` section of the workspace-file. 27 | //! 2. Note which dependencies occur `n` or more times. 28 | //! 3. Update all dependencies that occurred `n` or more times: 29 | //! 1. Turn `dependency = "0.1.3"` into inline tables. 30 | //! 2. Add `workspace = true` key-value to the dependency inline table. 31 | //! 3. Remove `version` from inline table if exists (this will be specified in the workspace file). 32 | //! 4. Add [`[workspace.dependencies]`][2] table to root workspace file with all the dependencies that occurred `n` times and their version. 33 | //! 34 | //! Result: 35 | //! 36 | //! ```toml 37 | //! ## in a project 38 | //! [dependencies] 39 | //! tokio = { workspace = true } 40 | //! 41 | //! ## in the workspace 42 | //! [workspace.dependencies] 43 | //! tokio = "1.0" 44 | //! ``` 45 | //! 46 | //! [1]: https://doc.rust-lang.org/nightly/cargo/reference/workspaces.html#the-package-table 47 | //! [2]: https://doc.rust-lang.org/nightly/cargo/reference/workspaces.html#the-dependencies-table 48 | //! [3]: https://doc.rust-lang.org/nightly/cargo/reference/specifying-dependencies.html#inheriting-a-dependency-from-a-workspace 49 | 50 | use std::{ 51 | collections::{BTreeMap, HashSet}, 52 | path::PathBuf, 53 | }; 54 | 55 | use clap::Parser; 56 | use toml_edit::{Document, Formatted, InlineTable, Item, Table, Value}; 57 | 58 | #[derive(Parser)] 59 | #[clap(author, version, about, long_about = None)] 60 | struct DependencyInheritor { 61 | /// Full path to the `Cargo.toml` file that defines the rust workspace. 62 | #[clap(short, long, value_parser)] 63 | workspace_path: PathBuf, 64 | 65 | /// If a dependency is used throughout the workspace more then 'n times', add the 'workspace = true' key value to it. 66 | #[clap(short, long, value_parser)] 67 | number: usize, 68 | 69 | /// Exclude workspace packages from being processed. 70 | /// Provide the package name as it is defined in by: `[package] name="x"` 71 | #[clap(long, value_parser)] 72 | exclude_packages: Vec, 73 | } 74 | 75 | #[derive(Parser)] 76 | #[clap(bin_name = "cargo")] 77 | enum Cargo { 78 | DependencyInheritor(DependencyInheritor), 79 | } 80 | 81 | fn main() { 82 | let args = Cargo::parse(); 83 | match args { 84 | Cargo::DependencyInheritor(args) => { 85 | // Gather metadata on the workspace. 86 | let mut cmd = cargo_metadata::MetadataCommand::new(); 87 | let mut workspace_path = dunce::canonicalize(&args.workspace_path).unwrap(); 88 | assert!(workspace_path.pop()); 89 | 90 | cmd.manifest_path(args.workspace_path.clone()); 91 | 92 | let metadata = cmd.exec().unwrap(); 93 | 94 | let exclude_packages: HashSet = 95 | HashSet::from_iter(args.exclude_packages.into_iter()); 96 | 97 | // Gather all dependencies that occur more then the configured number of times throughout the workspace. 98 | let mut duplicated_dependencies = BTreeMap::<&String, Entry>::new(); 99 | 100 | for package in metadata.workspace_packages() { 101 | if exclude_packages.contains(&package.name) { 102 | continue; 103 | } 104 | 105 | for package_dependency in &package.dependencies { 106 | let mut detected_dependency = duplicated_dependencies 107 | .entry(&package_dependency.name) 108 | .or_default(); 109 | 110 | detected_dependency.version = package_dependency.req.to_string(); 111 | detected_dependency.count += 1; 112 | detected_dependency 113 | .workspace_packages 114 | .push(package.manifest_path.to_string()); 115 | detected_dependency.no_default_features |= 116 | !package_dependency.uses_default_features; 117 | 118 | detected_dependency.path = package_dependency 119 | .path 120 | .as_ref() 121 | .map(|path| path.strip_prefix(&workspace_path).unwrap().into()); 122 | } 123 | } 124 | 125 | let dependency_candidates = duplicated_dependencies 126 | .iter() 127 | .filter(|(_, dep)| dep.count >= args.number) 128 | .map(|(&name, _)| name.to_owned()) 129 | .collect(); 130 | 131 | // Update the toml definition of the workspace. And add the new 'workspace = true' key value pair. 132 | for package in metadata.workspace_packages() { 133 | let package_toml = &package.manifest_path; 134 | let toml_contents = if let Ok(doc) = std::fs::read_to_string(package_toml) { 135 | doc 136 | } else { 137 | continue; 138 | }; 139 | let mut toml_document = if let Ok(doc) = toml_contents.parse::() { 140 | doc 141 | } else { 142 | continue; 143 | }; 144 | 145 | fn rewrite_dependency_table( 146 | dependency_table: &mut Table, 147 | dependency_candidates: &HashSet, 148 | ) { 149 | // Iterate all packages with deps that ocurred more then the configured number times. 150 | for (key, val) in dependency_table.iter_mut() { 151 | if !dependency_candidates.contains(key.get()) { 152 | continue; 153 | } 154 | 155 | match val { 156 | Item::None => todo!(), 157 | Item::Table(table) => { 158 | table.insert("workspace", Item::Value(Value::from(true))); 159 | table.remove("version"); 160 | table.remove("path"); 161 | } 162 | Item::ArrayOfTables(_) => todo!(), 163 | Item::Value(val) => match val { 164 | Value::InlineTable(table) => { 165 | // dependency specified as `dep = {version="x"}`. 166 | 167 | table.insert("workspace", Value::from(true)); 168 | table.remove("version"); 169 | table.remove("path"); 170 | } 171 | Value::String(_) => { 172 | // dependency specified as `dep = "x"` 173 | let mut new_table = InlineTable::new(); 174 | new_table.insert("workspace", Value::from(true)); 175 | 176 | // preserve any line decoration such as comments. 177 | let decor = val.decor().clone(); 178 | *val = Value::InlineTable(new_table); 179 | *val.decor_mut() = decor; 180 | } 181 | Value::Integer(_) 182 | | Value::Float(_) 183 | | Value::Boolean(_) 184 | | Value::Datetime(_) 185 | | Value::Array(_) => { 186 | // dependency not specified in those forms. 187 | } 188 | }, 189 | } 190 | } 191 | } 192 | 193 | for dependency_type in ["dependencies", "dev-dependencies", "build-dependencies"] { 194 | // Fetch the dependency table from the workspace package toml document. 195 | if let Some(Item::Table(dependency_table)) = 196 | toml_document.get_mut(dependency_type) 197 | { 198 | rewrite_dependency_table(dependency_table, &dependency_candidates) 199 | } 200 | if let Some(Item::Table(target)) = toml_document.get_mut("target") { 201 | for (_name, cfg) in target.iter_mut() { 202 | if let Some(Item::Table(dependency_table)) = 203 | cfg.get_mut(dependency_type) 204 | { 205 | rewrite_dependency_table(dependency_table, &dependency_candidates) 206 | } 207 | } 208 | } 209 | } 210 | 211 | if let Err(e) = std::fs::write(package_toml, toml_document.to_string()) { 212 | eprintln!("Failed to write to {package_toml:?}: {e:?}"); 213 | } 214 | } 215 | 216 | // Print the results. 217 | for (d, entry) in &duplicated_dependencies { 218 | if entry.count >= args.number { 219 | println!("==== Dependency: '{d}' ({}) =====", entry.count); 220 | 221 | for workspace_package in &entry.workspace_packages { 222 | println!(" - {workspace_package}"); 223 | } 224 | } 225 | } 226 | 227 | if let Ok(toml_contents) = std::fs::read_to_string(args.workspace_path.clone()) { 228 | if let Ok(mut doc) = toml_contents.parse::() { 229 | edit_workspace_dependency_table( 230 | &mut doc, 231 | &duplicated_dependencies, 232 | args.number, 233 | ); 234 | 235 | if let Err(e) = std::fs::write(&args.workspace_path, doc.to_string()) { 236 | eprintln!("Failed to write to {:?}: {:?}", args.workspace_path, e); 237 | } 238 | } else { 239 | println!("failed to parse workspace definition"); 240 | }; 241 | } else { 242 | println!("failed to update workspace definition"); 243 | }; 244 | } 245 | } 246 | } 247 | 248 | fn edit_workspace_dependency_table( 249 | document: &mut Document, 250 | workspace_deps: &BTreeMap<&String, Entry>, 251 | occurrences: usize, 252 | ) { 253 | // Crate table if not exist, otherwise edit. 254 | if let Some(Item::Table(table)) = document.get_mut("workspace.dependencies") { 255 | for (key, val) in workspace_deps { 256 | if val.count >= occurrences && !table.contains_key(key.as_str()) { 257 | table.insert(key, val.to_toml()); 258 | } 259 | } 260 | } else { 261 | let mut new_table = Table::new(); 262 | 263 | for (key, val) in workspace_deps { 264 | if val.count >= occurrences { 265 | new_table.insert(key, val.to_toml()); 266 | } 267 | } 268 | 269 | document["workspace"]["dependencies"] = Item::Table(new_table); 270 | } 271 | } 272 | 273 | #[derive(Default)] 274 | struct Entry { 275 | pub count: usize, 276 | pub workspace_packages: Vec, 277 | pub version: String, 278 | pub path: Option, 279 | /// Whether _any_ package uses this crate with the default features _enabled_ 280 | pub no_default_features: bool, 281 | } 282 | 283 | impl Entry { 284 | fn to_toml(&self) -> Item { 285 | let version = Value::String(Formatted::new(self.version.clone())); 286 | Item::Value(if self.no_default_features || self.path.is_some() { 287 | let mut itable = InlineTable::new(); 288 | if self.version != "*" { 289 | itable.insert("version", version); 290 | } 291 | if let Some(path) = &self.path { 292 | itable.insert("path", Value::from(path.to_str().unwrap())); 293 | } 294 | if self.no_default_features { 295 | itable.insert("default-features", Value::from(false)); 296 | } 297 | Value::InlineTable(itable) 298 | } else { 299 | version 300 | }) 301 | } 302 | } 303 | --------------------------------------------------------------------------------